From 19b7b8874dc47e1f1bd610438386305b6cbd2db6 Mon Sep 17 00:00:00 2001 From: unknown <tuethan@msu.edu> Date: Thu, 13 Feb 2020 17:17:43 -0500 Subject: [PATCH] Adding documentation to Docs folder of all python notebooks as well as the updated ipynb files for my models --- doc/pk1Comp.html | 433 +++++++++++++++++++++++++ doc/pk2Comp.html | 512 ++++++++++++++++++++++++++++++ doc/pkGUI.html | 450 ++++++++++++++++++++++++++ doc/pkOptimizer.html | 561 +++++++++++++++++++++++++++++++++ pk_optimizer/pk1Comp.ipynb | 2 +- pk_optimizer/pk2Comp.ipynb | 119 +++++-- pk_optimizer/pkOptimizer.ipynb | 175 ++++++++++ 7 files changed, 2219 insertions(+), 33 deletions(-) create mode 100644 doc/pk1Comp.html create mode 100644 doc/pk2Comp.html create mode 100644 doc/pkGUI.html create mode 100644 doc/pkOptimizer.html create mode 100644 pk_optimizer/pkOptimizer.ipynb diff --git a/doc/pk1Comp.html b/doc/pk1Comp.html new file mode 100644 index 0000000..47e909c --- /dev/null +++ b/doc/pk1Comp.html @@ -0,0 +1,433 @@ +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" /> +<meta name="generator" content="pdoc 0.7.4" /> +<title>pk1Comp API documentation</title> +<meta name="description" content="" /> +<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'> +<link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'> +<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet"> +<style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style> +<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style> +<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style> +</head> +<body> +<main> +<article id="content"> +<header> +<h1 class="title">Module <code>pk1Comp</code></h1> +</header> +<section id="section-intro"> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">#!/usr/bin/env python +# coding: utf-8 + +# In[1]: + + +# Import commands +from scipy.stats import gamma +import numpy as np +import matplotlib.pyplot as plt +#%matplotlib inline +from scipy.integrate import odeint +import math as math + +class pk1Comp: + + """The pk1Comp object is a one compartment PK model that outputs graphs of mass of tracer over time.""" + + def __init__ (self, numParam = 4, Flow = 1, Vp = 0.1, Visf = 0.5, PS = 0.15): + + """Initializes the model with default parameter values for flow, Vp, Visf, and PS. + Parameters + ---------- + numParam: int + numParam is the number of parameters you want to optimize for the model. Defaults to 4. + + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1. + + Vp : double + Vp is the volume of plasma in mL. Defaults to 0.1. + + Visf : double + Visf is the volume of interstitial fluid in mL. Defaults to 0.5. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). Defaults to 0.15. + """ + + # Declare Variables for initial conditions + self.numParam = numParam + self.Flow = Flow + self.Vp = Vp + self.Visf = Visf + self.PS = PS + C0 = 0 # Initial concentration of tracer in plasma + tmax = 10 #Time in seconds + dt = 0.1 #Time step + a = 2. # Alpha for gamma distribution + rv = gamma(a, loc = 2, scale = 0.65) #input function + + # Define the time array + time = np.arange(0, tmax + dt, dt) + + # Derivative function + def derivs(curr_vals, time): + """Finds derivatives of ODEs. + + Parameters + ---------- + curr_vals : double[] + curr_vals it he current values of the variables we wish to "update" from the curr_vals list. + + time : double[] + time is our time array from 0 to tmax with timestep dt. + + Returns + ------- + dc_dt : double[] + contains the derivative of concentrations with respect to time. + """ + + # Define value of input function Cin + Cin = rv.pdf(time) + + # Unpack the current values of the variables we wish to "update" from the curr_vals list + C = curr_vals + + # Right-hand side of odes, which are used to computer the derivative + dC_dt = flow*(Cin - C)/Vol + #Cout = C + return dC_dt + + def getPlot(self): + """Plots the solution of the solved ODEs. + + Parameters + ---------- + self : self + Passes variables needed from self. + """ + + # Plot the results using the values stored in the solution variable, "sol" + # Plot Cp using the "0" element from the solution + plt.figure(1) + plt.plot(time, rv.pdf(time), color = 'blue', label = 'Input Function') + plt.plot(time, sol[:,0],color="green", label = 'Cout') + + # Plot Cisf using the "1" element from the solution + #plt.plot(time, sol[:,1],color="purple", label = 'Cisf') + plt.xlabel('Time [s]') + plt.ylabel('Concentration [mM]') + plt.legend(loc = 'best') + plt.grid() + + def main(self): + """Main function to run and solve ODEs""" + + # Store the initial values in a list + init = [C0] + + # Solve the odes with odeint + sol = odeint(derivs, init, time) + + #Mass_plasma = Vp * sol[:,0] #mass of tracer in plasma + #Mass_isf = Visf * sol[:,1] #mass of tracer in isf + #Tp = Vp/(flow + PS) # mean transit time + #E = 1 - np.exp(-PS/flow) #extraction fraction + #Q = Mass_plasma + Mass_isf + + #print('The mean transit time is ' + str(Tp)) + #print('The extraction fraction is ' + str(E)) + + # Plot mass of tracer using the "2" element from the solution + #plt.figure(2) + #plt.plot(time, Mass_plasma,color="red", label = 'Plasma') + # Plot mass of tracer in tissue using the "3" element from the solution + #plt.plot(time, Mass_isf,color="black", label = 'Interstitial Space') + #plt.plot(time, Q, color="blue", label = 'Total mass') + #plt.xlabel('Time [s]') + #plt.ylabel('Mass [mg]') + #plt.legend(loc = 'best') + #plt.grid() + + +# In[ ]:</code></pre> +</details> +</section> +<section> +</section> +<section> +</section> +<section> +</section> +<section> +<h2 class="section-title" id="header-classes">Classes</h2> +<dl> +<dt id="pk1Comp.pk1Comp"><code class="flex name class"> +<span>class <span class="ident">pk1Comp</span></span> +<span>(</span><span>numParam=4, Flow=1, Vp=0.1, Visf=0.5, PS=0.15)</span> +</code></dt> +<dd> +<section class="desc"><p>The pk1Comp object is a one compartment PK model that outputs graphs of mass of tracer over time.</p> +<p>Initializes the model with default parameter values for flow, Vp, Visf, and PS. +Parameters</p> +<hr> +<p>numParam: int +numParam is the number of parameters you want to optimize for the model. Defaults to 4.</p> +<p>Flow : double +Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1.</p> +<p>Vp : double +Vp is the volume of plasma in mL. Defaults to 0.1.</p> +<p>Visf : double +Visf is the volume of interstitial fluid in mL. Defaults to 0.5.</p> +<p>PS : double +PS is the permeability-surface area constant in mL/(g*min). Defaults to 0.15.</p></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">class pk1Comp: + + """The pk1Comp object is a one compartment PK model that outputs graphs of mass of tracer over time.""" + + def __init__ (self, numParam = 4, Flow = 1, Vp = 0.1, Visf = 0.5, PS = 0.15): + + """Initializes the model with default parameter values for flow, Vp, Visf, and PS. + Parameters + ---------- + numParam: int + numParam is the number of parameters you want to optimize for the model. Defaults to 4. + + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1. + + Vp : double + Vp is the volume of plasma in mL. Defaults to 0.1. + + Visf : double + Visf is the volume of interstitial fluid in mL. Defaults to 0.5. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). Defaults to 0.15. + """ + + # Declare Variables for initial conditions + self.numParam = numParam + self.Flow = Flow + self.Vp = Vp + self.Visf = Visf + self.PS = PS + C0 = 0 # Initial concentration of tracer in plasma + tmax = 10 #Time in seconds + dt = 0.1 #Time step + a = 2. # Alpha for gamma distribution + rv = gamma(a, loc = 2, scale = 0.65) #input function + + # Define the time array + time = np.arange(0, tmax + dt, dt) + + # Derivative function + def derivs(curr_vals, time): + """Finds derivatives of ODEs. + + Parameters + ---------- + curr_vals : double[] + curr_vals it he current values of the variables we wish to "update" from the curr_vals list. + + time : double[] + time is our time array from 0 to tmax with timestep dt. + + Returns + ------- + dc_dt : double[] + contains the derivative of concentrations with respect to time. + """ + + # Define value of input function Cin + Cin = rv.pdf(time) + + # Unpack the current values of the variables we wish to "update" from the curr_vals list + C = curr_vals + + # Right-hand side of odes, which are used to computer the derivative + dC_dt = flow*(Cin - C)/Vol + #Cout = C + return dC_dt + + def getPlot(self): + """Plots the solution of the solved ODEs. + + Parameters + ---------- + self : self + Passes variables needed from self. + """ + + # Plot the results using the values stored in the solution variable, "sol" + # Plot Cp using the "0" element from the solution + plt.figure(1) + plt.plot(time, rv.pdf(time), color = 'blue', label = 'Input Function') + plt.plot(time, sol[:,0],color="green", label = 'Cout') + + # Plot Cisf using the "1" element from the solution + #plt.plot(time, sol[:,1],color="purple", label = 'Cisf') + plt.xlabel('Time [s]') + plt.ylabel('Concentration [mM]') + plt.legend(loc = 'best') + plt.grid() + + def main(self): + """Main function to run and solve ODEs""" + + # Store the initial values in a list + init = [C0] + + # Solve the odes with odeint + sol = odeint(derivs, init, time)</code></pre> +</details> +<h3>Methods</h3> +<dl> +<dt id="pk1Comp.pk1Comp.derivs"><code class="name flex"> +<span>def <span class="ident">derivs</span></span>(<span>curr_vals, time)</span> +</code></dt> +<dd> +<section class="desc"><p>Finds derivatives of ODEs.</p> +<h2 id="parameters">Parameters</h2> +<p>curr_vals : double[] +curr_vals it he current values of the variables we wish to "update" from the curr_vals list.</p> +<p>time : double[] +time is our time array from 0 to tmax with timestep dt.</p> +<h2 id="returns">Returns</h2> +<dl> +<dt><strong><code>dc_dt</code></strong> : <code>double</code>[]</dt> +<dd>contains the derivative of concentrations with respect to time.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def derivs(curr_vals, time): + """Finds derivatives of ODEs. + + Parameters + ---------- + curr_vals : double[] + curr_vals it he current values of the variables we wish to "update" from the curr_vals list. + + time : double[] + time is our time array from 0 to tmax with timestep dt. + + Returns + ------- + dc_dt : double[] + contains the derivative of concentrations with respect to time. + """ + + # Define value of input function Cin + Cin = rv.pdf(time) + + # Unpack the current values of the variables we wish to "update" from the curr_vals list + C = curr_vals + + # Right-hand side of odes, which are used to computer the derivative + dC_dt = flow*(Cin - C)/Vol + #Cout = C + return dC_dt</code></pre> +</details> +</dd> +<dt id="pk1Comp.pk1Comp.getPlot"><code class="name flex"> +<span>def <span class="ident">getPlot</span></span>(<span>self)</span> +</code></dt> +<dd> +<section class="desc"><p>Plots the solution of the solved ODEs.</p> +<h2 id="parameters">Parameters</h2> +<p>self : self +Passes variables needed from self.</p></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def getPlot(self): + """Plots the solution of the solved ODEs. + + Parameters + ---------- + self : self + Passes variables needed from self. + """ + + # Plot the results using the values stored in the solution variable, "sol" + # Plot Cp using the "0" element from the solution + plt.figure(1) + plt.plot(time, rv.pdf(time), color = 'blue', label = 'Input Function') + plt.plot(time, sol[:,0],color="green", label = 'Cout') + + # Plot Cisf using the "1" element from the solution + #plt.plot(time, sol[:,1],color="purple", label = 'Cisf') + plt.xlabel('Time [s]') + plt.ylabel('Concentration [mM]') + plt.legend(loc = 'best') + plt.grid()</code></pre> +</details> +</dd> +<dt id="pk1Comp.pk1Comp.main"><code class="name flex"> +<span>def <span class="ident">main</span></span>(<span>self)</span> +</code></dt> +<dd> +<section class="desc"><p>Main function to run and solve ODEs</p></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def main(self): + """Main function to run and solve ODEs""" + + # Store the initial values in a list + init = [C0] + + # Solve the odes with odeint + sol = odeint(derivs, init, time)</code></pre> +</details> +</dd> +</dl> +</dd> +</dl> +</section> +</article> +<nav id="sidebar"> +<h1>Index</h1> +<div class="toc"> +<ul></ul> +</div> +<ul id="index"> +<li><h3><a href="#header-classes">Classes</a></h3> +<ul> +<li> +<h4><code><a title="pk1Comp.pk1Comp" href="#pk1Comp.pk1Comp">pk1Comp</a></code></h4> +<ul class=""> +<li><code><a title="pk1Comp.pk1Comp.derivs" href="#pk1Comp.pk1Comp.derivs">derivs</a></code></li> +<li><code><a title="pk1Comp.pk1Comp.getPlot" href="#pk1Comp.pk1Comp.getPlot">getPlot</a></code></li> +<li><code><a title="pk1Comp.pk1Comp.main" href="#pk1Comp.pk1Comp.main">main</a></code></li> +</ul> +</li> +</ul> +</li> +</ul> +</nav> +</main> +<footer id="footer"> +<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.7.4</a>.</p> +</footer> +<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script> +<script>hljs.initHighlightingOnLoad()</script> +</body> +</html> \ No newline at end of file diff --git a/doc/pk2Comp.html b/doc/pk2Comp.html new file mode 100644 index 0000000..55476a0 --- /dev/null +++ b/doc/pk2Comp.html @@ -0,0 +1,512 @@ +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" /> +<meta name="generator" content="pdoc 0.7.4" /> +<title>pk2Comp API documentation</title> +<meta name="description" content="" /> +<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'> +<link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'> +<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet"> +<style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style> +<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style> +<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style> +</head> +<body> +<main> +<article id="content"> +<header> +<h1 class="title">Module <code>pk2Comp</code></h1> +</header> +<section id="section-intro"> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">#!/usr/bin/env python +# coding: utf-8 + +# In[51]: + + +# Import commands +from scipy.stats import gamma +import numpy as np +import matplotlib.pyplot as plt +#%matplotlib inline +from scipy.integrate import odeint +import math as math +import os +import pandas as pd + +class pk2Comp: + """The pk2Comp object is a two compartment PK model that outputs graphs of concentration of tracer over time.""" + + def __init__ (self, numParam = 4, Flow = 1/60, Vp = 0.05, Visf = 0.15, PS = 1/60): + + """Initializes the model with default parameter values for flow, Vp, Visf, and PS. + Parameters + ---------- + numParam: int + numParam is the number of parameters you want to optimize for the model. Defaults to 4. + + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1/60. + + Vp : double + Vp is the volume of plasma in mL. Defaults to 0.05. + + Visf : double + Visf is the volume of interstitial fluid in mL. Defaults to 0.15. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). Defaults to 1/60. + """ + + # Declare Variables for initial conditions + self.numParam = numParam + self.flow = Flow + self.Vp = Vp + self.Visf = Visf + self.PS = PS + self.sol = [] + self.Cp0 = 0 # Initial concentration of tracer in plasma + self.Cisf0 = 0 # Initial concentration of tracer in interstitial space + self.tmax = 10 #Time in seconds + self.dt = 0.1 #Time step + self.a = 2. # Alpha for gamma distribution + self.rv = gamma(self.a, loc = 2, scale = 0.55) #input function + self.sol = [] + self.Mass_plasma = [] #mass of tracer in plasma + self.Mass_isf = [] + self.Q = [] + + # Define the time array + self.time = np.arange(0, self.tmax + self.dt, self.dt) + + + # Derivative function + def derivs(self, curr_vals, time): + """Finds derivatives of ODEs. + + Parameters + ---------- + curr_vals : double[] + curr_vals it he current values of the variables we wish to "update" from the curr_vals list. + + time : double[] + time is our time array from 0 to tmax with timestep dt. + + Returns + ------- + dCp_dt : double[] + contains the derivative of concentration in plasma with respect to time. + dCisf_dt : double[] + contains the derivative of concentration in interstitial fluid with respect to time. + """ + + # Unpack the current values of the variables we wish to "update" from the curr_vals list + Cp, Cisf = curr_vals + + # Define value of input function Cin + Cin = self.rv.pdf(time) + + # Right-hand side of odes, which are used to computer the derivative + dCp_dt = (self.flow/self.Vp)*(Cin - Cp) + (self.PS/self.Vp)*(Cisf - Cp) + dCisf_dt = (self.PS/self.Visf)*(Cp - Cisf) + + return dCp_dt, dCisf_dt + + def main(self): + """Main function to solve ODEs""" + # Store the initial values in a list + init = [self.Cp0, self.Cisf0] + + # Solve the odes with odeint + self.sol = odeint(self.derivs, init, self.time) + + self.Mass_plasma = self.Vp * self.sol[:,0] #mass of tracer in plasma + self.Mass_isf = self.Visf * self.sol[:,1] #mass of tracer in isf + #Tp = Vp/(flow + PS) # mean transit time + #E = 1 - np.exp(-PS/flow) #extraction fraction + self.Q = self.Mass_plasma + self.Mass_isf + + #print('The mean transit time is ' + str(Tp)) + #print('The extraction fraction is ' + str(E)) + + def getPlot(self): + """Plots the solution of the solved ODEs. + + Attributes + ---------- + sol : double[] + contains the solutions of our ODE functions. + """ + + # Plot the results using the values stored in the solution variable, "sol" + # Plot Cp using the "0" element from the solution + plt.figure(1) + plt.plot(self.time, self.rv.pdf(self.time), color = 'blue', label = 'Input Function') + plt.plot(self.time, self.sol[:,0],color="green", label = 'Cp') + + # Plot Cisf using the "1" element from the solution + plt.plot(self.time, self.sol[:,1],color="purple", label = 'Cisf') + plt.xlabel('Time [s]') + plt.ylabel('Concentration [mM]') + plt.legend(loc = 'best') + plt.grid() + + # Plot mass of tracer using the "2" element from the solution + plt.figure(2) + plt.plot(self.time, self.Mass_plasma,color="red", label = 'Plasma') + + # Plot mass of tracer in tissue using the "3" element from the solution + plt.plot(self.time, self.Mass_isf,color="black", label = 'Interstitial Space') + plt.plot(self.time, self.Q, color="blue", label = 'Total mass') + plt.xlabel('Time [s]') + plt.ylabel('Mass [mg]') + plt.legend(loc = 'best') + plt.grid() + + print('Cp at 10 sec is ' + str(self.sol[100,0])) + print('Cisf at 10 sec is ' + str(self.sol[100,1])) + + +# In[52]: + + +test = pk2Comp(4,.2,.2,.7,1/60) +test.main() +test.getPlot() + + +# In[ ]:</code></pre> +</details> +</section> +<section> +</section> +<section> +</section> +<section> +</section> +<section> +<h2 class="section-title" id="header-classes">Classes</h2> +<dl> +<dt id="pk2Comp.pk2Comp"><code class="flex name class"> +<span>class <span class="ident">pk2Comp</span></span> +<span>(</span><span>numParam=4, Flow=0.016666666666666666, Vp=0.05, Visf=0.15, PS=0.016666666666666666)</span> +</code></dt> +<dd> +<section class="desc"><p>The pk2Comp object is a two compartment PK model that outputs graphs of concentration of tracer over time.</p> +<p>Initializes the model with default parameter values for flow, Vp, Visf, and PS. +Parameters</p> +<hr> +<p>numParam: int +numParam is the number of parameters you want to optimize for the model. Defaults to 4.</p> +<p>Flow : double +Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1/60.</p> +<p>Vp : double +Vp is the volume of plasma in mL. Defaults to 0.05.</p> +<p>Visf : double +Visf is the volume of interstitial fluid in mL. Defaults to 0.15.</p> +<p>PS : double +PS is the permeability-surface area constant in mL/(g*min). Defaults to 1/60.</p></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">class pk2Comp: + """The pk2Comp object is a two compartment PK model that outputs graphs of concentration of tracer over time.""" + + def __init__ (self, numParam = 4, Flow = 1/60, Vp = 0.05, Visf = 0.15, PS = 1/60): + + """Initializes the model with default parameter values for flow, Vp, Visf, and PS. + Parameters + ---------- + numParam: int + numParam is the number of parameters you want to optimize for the model. Defaults to 4. + + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1/60. + + Vp : double + Vp is the volume of plasma in mL. Defaults to 0.05. + + Visf : double + Visf is the volume of interstitial fluid in mL. Defaults to 0.15. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). Defaults to 1/60. + """ + + # Declare Variables for initial conditions + self.numParam = numParam + self.flow = Flow + self.Vp = Vp + self.Visf = Visf + self.PS = PS + self.sol = [] + self.Cp0 = 0 # Initial concentration of tracer in plasma + self.Cisf0 = 0 # Initial concentration of tracer in interstitial space + self.tmax = 10 #Time in seconds + self.dt = 0.1 #Time step + self.a = 2. # Alpha for gamma distribution + self.rv = gamma(self.a, loc = 2, scale = 0.55) #input function + self.sol = [] + self.Mass_plasma = [] #mass of tracer in plasma + self.Mass_isf = [] + self.Q = [] + + # Define the time array + self.time = np.arange(0, self.tmax + self.dt, self.dt) + + + # Derivative function + def derivs(self, curr_vals, time): + """Finds derivatives of ODEs. + + Parameters + ---------- + curr_vals : double[] + curr_vals it he current values of the variables we wish to "update" from the curr_vals list. + + time : double[] + time is our time array from 0 to tmax with timestep dt. + + Returns + ------- + dCp_dt : double[] + contains the derivative of concentration in plasma with respect to time. + dCisf_dt : double[] + contains the derivative of concentration in interstitial fluid with respect to time. + """ + + # Unpack the current values of the variables we wish to "update" from the curr_vals list + Cp, Cisf = curr_vals + + # Define value of input function Cin + Cin = self.rv.pdf(time) + + # Right-hand side of odes, which are used to computer the derivative + dCp_dt = (self.flow/self.Vp)*(Cin - Cp) + (self.PS/self.Vp)*(Cisf - Cp) + dCisf_dt = (self.PS/self.Visf)*(Cp - Cisf) + + return dCp_dt, dCisf_dt + + def main(self): + """Main function to solve ODEs""" + # Store the initial values in a list + init = [self.Cp0, self.Cisf0] + + # Solve the odes with odeint + self.sol = odeint(self.derivs, init, self.time) + + self.Mass_plasma = self.Vp * self.sol[:,0] #mass of tracer in plasma + self.Mass_isf = self.Visf * self.sol[:,1] #mass of tracer in isf + #Tp = Vp/(flow + PS) # mean transit time + #E = 1 - np.exp(-PS/flow) #extraction fraction + self.Q = self.Mass_plasma + self.Mass_isf + + #print('The mean transit time is ' + str(Tp)) + #print('The extraction fraction is ' + str(E)) + + def getPlot(self): + """Plots the solution of the solved ODEs. + + Attributes + ---------- + sol : double[] + contains the solutions of our ODE functions. + """ + + # Plot the results using the values stored in the solution variable, "sol" + # Plot Cp using the "0" element from the solution + plt.figure(1) + plt.plot(self.time, self.rv.pdf(self.time), color = 'blue', label = 'Input Function') + plt.plot(self.time, self.sol[:,0],color="green", label = 'Cp') + + # Plot Cisf using the "1" element from the solution + plt.plot(self.time, self.sol[:,1],color="purple", label = 'Cisf') + plt.xlabel('Time [s]') + plt.ylabel('Concentration [mM]') + plt.legend(loc = 'best') + plt.grid() + + # Plot mass of tracer using the "2" element from the solution + plt.figure(2) + plt.plot(self.time, self.Mass_plasma,color="red", label = 'Plasma') + + # Plot mass of tracer in tissue using the "3" element from the solution + plt.plot(self.time, self.Mass_isf,color="black", label = 'Interstitial Space') + plt.plot(self.time, self.Q, color="blue", label = 'Total mass') + plt.xlabel('Time [s]') + plt.ylabel('Mass [mg]') + plt.legend(loc = 'best') + plt.grid() + + print('Cp at 10 sec is ' + str(self.sol[100,0])) + print('Cisf at 10 sec is ' + str(self.sol[100,1]))</code></pre> +</details> +<h3>Methods</h3> +<dl> +<dt id="pk2Comp.pk2Comp.derivs"><code class="name flex"> +<span>def <span class="ident">derivs</span></span>(<span>self, curr_vals, time)</span> +</code></dt> +<dd> +<section class="desc"><p>Finds derivatives of ODEs.</p> +<h2 id="parameters">Parameters</h2> +<p>curr_vals : double[] +curr_vals it he current values of the variables we wish to "update" from the curr_vals list.</p> +<p>time : double[] +time is our time array from 0 to tmax with timestep dt.</p> +<h2 id="returns">Returns</h2> +<dl> +<dt><strong><code>dCp_dt</code></strong> : <code>double</code>[]</dt> +<dd>contains the derivative of concentration in plasma with respect to time.</dd> +<dt><strong><code>dCisf_dt</code></strong> : <code>double</code>[]</dt> +<dd>contains the derivative of concentration in interstitial fluid with respect to time.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def derivs(self, curr_vals, time): + """Finds derivatives of ODEs. + + Parameters + ---------- + curr_vals : double[] + curr_vals it he current values of the variables we wish to "update" from the curr_vals list. + + time : double[] + time is our time array from 0 to tmax with timestep dt. + + Returns + ------- + dCp_dt : double[] + contains the derivative of concentration in plasma with respect to time. + dCisf_dt : double[] + contains the derivative of concentration in interstitial fluid with respect to time. + """ + + # Unpack the current values of the variables we wish to "update" from the curr_vals list + Cp, Cisf = curr_vals + + # Define value of input function Cin + Cin = self.rv.pdf(time) + + # Right-hand side of odes, which are used to computer the derivative + dCp_dt = (self.flow/self.Vp)*(Cin - Cp) + (self.PS/self.Vp)*(Cisf - Cp) + dCisf_dt = (self.PS/self.Visf)*(Cp - Cisf) + + return dCp_dt, dCisf_dt</code></pre> +</details> +</dd> +<dt id="pk2Comp.pk2Comp.getPlot"><code class="name flex"> +<span>def <span class="ident">getPlot</span></span>(<span>self)</span> +</code></dt> +<dd> +<section class="desc"><p>Plots the solution of the solved ODEs.</p> +<h2 id="attributes">Attributes</h2> +<p>sol : double[] +contains the solutions of our ODE functions.</p></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def getPlot(self): + """Plots the solution of the solved ODEs. + + Attributes + ---------- + sol : double[] + contains the solutions of our ODE functions. + """ + + # Plot the results using the values stored in the solution variable, "sol" + # Plot Cp using the "0" element from the solution + plt.figure(1) + plt.plot(self.time, self.rv.pdf(self.time), color = 'blue', label = 'Input Function') + plt.plot(self.time, self.sol[:,0],color="green", label = 'Cp') + + # Plot Cisf using the "1" element from the solution + plt.plot(self.time, self.sol[:,1],color="purple", label = 'Cisf') + plt.xlabel('Time [s]') + plt.ylabel('Concentration [mM]') + plt.legend(loc = 'best') + plt.grid() + + # Plot mass of tracer using the "2" element from the solution + plt.figure(2) + plt.plot(self.time, self.Mass_plasma,color="red", label = 'Plasma') + + # Plot mass of tracer in tissue using the "3" element from the solution + plt.plot(self.time, self.Mass_isf,color="black", label = 'Interstitial Space') + plt.plot(self.time, self.Q, color="blue", label = 'Total mass') + plt.xlabel('Time [s]') + plt.ylabel('Mass [mg]') + plt.legend(loc = 'best') + plt.grid() + + print('Cp at 10 sec is ' + str(self.sol[100,0])) + print('Cisf at 10 sec is ' + str(self.sol[100,1]))</code></pre> +</details> +</dd> +<dt id="pk2Comp.pk2Comp.main"><code class="name flex"> +<span>def <span class="ident">main</span></span>(<span>self)</span> +</code></dt> +<dd> +<section class="desc"><p>Main function to solve ODEs</p></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def main(self): + """Main function to solve ODEs""" + # Store the initial values in a list + init = [self.Cp0, self.Cisf0] + + # Solve the odes with odeint + self.sol = odeint(self.derivs, init, self.time) + + self.Mass_plasma = self.Vp * self.sol[:,0] #mass of tracer in plasma + self.Mass_isf = self.Visf * self.sol[:,1] #mass of tracer in isf + #Tp = Vp/(flow + PS) # mean transit time + #E = 1 - np.exp(-PS/flow) #extraction fraction + self.Q = self.Mass_plasma + self.Mass_isf</code></pre> +</details> +</dd> +</dl> +</dd> +</dl> +</section> +</article> +<nav id="sidebar"> +<h1>Index</h1> +<div class="toc"> +<ul></ul> +</div> +<ul id="index"> +<li><h3><a href="#header-classes">Classes</a></h3> +<ul> +<li> +<h4><code><a title="pk2Comp.pk2Comp" href="#pk2Comp.pk2Comp">pk2Comp</a></code></h4> +<ul class=""> +<li><code><a title="pk2Comp.pk2Comp.derivs" href="#pk2Comp.pk2Comp.derivs">derivs</a></code></li> +<li><code><a title="pk2Comp.pk2Comp.getPlot" href="#pk2Comp.pk2Comp.getPlot">getPlot</a></code></li> +<li><code><a title="pk2Comp.pk2Comp.main" href="#pk2Comp.pk2Comp.main">main</a></code></li> +</ul> +</li> +</ul> +</li> +</ul> +</nav> +</main> +<footer id="footer"> +<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.7.4</a>.</p> +</footer> +<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script> +<script>hljs.initHighlightingOnLoad()</script> +</body> +</html> \ No newline at end of file diff --git a/doc/pkGUI.html b/doc/pkGUI.html new file mode 100644 index 0000000..d90b662 --- /dev/null +++ b/doc/pkGUI.html @@ -0,0 +1,450 @@ +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" /> +<meta name="generator" content="pdoc 0.7.4" /> +<title>pkGUI API documentation</title> +<meta name="description" content="" /> +<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'> +<link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'> +<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet"> +<style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style> +<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style> +<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style> +</head> +<body> +<main> +<article id="content"> +<header> +<h1 class="title">Module <code>pkGUI</code></h1> +</header> +<section id="section-intro"> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">#!/usr/bin/env python +# coding: utf-8 + +# In[9]: + + +#Import Everything + +class pkGUI: + """The pkGUI object creates a GUI of size x by y to display the optimization of parameters for PK models.""" + + def __init__ (self, xdim = 512, ydim = 320, numParam = 4, Flow = 1, Vp = 0.1, Visf = 0.5, PS = 0.15): + + """Initializes the GUI with default size 512 by 320 pixels and one button to start optimization. + Parameters + ---------- + xdim : int + xdim is used for defining the x dimension of your GUI size. + + ydim : int + ydim is used for defining the y dimension of your GUI size. + + numParam: int + numParam is the number of parameters you want to optimize for the model. Defaults to 4. + + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1. + + Vp : double + Vp is the volume of plasma in mL. Defaults to 0.1. + + Visf : double + Visf is the volume of interstitial fluid in mL. Defaults to 0.5. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). Defaults to 0.15. + """ + + self.xdim = xdim + self.ydim = ydim + self.numParam = numParam + self.Flow = Flow + self.Vp = Vp + self.Visf = Visf + self.PS = PS + + #build GUI with dims x, y + #build button in center named Optimize! + + def slider(numParam = 4, labels = ['Flow', 'Vp', 'Visf', 'PS']): + """Adds a slider for each parameter to the GUI with labels as their names. + Parameters + ---------- + numParam : int + numParam is defaulted to 4 (self.numParam) and is used for defining the number of sliders in th GUI. + + labels : str[] + labels are defaulted to Flow, Vp, Visf, and PS, and are used for naming the sliders. + """ + #Creates and sliders with names for each model. + #EVENT: move sliders = move parameter values and change model + + def IOBoxes(numParam = 4): + """Creates text boxes for initial guesses and output text boxes for final parameter values. + Parameters + ---------- + numParam : int + numParam is defaulted to 4 (self.numParam) and is used for defining the number of text boxes in th GUI. + """ + #Creates text boxes for initial guesses + #Creates text boxes for displaying output (optimized) values. + + def passValues(paramName, paramVal): + """If a parameter is changed from slider, it will pass the changes to the Model object and text boxes. + Parameters + ---------- + paramName : str + paramName is the name of the parameter that changed. + + paramVal : double + paramVal is the new value of the parameter that is passed to the model and text boxes. + """ + self.paramName = paramVal + + #update text box output + #update model + + def getValues(paramName, model): + """Get the parameter value of paramName from model model. + + Parameters + ---------- + paramName : str + paramName is the name of the parameter that changed. + + model : model object + model is the model you want to get values from. + """ + #call object for value + + def dispModel(objModel): + """Displays a Model object in GUI. + Parameters + ---------- + objModel : Model object + objModel will be either a 1 Comp or 2 Comp model and will be displayed in the GUI. + """ + #Displays model in GUI + + +# In[ ]:</code></pre> +</details> +</section> +<section> +</section> +<section> +</section> +<section> +</section> +<section> +<h2 class="section-title" id="header-classes">Classes</h2> +<dl> +<dt id="pkGUI.pkGUI"><code class="flex name class"> +<span>class <span class="ident">pkGUI</span></span> +<span>(</span><span>xdim=512, ydim=320, numParam=4, Flow=1, Vp=0.1, Visf=0.5, PS=0.15)</span> +</code></dt> +<dd> +<section class="desc"><p>The pkGUI object creates a GUI of size x by y to display the optimization of parameters for PK models.</p> +<p>Initializes the GUI with default size 512 by 320 pixels and one button to start optimization. +Parameters</p> +<hr> +<dl> +<dt><strong><code>xdim</code></strong> : <code>int</code></dt> +<dd>xdim is used for defining the x dimension of your GUI size.</dd> +<dt><strong><code>ydim</code></strong> : <code>int</code></dt> +<dd>ydim is used for defining the y dimension of your GUI size.</dd> +<dt><strong><code>numParam</code></strong> : <code>int</code></dt> +<dd>numParam is the number of parameters you want to optimize for the model. Defaults to 4.</dd> +<dt><strong><code>Flow</code></strong> : <code>double</code></dt> +<dd>Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1.</dd> +<dt><strong><code>Vp</code></strong> : <code>double</code></dt> +<dd>Vp is the volume of plasma in mL. Defaults to 0.1.</dd> +<dt><strong><code>Visf</code></strong> : <code>double</code></dt> +<dd>Visf is the volume of interstitial fluid in mL. Defaults to 0.5.</dd> +<dt><strong><code>PS</code></strong> : <code>double</code></dt> +<dd>PS is the permeability-surface area constant in mL/(g*min). Defaults to 0.15.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">class pkGUI: + """The pkGUI object creates a GUI of size x by y to display the optimization of parameters for PK models.""" + + def __init__ (self, xdim = 512, ydim = 320, numParam = 4, Flow = 1, Vp = 0.1, Visf = 0.5, PS = 0.15): + + """Initializes the GUI with default size 512 by 320 pixels and one button to start optimization. + Parameters + ---------- + xdim : int + xdim is used for defining the x dimension of your GUI size. + + ydim : int + ydim is used for defining the y dimension of your GUI size. + + numParam: int + numParam is the number of parameters you want to optimize for the model. Defaults to 4. + + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1. + + Vp : double + Vp is the volume of plasma in mL. Defaults to 0.1. + + Visf : double + Visf is the volume of interstitial fluid in mL. Defaults to 0.5. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). Defaults to 0.15. + """ + + self.xdim = xdim + self.ydim = ydim + self.numParam = numParam + self.Flow = Flow + self.Vp = Vp + self.Visf = Visf + self.PS = PS + + #build GUI with dims x, y + #build button in center named Optimize! + + def slider(numParam = 4, labels = ['Flow', 'Vp', 'Visf', 'PS']): + """Adds a slider for each parameter to the GUI with labels as their names. + Parameters + ---------- + numParam : int + numParam is defaulted to 4 (self.numParam) and is used for defining the number of sliders in th GUI. + + labels : str[] + labels are defaulted to Flow, Vp, Visf, and PS, and are used for naming the sliders. + """ + #Creates and sliders with names for each model. + #EVENT: move sliders = move parameter values and change model + + def IOBoxes(numParam = 4): + """Creates text boxes for initial guesses and output text boxes for final parameter values. + Parameters + ---------- + numParam : int + numParam is defaulted to 4 (self.numParam) and is used for defining the number of text boxes in th GUI. + """ + #Creates text boxes for initial guesses + #Creates text boxes for displaying output (optimized) values. + + def passValues(paramName, paramVal): + """If a parameter is changed from slider, it will pass the changes to the Model object and text boxes. + Parameters + ---------- + paramName : str + paramName is the name of the parameter that changed. + + paramVal : double + paramVal is the new value of the parameter that is passed to the model and text boxes. + """ + self.paramName = paramVal + + #update text box output + #update model + + def getValues(paramName, model): + """Get the parameter value of paramName from model model. + + Parameters + ---------- + paramName : str + paramName is the name of the parameter that changed. + + model : model object + model is the model you want to get values from. + """ + #call object for value + + def dispModel(objModel): + """Displays a Model object in GUI. + Parameters + ---------- + objModel : Model object + objModel will be either a 1 Comp or 2 Comp model and will be displayed in the GUI. + """</code></pre> +</details> +<h3>Methods</h3> +<dl> +<dt id="pkGUI.pkGUI.IOBoxes"><code class="name flex"> +<span>def <span class="ident">IOBoxes</span></span>(<span>numParam=4)</span> +</code></dt> +<dd> +<section class="desc"><p>Creates text boxes for initial guesses and output text boxes for final parameter values. +Parameters</p> +<hr> +<dl> +<dt><strong><code>numParam</code></strong> : <code>int</code></dt> +<dd>numParam is defaulted to 4 (self.numParam) and is used for defining the number of text boxes in th GUI.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def IOBoxes(numParam = 4): + """Creates text boxes for initial guesses and output text boxes for final parameter values. + Parameters + ---------- + numParam : int + numParam is defaulted to 4 (self.numParam) and is used for defining the number of text boxes in th GUI. + """</code></pre> +</details> +</dd> +<dt id="pkGUI.pkGUI.dispModel"><code class="name flex"> +<span>def <span class="ident">dispModel</span></span>(<span>objModel)</span> +</code></dt> +<dd> +<section class="desc"><p>Displays a Model object in GUI. +Parameters</p> +<hr> +<dl> +<dt><strong><code>objModel</code></strong> : <code>Model</code> <code>object</code></dt> +<dd>objModel will be either a 1 Comp or 2 Comp model and will be displayed in the GUI.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def dispModel(objModel): + """Displays a Model object in GUI. + Parameters + ---------- + objModel : Model object + objModel will be either a 1 Comp or 2 Comp model and will be displayed in the GUI. + """</code></pre> +</details> +</dd> +<dt id="pkGUI.pkGUI.getValues"><code class="name flex"> +<span>def <span class="ident">getValues</span></span>(<span>paramName, model)</span> +</code></dt> +<dd> +<section class="desc"><p>Get the parameter value of paramName from model model.</p> +<h2 id="parameters">Parameters</h2> +<dl> +<dt><strong><code>paramName</code></strong> : <code>str</code></dt> +<dd>paramName is the name of the parameter that changed.</dd> +<dt><strong><code>model</code></strong> : <code>model</code> <code>object</code></dt> +<dd>model is the model you want to get values from.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def getValues(paramName, model): + """Get the parameter value of paramName from model model. + + Parameters + ---------- + paramName : str + paramName is the name of the parameter that changed. + + model : model object + model is the model you want to get values from. + """</code></pre> +</details> +</dd> +<dt id="pkGUI.pkGUI.passValues"><code class="name flex"> +<span>def <span class="ident">passValues</span></span>(<span>paramName, paramVal)</span> +</code></dt> +<dd> +<section class="desc"><p>If a parameter is changed from slider, it will pass the changes to the Model object and text boxes. +Parameters</p> +<hr> +<dl> +<dt><strong><code>paramName</code></strong> : <code>str</code></dt> +<dd>paramName is the name of the parameter that changed.</dd> +<dt><strong><code>paramVal</code></strong> : <code>double</code></dt> +<dd>paramVal is the new value of the parameter that is passed to the model and text boxes.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def passValues(paramName, paramVal): + """If a parameter is changed from slider, it will pass the changes to the Model object and text boxes. + Parameters + ---------- + paramName : str + paramName is the name of the parameter that changed. + + paramVal : double + paramVal is the new value of the parameter that is passed to the model and text boxes. + """ + self.paramName = paramVal</code></pre> +</details> +</dd> +<dt id="pkGUI.pkGUI.slider"><code class="name flex"> +<span>def <span class="ident">slider</span></span>(<span>numParam=4, labels=['Flow', 'Vp', 'Visf', 'PS'])</span> +</code></dt> +<dd> +<section class="desc"><p>Adds a slider for each parameter to the GUI with labels as their names. +Parameters</p> +<hr> +<dl> +<dt><strong><code>numParam</code></strong> : <code>int</code></dt> +<dd>numParam is defaulted to 4 (self.numParam) and is used for defining the number of sliders in th GUI.</dd> +<dt><strong><code>labels</code></strong> : <code>str</code>[]</dt> +<dd>labels are defaulted to Flow, Vp, Visf, and PS, and are used for naming the sliders.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def slider(numParam = 4, labels = ['Flow', 'Vp', 'Visf', 'PS']): + """Adds a slider for each parameter to the GUI with labels as their names. + Parameters + ---------- + numParam : int + numParam is defaulted to 4 (self.numParam) and is used for defining the number of sliders in th GUI. + + labels : str[] + labels are defaulted to Flow, Vp, Visf, and PS, and are used for naming the sliders. + """</code></pre> +</details> +</dd> +</dl> +</dd> +</dl> +</section> +</article> +<nav id="sidebar"> +<h1>Index</h1> +<div class="toc"> +<ul></ul> +</div> +<ul id="index"> +<li><h3><a href="#header-classes">Classes</a></h3> +<ul> +<li> +<h4><code><a title="pkGUI.pkGUI" href="#pkGUI.pkGUI">pkGUI</a></code></h4> +<ul class=""> +<li><code><a title="pkGUI.pkGUI.IOBoxes" href="#pkGUI.pkGUI.IOBoxes">IOBoxes</a></code></li> +<li><code><a title="pkGUI.pkGUI.dispModel" href="#pkGUI.pkGUI.dispModel">dispModel</a></code></li> +<li><code><a title="pkGUI.pkGUI.getValues" href="#pkGUI.pkGUI.getValues">getValues</a></code></li> +<li><code><a title="pkGUI.pkGUI.passValues" href="#pkGUI.pkGUI.passValues">passValues</a></code></li> +<li><code><a title="pkGUI.pkGUI.slider" href="#pkGUI.pkGUI.slider">slider</a></code></li> +</ul> +</li> +</ul> +</li> +</ul> +</nav> +</main> +<footer id="footer"> +<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.7.4</a>.</p> +</footer> +<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script> +<script>hljs.initHighlightingOnLoad()</script> +</body> +</html> \ No newline at end of file diff --git a/doc/pkOptimizer.html b/doc/pkOptimizer.html new file mode 100644 index 0000000..61df014 --- /dev/null +++ b/doc/pkOptimizer.html @@ -0,0 +1,561 @@ +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" /> +<meta name="generator" content="pdoc 0.7.4" /> +<title>pkOptimizer API documentation</title> +<meta name="description" content="" /> +<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'> +<link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'> +<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet"> +<style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style> +<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style> +<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style> +</head> +<body> +<main> +<article id="content"> +<header> +<h1 class="title">Module <code>pkOptimizer</code></h1> +</header> +<section id="section-intro"> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">#!/usr/bin/env python +# coding: utf-8 + +# In[9]: + + +from scipy.stats import gamma +from scipy.integrate import odeint +from scipy.optimize import minimize +from scipy.optimize import curve_fit + +import os +import csv +import re +import math as math +import numpy as np +import matplotlib.pyplot as plt +#%matplotlib inline + +class pkOptimizer: + """The pkOptimizer object is an optimizer for parameters in pk models.""" + + def __init__ (self, wd, Flow = 1/60, Vp = 0.05, Visf = 0.15, PS = 1/60): + """Initializes the model with initial guess parameter values for flow, Vp, Visf, and PS. + Parameters + ---------- + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1/60. + + Vp : double + Vp is the volume of plasma in mL. Defaults to 0.05. + + Visf : double + Visf is the volume of interstitial fluid in mL. Defaults to 0.15. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). Defaults to 1/60. + """ + + def getData(self, wd): + """Imports data from all .csv files in directory. + Parameters + ---------- + wd : str + wd is the working directory path + + Attributes + ---------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + + Returns + ------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + """ + + os.chdir(wd) + #os.chdir(r"C:\Users\Ethan\OneDrive - Michigan State University\MSU\Classwork\Computational Modeling\Models\Data") + #create directory of all csv files, + data = list(csv.reader(open('CTPERF005_stress.csv'), delimiter = '\t')) + + t = [] + aorta = [] + myo = [] + + for i in range(12): + t.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][0])[0])) + aorta.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][1])[0])) + myo.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][2])[0])) + + return t, aorta, myo + + def gammaFunc(self, time, a, l, s): + """Creates a gamma variate probability density function with given alpha, location, and scale values. + Parameters + ---------- + time : double[] + array of timepoints + a : double + alpha value of gamma PDF + l : double + location of 50th percentile of function + s : double + scale parameter + + Returns + ------- + rv.pdf(time) + probability density function of your gamma variate. + """ + rv = gamma(a, loc = l, scale = s) #input function + return rv.pdf(time) + + def curveFit(self, t, aorta, myo, model): + """Takes in data and fits gamma curve to aorta and Cisf from model to myo. Returns parameters for best fit. + + Parameters + ---------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + model : pkModel object + a pk model, either 1Comp or 2Comp + + Returns + ------- + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). + + Vp : double + Vp is the volume of plasma in mL. + + Visf : double + Visf is the volume of interstitial fluid in mL. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). + """ + + def getPlot(self): + """Plots the original data to the fitted curve.""" + plt.plot(t, aorta, 'bo', label='data') + #plt.plot(t, y, 'b-', label='data') + popt, pcov = curve_fit(gammaFunc, t, aorta, p0 = [2, 8, 10000], method = 'trf') + + print(f'alpha = {popt[0]}, loc = {popt[1]}, scale = {popt[2]}') + + plt.plot(t, gammaFunc(t, *popt), 'r-', label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)) + plt.plot(time, gammaFunc(time, .1313, 8.533, 10000), 'b-') + + +# In[ ]:</code></pre> +</details> +</section> +<section> +</section> +<section> +</section> +<section> +</section> +<section> +<h2 class="section-title" id="header-classes">Classes</h2> +<dl> +<dt id="pkOptimizer.pkOptimizer"><code class="flex name class"> +<span>class <span class="ident">pkOptimizer</span></span> +<span>(</span><span>wd, Flow=0.016666666666666666, Vp=0.05, Visf=0.15, PS=0.016666666666666666)</span> +</code></dt> +<dd> +<section class="desc"><p>The pkOptimizer object is an optimizer for parameters in pk models.</p> +<p>Initializes the model with initial guess parameter values for flow, Vp, Visf, and PS. +Parameters</p> +<hr> +<p>Flow : double +Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1/60.</p> +<p>Vp : double +Vp is the volume of plasma in mL. Defaults to 0.05.</p> +<p>Visf : double +Visf is the volume of interstitial fluid in mL. Defaults to 0.15.</p> +<p>PS : double +PS is the permeability-surface area constant in mL/(g*min). Defaults to 1/60.</p></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">class pkOptimizer: + """The pkOptimizer object is an optimizer for parameters in pk models.""" + + def __init__ (self, wd, Flow = 1/60, Vp = 0.05, Visf = 0.15, PS = 1/60): + """Initializes the model with initial guess parameter values for flow, Vp, Visf, and PS. + Parameters + ---------- + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1/60. + + Vp : double + Vp is the volume of plasma in mL. Defaults to 0.05. + + Visf : double + Visf is the volume of interstitial fluid in mL. Defaults to 0.15. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). Defaults to 1/60. + """ + + def getData(self, wd): + """Imports data from all .csv files in directory. + Parameters + ---------- + wd : str + wd is the working directory path + + Attributes + ---------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + + Returns + ------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + """ + + os.chdir(wd) + #os.chdir(r"C:\Users\Ethan\OneDrive - Michigan State University\MSU\Classwork\Computational Modeling\Models\Data") + #create directory of all csv files, + data = list(csv.reader(open('CTPERF005_stress.csv'), delimiter = '\t')) + + t = [] + aorta = [] + myo = [] + + for i in range(12): + t.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][0])[0])) + aorta.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][1])[0])) + myo.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][2])[0])) + + return t, aorta, myo + + def gammaFunc(self, time, a, l, s): + """Creates a gamma variate probability density function with given alpha, location, and scale values. + Parameters + ---------- + time : double[] + array of timepoints + a : double + alpha value of gamma PDF + l : double + location of 50th percentile of function + s : double + scale parameter + + Returns + ------- + rv.pdf(time) + probability density function of your gamma variate. + """ + rv = gamma(a, loc = l, scale = s) #input function + return rv.pdf(time) + + def curveFit(self, t, aorta, myo, model): + """Takes in data and fits gamma curve to aorta and Cisf from model to myo. Returns parameters for best fit. + + Parameters + ---------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + model : pkModel object + a pk model, either 1Comp or 2Comp + + Returns + ------- + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). + + Vp : double + Vp is the volume of plasma in mL. + + Visf : double + Visf is the volume of interstitial fluid in mL. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). + """ + + def getPlot(self): + """Plots the original data to the fitted curve.""" + plt.plot(t, aorta, 'bo', label='data') + #plt.plot(t, y, 'b-', label='data') + popt, pcov = curve_fit(gammaFunc, t, aorta, p0 = [2, 8, 10000], method = 'trf') + + print(f'alpha = {popt[0]}, loc = {popt[1]}, scale = {popt[2]}') + + plt.plot(t, gammaFunc(t, *popt), 'r-', label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)) + plt.plot(time, gammaFunc(time, .1313, 8.533, 10000), 'b-')</code></pre> +</details> +<h3>Methods</h3> +<dl> +<dt id="pkOptimizer.pkOptimizer.curveFit"><code class="name flex"> +<span>def <span class="ident">curveFit</span></span>(<span>self, t, aorta, myo, model)</span> +</code></dt> +<dd> +<section class="desc"><p>Takes in data and fits gamma curve to aorta and Cisf from model to myo. Returns parameters for best fit.</p> +<h2 id="parameters">Parameters</h2> +<p>t : double[] +list of all timepoints +aorta : double[] +concentration of tracer in aorta (input function) +myo : double[] +concentration of tracer in myocardial tissue (Cisf) +model : pkModel object +a pk model, either 1Comp or 2Comp</p> +<h2 id="returns">Returns</h2> +<dl> +<dt><strong><code>Flow</code></strong> : <code>double</code></dt> +<dd>Flow is the flow of plasma through the blood vessel in mL/(mL*min).</dd> +<dt><strong><code>Vp</code></strong> : <code>double</code></dt> +<dd>Vp is the volume of plasma in mL.</dd> +<dt><strong><code>Visf</code></strong> : <code>double</code></dt> +<dd>Visf is the volume of interstitial fluid in mL.</dd> +<dt><strong><code>PS</code></strong> : <code>double</code></dt> +<dd>PS is the permeability-surface area constant in mL/(g*min).</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def curveFit(self, t, aorta, myo, model): + """Takes in data and fits gamma curve to aorta and Cisf from model to myo. Returns parameters for best fit. + + Parameters + ---------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + model : pkModel object + a pk model, either 1Comp or 2Comp + + Returns + ------- + Flow : double + Flow is the flow of plasma through the blood vessel in mL/(mL*min). + + Vp : double + Vp is the volume of plasma in mL. + + Visf : double + Visf is the volume of interstitial fluid in mL. + + PS : double + PS is the permeability-surface area constant in mL/(g*min). + """</code></pre> +</details> +</dd> +<dt id="pkOptimizer.pkOptimizer.gammaFunc"><code class="name flex"> +<span>def <span class="ident">gammaFunc</span></span>(<span>self, time, a, l, s)</span> +</code></dt> +<dd> +<section class="desc"><p>Creates a gamma variate probability density function with given alpha, location, and scale values. +Parameters</p> +<hr> +<p>time : double[] +array of timepoints +a : double +alpha value of gamma PDF +l : double +location of 50th percentile of function +s : double +scale parameter </p> +<h2 id="returns">Returns</h2> +<dl> +<dt><code>rv.pdf</code>(<code>time</code>)</dt> +<dd>probability density function of your gamma variate.</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def gammaFunc(self, time, a, l, s): + """Creates a gamma variate probability density function with given alpha, location, and scale values. + Parameters + ---------- + time : double[] + array of timepoints + a : double + alpha value of gamma PDF + l : double + location of 50th percentile of function + s : double + scale parameter + + Returns + ------- + rv.pdf(time) + probability density function of your gamma variate. + """ + rv = gamma(a, loc = l, scale = s) #input function + return rv.pdf(time)</code></pre> +</details> +</dd> +<dt id="pkOptimizer.pkOptimizer.getData"><code class="name flex"> +<span>def <span class="ident">getData</span></span>(<span>self, wd)</span> +</code></dt> +<dd> +<section class="desc"><p>Imports data from all .csv files in directory. +Parameters</p> +<hr> +<p>wd : str +wd is the working directory path</p> +<h2 id="attributes">Attributes</h2> +<dl> +<dt><strong><code>t</code></strong> : <code>double</code>[]</dt> +<dd>list of all timepoints</dd> +<dt><strong><code>aorta</code></strong> : <code>double</code>[]</dt> +<dd>concentration of tracer in aorta (input function)</dd> +<dt><strong><code>myo</code></strong> : <code>double</code>[]</dt> +<dd>concentration of tracer in myocardial tissue (Cisf)</dd> +</dl> +<h2 id="returns">Returns</h2> +<dl> +<dt><strong><code>t</code></strong> : <code>double</code>[]</dt> +<dd>list of all timepoints</dd> +<dt><strong><code>aorta</code></strong> : <code>double</code>[]</dt> +<dd>concentration of tracer in aorta (input function)</dd> +<dt><strong><code>myo</code></strong> : <code>double</code>[]</dt> +<dd>concentration of tracer in myocardial tissue (Cisf)</dd> +</dl></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def getData(self, wd): + """Imports data from all .csv files in directory. + Parameters + ---------- + wd : str + wd is the working directory path + + Attributes + ---------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + + Returns + ------- + t : double[] + list of all timepoints + aorta : double[] + concentration of tracer in aorta (input function) + myo : double[] + concentration of tracer in myocardial tissue (Cisf) + """ + + os.chdir(wd) + #os.chdir(r"C:\Users\Ethan\OneDrive - Michigan State University\MSU\Classwork\Computational Modeling\Models\Data") + #create directory of all csv files, + data = list(csv.reader(open('CTPERF005_stress.csv'), delimiter = '\t')) + + t = [] + aorta = [] + myo = [] + + for i in range(12): + t.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][0])[0])) + aorta.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][1])[0])) + myo.append(float(re.compile('\d+[.]+\d+|\d+').findall(data[i+1][2])[0])) + + return t, aorta, myo</code></pre> +</details> +</dd> +<dt id="pkOptimizer.pkOptimizer.getPlot"><code class="name flex"> +<span>def <span class="ident">getPlot</span></span>(<span>self)</span> +</code></dt> +<dd> +<section class="desc"><p>Plots the original data to the fitted curve.</p></section> +<details class="source"> +<summary> +<span>Expand source code</span> +</summary> +<pre><code class="python">def getPlot(self): + """Plots the original data to the fitted curve.""" + plt.plot(t, aorta, 'bo', label='data') + #plt.plot(t, y, 'b-', label='data') + popt, pcov = curve_fit(gammaFunc, t, aorta, p0 = [2, 8, 10000], method = 'trf') + + print(f'alpha = {popt[0]}, loc = {popt[1]}, scale = {popt[2]}') + + plt.plot(t, gammaFunc(t, *popt), 'r-', label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)) + plt.plot(time, gammaFunc(time, .1313, 8.533, 10000), 'b-')</code></pre> +</details> +</dd> +</dl> +</dd> +</dl> +</section> +</article> +<nav id="sidebar"> +<h1>Index</h1> +<div class="toc"> +<ul></ul> +</div> +<ul id="index"> +<li><h3><a href="#header-classes">Classes</a></h3> +<ul> +<li> +<h4><code><a title="pkOptimizer.pkOptimizer" href="#pkOptimizer.pkOptimizer">pkOptimizer</a></code></h4> +<ul class=""> +<li><code><a title="pkOptimizer.pkOptimizer.curveFit" href="#pkOptimizer.pkOptimizer.curveFit">curveFit</a></code></li> +<li><code><a title="pkOptimizer.pkOptimizer.gammaFunc" href="#pkOptimizer.pkOptimizer.gammaFunc">gammaFunc</a></code></li> +<li><code><a title="pkOptimizer.pkOptimizer.getData" href="#pkOptimizer.pkOptimizer.getData">getData</a></code></li> +<li><code><a title="pkOptimizer.pkOptimizer.getPlot" href="#pkOptimizer.pkOptimizer.getPlot">getPlot</a></code></li> +</ul> +</li> +</ul> +</li> +</ul> +</nav> +</main> +<footer id="footer"> +<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.7.4</a>.</p> +</footer> +<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script> +<script>hljs.initHighlightingOnLoad()</script> +</body> +</html> \ No newline at end of file diff --git a/pk_optimizer/pk1Comp.ipynb b/pk_optimizer/pk1Comp.ipynb index 017084c..b6512f2 100644 --- a/pk_optimizer/pk1Comp.ipynb +++ b/pk_optimizer/pk1Comp.ipynb @@ -10,7 +10,7 @@ "from scipy.stats import gamma\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", - "%matplotlib inline\n", + "#%matplotlib inline\n", "from scipy.integrate import odeint \n", "import math as math\n", "\n", diff --git a/pk_optimizer/pk2Comp.ipynb b/pk_optimizer/pk2Comp.ipynb index 15ea8fb..370e63d 100644 --- a/pk_optimizer/pk2Comp.ipynb +++ b/pk_optimizer/pk2Comp.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ @@ -10,7 +10,7 @@ "from scipy.stats import gamma\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", - "%matplotlib inline\n", + "#%matplotlib inline\n", "from scipy.integrate import odeint \n", "import math as math\n", "import os\n", @@ -42,24 +42,28 @@ " \n", " # Declare Variables for initial conditions\n", " self.numParam = numParam\n", - " self.Flow = Flow\n", + " self.flow = Flow\n", " self.Vp = Vp\n", " self.Visf = Visf\n", " self.PS = PS\n", " self.sol = []\n", - " Cp0 = 0 # Initial concentration of tracer in plasma\n", - " Cisf0 = 0 # Initial concentration of tracer in interstitial space\n", - " tmax = 10 #Time in seconds\n", - " dt = 0.1 #Time step\n", - " a = 2. # Alpha for gamma distribution\n", - " rv = gamma(a, loc = 2, scale = 0.55) #input function\n", - "\n", + " self.Cp0 = 0 # Initial concentration of tracer in plasma\n", + " self.Cisf0 = 0 # Initial concentration of tracer in interstitial space\n", + " self.tmax = 10 #Time in seconds\n", + " self.dt = 0.1 #Time step\n", + " self.a = 2. # Alpha for gamma distribution\n", + " self.rv = gamma(self.a, loc = 2, scale = 0.55) #input function\n", + " self.sol = []\n", + " self.Mass_plasma = [] #mass of tracer in plasma\n", + " self.Mass_isf = []\n", + " self.Q = []\n", + " \n", " # Define the time array\n", - " time = np.arange(0, tmax + dt, dt)\n", + " self.time = np.arange(0, self.tmax + self.dt, self.dt)\n", " \n", " \n", " # Derivative function\n", - " def derivs(curr_vals, time):\n", + " def derivs(self, curr_vals, time):\n", " \"\"\"Finds derivatives of ODEs.\n", " \n", " Parameters\n", @@ -82,30 +86,30 @@ " Cp, Cisf = curr_vals\n", "\n", " # Define value of input function Cin\n", - " Cin = rv.pdf(time) \n", + " Cin = self.rv.pdf(time) \n", "\n", " # Right-hand side of odes, which are used to computer the derivative\n", - " dCp_dt = (flow/Vp)*(Cin - Cp) + (PS/Vp)*(Cisf - Cp)\n", - " dCisf_dt = (PS/Visf)*(Cp - Cisf)\n", + " dCp_dt = (self.flow/self.Vp)*(Cin - Cp) + (self.PS/self.Vp)*(Cisf - Cp)\n", + " dCisf_dt = (self.PS/self.Visf)*(Cp - Cisf)\n", "\n", " return dCp_dt, dCisf_dt\n", "\n", " def main(self):\n", " \"\"\"Main function to solve ODEs\"\"\"\n", " # Store the initial values in a list\n", - " init = [Cp0, Cisf0]\n", + " init = [self.Cp0, self.Cisf0]\n", "\n", " # Solve the odes with odeint\n", - " self.sol = odeint(derivs, init, time)\n", + " self.sol = odeint(self.derivs, init, self.time)\n", "\n", - " Mass_plasma = Vp * sol[:,0] #mass of tracer in plasma\n", - " Mass_isf = Visf * sol[:,1] #mass of tracer in isf\n", - " Tp = Vp/(flow + PS) # mean transit time\n", - " E = 1 - np.exp(-PS/flow) #extraction fraction\n", - " Q = Mass_plasma + Mass_isf\n", + " self.Mass_plasma = self.Vp * self.sol[:,0] #mass of tracer in plasma\n", + " self.Mass_isf = self.Visf * self.sol[:,1] #mass of tracer in isf\n", + " #Tp = Vp/(flow + PS) # mean transit time\n", + " #E = 1 - np.exp(-PS/flow) #extraction fraction\n", + " self.Q = self.Mass_plasma + self.Mass_isf\n", "\n", - " print('The mean transit time is ' + str(Tp))\n", - " print('The extraction fraction is ' + str(E))\n", + " #print('The mean transit time is ' + str(Tp))\n", + " #print('The extraction fraction is ' + str(E))\n", "\n", " def getPlot(self):\n", " \"\"\"Plots the solution of the solved ODEs.\n", @@ -119,11 +123,11 @@ " # Plot the results using the values stored in the solution variable, \"sol\"\n", " # Plot Cp using the \"0\" element from the solution\n", " plt.figure(1)\n", - " plt.plot(time, rv.pdf(time), color = 'blue', label = 'Input Function')\n", - " plt.plot(time, sol[:,0],color=\"green\", label = 'Cp')\n", + " plt.plot(self.time, self.rv.pdf(self.time), color = 'blue', label = 'Input Function')\n", + " plt.plot(self.time, self.sol[:,0],color=\"green\", label = 'Cp')\n", "\n", " # Plot Cisf using the \"1\" element from the solution\n", - " plt.plot(time, sol[:,1],color=\"purple\", label = 'Cisf')\n", + " plt.plot(self.time, self.sol[:,1],color=\"purple\", label = 'Cisf')\n", " plt.xlabel('Time [s]')\n", " plt.ylabel('Concentration [mM]')\n", " plt.legend(loc = 'best')\n", @@ -131,19 +135,70 @@ "\n", " # Plot mass of tracer using the \"2\" element from the solution\n", " plt.figure(2)\n", - " plt.plot(time, Mass_plasma,color=\"red\", label = 'Plasma')\n", + " plt.plot(self.time, self.Mass_plasma,color=\"red\", label = 'Plasma')\n", " \n", " # Plot mass of tracer in tissue using the \"3\" element from the solution\n", - " plt.plot(time, Mass_isf,color=\"black\", label = 'Interstitial Space')\n", - " plt.plot(time, Q, color=\"blue\", label = 'Total mass')\n", + " plt.plot(self.time, self.Mass_isf,color=\"black\", label = 'Interstitial Space')\n", + " plt.plot(self.time, self.Q, color=\"blue\", label = 'Total mass')\n", " plt.xlabel('Time [s]')\n", " plt.ylabel('Mass [mg]')\n", " plt.legend(loc = 'best')\n", " plt.grid()\n", "\n", - " print('Cp at 10 sec is ' + str(sol[100,0]))\n", - " print('Cisf at 10 sec is ' + str(sol[100,1]))\n" + " print('Cp at 10 sec is ' + str(self.sol[100,0]))\n", + " print('Cisf at 10 sec is ' + str(self.sol[100,1]))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cp at 10 sec is 0.00253322382670989\n", + "Cisf at 10 sec is 0.019226757646006787\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAEKCAYAAAD9xUlFAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzt3Xl8VNX5+PHPM5M9hD2ALEJAkJ2wGEGURkQFFxa1KiLuIrYqWn9+i1qVUmu1blVLtWpRsVVQQKFURcGEXXZcWIQAAQLITsi+nt8fdxKGkGUymTuTSZ63r/u6M3fuPfe5JM6Tc86954gxBqWUUgrAEegAlFJK1R6aFJRSSpXSpKCUUqqUJgWllFKlNCkopZQqpUlBKaVUKVuTgogMF5GfRSRFRCaX8/mrIrLJtWwXkZN2xqOUUqpyYtdzCiLiBLYDlwNpwFpgrDFmSwX7Pwj0NcbcZUtASimlqmRnTSEBSDHG7DLG5AMzgVGV7D8W+NjGeJRSSlUhxMay2wD73N6nAReWt6OItAfigG8r+HwCMAEgMjKyf7t27bwKqLi4GIejfnWj6DXXD3rN9UNNrnn79u1HjTGxVe1nZ1KQcrZV1FZ1MzDbGFNU3ofGmLeBtwEGDBhg1q1b51VAycnJJCYmenVssNJrrh/0muuHmlyziOzxZD8702wa4P4nfVvgQAX73ow2HSmlVMDZmRTWAp1FJE5EwrC++OeX3UlEzgeaAKtsjEUppZQHbEsKxphC4AFgIbAV+MQYs1lEporISLddxwIzjQ7XqpRSAWdnnwLGmC+AL8pse7rM+yl2xqCUqrmCggLS0tLIzc0NdCilGjVqxNatWwMdhl95cs0RERG0bduW0NBQr85ha1JQStUNaWlpxMTE0KFDB0TKu4fE/zIyMoiJiQl0GH5V1TUbYzh27BhpaWnExcV5dY76dT+XUsorubm5NGvWrNYkBFU+EaFZs2Y1qtFpUlBKeUQTQnCo6c9Jm49UubKy4KOP4NQpyM2FiAh46CHwsplSKRUkNCmocr32Gjz55JnbQkJg0qTAxKNUgwYNyMzM9GmZqamprFy5kltuuaXcz7p168b5559fum3NmjWEhYXZcu5169YxY8YMXn/9dZ+U7y1tPlJnMQamT4chQ+DkScjLg8svhz/+EY4fD3R0SvlOamoqH330UYWfd+rUiU2bNpUuvkoI5Z17wIABAU8IoElBlWPZMti5E+65Bxo1grAwePllSE+HqVMDHZ2q70qGehg/fjxdu3Zl3LhxlDzm1KFDB37/+9+TkJBAQkICKSkpANxxxx3Mnj27tIwGDRoAMHnyZJYtW0Z8fDyvvvqqR+efMmUKL730Uun7nj17kpqaWlqzuPfee+nRowdXXHEFOTk5AKSkpDBs2DD69OlDv3792Llz51nnTk5O5pprrgHg+PHjjB49mt69ezNw4EB++OEHAJ577jnuuusuEhMT6dixoy1JRJuP1Fneew9iYuD6609v69UL7r4bpk2D3/wGunQJXHwqsB5+GDZt8m2Z8fHwt795vv/GjRtZvXo1Xbp0YfDgwaxYsYKLL74YgIYNG7JmzRpmzJjBww8/zIIFCyos5/nnn+ell16qcJ+dO3cSHx8PwODBg5k2bVqlce3YsYOPP/6Yd955hxtvvJE5c+Zw6623Mm7cOCZPnsyYMWPIzc2luLj4rHMnJyeXlvPMM8/Qt29fPv/8c7799ltuu+02Nrn+0bdt20ZSUhIZGRmcf/753H///V4/k1AerSmoM2RkwCefwE03QVTUmZ/96U9Wh/NjjwUmNqVKJCQk0KZNGxwOB/Hx8aSmppZ+Nnbs2NL1qlU1Gz3HvfmoqoQAEBcXV5pE+vfvT2pqKhkZGezfv58xY8YA1sNlUWX/5ypj+fLljB8/HoChQ4dy7Ngx0tPTAbj66qsJDw+nefPmtGjRgkOHDtXkEs+iNQV1hk8/hexsuKucqY5atoQnnrCWtWvhggv8H58KvOr8RW+X8PDw0tdOp5PCwsLS9+63ZJa8DgkJobi4GLAe8MrPz/f63O5lAWc8E1A2rpycHLwZwae8Y0qupbJr9wWtKagzvPcenH8+DBxY/uf332/dljpzpn/jUspTs2bNKl0PGjQIsPoa1q9fD8C8efMoKCgAICYmhoyMjGqV36FDBzZs2ADAhg0b2L17d6X7N2zYkLZt2/L5558DkJeXR3Z2dqXnHjJkCP/5z38Aq1mpefPmNGzYsFpxekuTgiq1fTssXw533gkVPf/SuDFceaVVo3D7Y0mpWiMvL48LL7yQ1157rbTz+N5772XJkiUkJCSwevVqoqOjAejduzchISH06dPH447m66+/nuPHjxMfH8+bb75JFw862D788ENef/11evfuzUUXXcQvv/xS6bmnTJnCunXr6N27N5MnT+aDDz6o5r9CDRhjgmrp37+/8VZSUpLXxwar6lzzK68YA8bs21f5fjNmWPutXFmz2OyiP2ff27Jli63le+PUqVNnbWvfvr05cuRIAKLxj/KuuTzl/byAdcaD71itKahSW7ZAbCy0bVv5fiNHWrepfvKJf+JSSvmPJgVVautW6Nq16v0aNYLhw7UJSdU+qampNG/ePNBhBDVNCgqwnmLeuhW6dfNs/xtvhP37oYZ3/CmlahlNCgqAI0esISw8TQrXXgvh4dqEpFRdo0lBAVYtATxPCg0bwogR2oSkVF2jSUEB1U8KYDUhHTyoTUhK1SWaFBRgJYXoaGjXzvNjRowAhwO+/tq+uJQq8csvv3DzzTfTqVMnunfvzvXXX8/27dsDHVado0lBAafvPKrOpE2NG1tDXXzzjX1xKQXW81RjxowhMTGRnTt3smXLFp555hmfj/ujbE4KIjJcRH4WkRQRmVzBPjeKyBYR2SwiFQ9srmxVnTuP3A0bBmvWWMNqK2WXpKQkQkNDmThxYum23r17U1RUxJAhQxgzZgzdu3dn4sSJZ4xLpKrPtgHxRMQJTAMuB9KAtSIy3xizxW2fzsDjwGBjzAkRaWFXPKpiGRmQluZdUrj8cvjznyE5GUaN8nloqhZ6+KuH2fSLb8fOjm8Vz9+GVzzS3k8//UT//v3L/WzNmjVs2bKF9u3bM3z4cObOncsNN9zg0/jqEztrCglAijFmlzEmH5gJlP3auBeYZow5AWCMOWxjPKoC27ZZa2+SwsCB1hDbixb5NialPJWQkEDHjh1xOp2MHTuW5cuXBzqkoGbn0NltgH1u79OAC8vs0wVARFYATmCKMearsgWJyARgAkDLli3PmIyiOjIzM70+Nlh5cs0LF7YEupGRsYbk5Oxqn6NXr17MmxfJ9dev8S5IH9Ofs+81atSodETPPw3+ky3nqGy00ri4OGbNmnXGPkVFRWRnZ1NcXFy6PTc3l4KCgmqPfBosioqKPLq23Nxc738fPBkgyZsF+DXwrtv78cAbZfZZAHwGhAJxWImjcWXl6oB41ePJNU+ebExIiDH5+d6do2Qgvb17vTve1/Tn7HuBHhCvuLjYJCQkmLfffrt0W1JSkpkyZYqJiIgwu3btMkVFReaKK64ws2fPDmCk9gr2AfHSAPcbHNsCB8rZZ54xpsAYsxv4GehsY0yqHFu3wnnnWfMkeGPYMGutTUjKLiLCZ599xjfffEOnTp3o0aMHf/nLX2jdujWDBg1i8uTJ9OzZk7i4uNIZzpR37Gw+Wgt0FpE4YD9wM3BLmX0+B8YC74tIc6zmpF02xqTKsXUr9Ojh/fE9e1qzsi1aZM3FoJQdWrduzSdu46pkZGSwfv16oqKiSifWUTVnW03BGFMIPAAsBLYCnxhjNovIVBEZ6dptIXBMRLYAScBjxphjdsWkzpafDzt3etfJXELEqi0sWqRDXigV7Gydo9kY8wXwRZltT7u9NsDvXIsKgB07oKioZkkBrFtT//Mf+PFH6NPHN7EpVZXExEQSExMDHUadok8013PejHlUnqFDrXU9u+lHqTpHk0I9l5JirT2YZrZS7dpBhw6wbFmNQ1JKBZAmhXru0CFrILyYmJqXNWQILF1qTdijlApOmhTquUOHrDuHfOGSS6zJen7+2TflKaX8T5NCPefLpDBkiLXWJiRlh/KGzl66dGmV4xy9/vrrdOvWjXHjxvkp0uBm691HqvY7dAg6++hxwc6drQSzdCnce69vylQKTg+dffvttzNz5kwAVqxYQXFxMbNnz6702H/84x98+eWXxMXF+SPUoKc1hXrOlzUFEasJSWsKytcqGjq7Xbt29OzZE4DNmzeTkJBAfHw8vXv3ZseOHUycOJFdu3YxcuRIXn311UCFH1S0plCPFRbCsWO+SwpgNSHNng179kD79r4rV9UeXz38Fb9s+sWnZbaKb8Xwvw2v8PPKhs4u8dZbbzFp0iTGjRtHfn4+RUVFvPXWW3z11VckJSXRvHlzn8ZcV2lNoR47etS6U6iFD2exuOQSa621BeVvgwYN4rnnnuOFF15gz549REZGBjqkoKQ1hXqsZCZDX9YUevWCRo2spHDrrb4rV9Uelf1Fb5cePXpU2Xdwyy23cOGFF/K///2PK6+8knfffZehJU9VKo9pTaEesyMpOJ0weLDV2ayUrwwdOpS8vDzeeeed0m3r169nz549pe937dpFx44deeihhxg5ciQ//PBDIEINepoU6jE7kgJY/QrbtsFhnUdP+UhlQ2eXmDVrFj179iQ+Pp5t27Zx2223BTDi4KXNR/VYyZe2r5NCSb/C8uVw3XW+LVvVX+UNnR0TE8NPP/0EwOOPP87jjz9+1nGpqan+CrFO0JpCPXboEISFQcOGvi23f38ID4cVK3xbrlLKfpoU6rGSZxREfFtueDhccIEmBaWCkSaFesyXD66VNXgwrF8P2dn2lK/8z+hIh0Ghpj8nTQr1mN1JobAQ1q61p3zlXxERERw7dkwTQy1njOHYsWNERER4XYZ2NNdjhw9Dv372lH3RRdZ6xQr41a/sOYfyn7Zt25KWlsaRI0cCHUqp3NzcGn35BSNPrjkiIoK2bdt6fQ5NCvVUcbGVFOyqKTRrZs3mpv0KdUNoaGitG1AuOTmZvn37BjoMv/LHNWvzUT114oTVvOPLIS7KGjwYVq60EpBSKjhoUqin7Hpwzd3gwXDy5Ol5oJVStZ+tSUFEhovIzyKSIiKTy/n8DhE5IiKbXMs9dsajTvNXUgBtQlIqmNiWFETECUwDRgDdgbEi0r2cXWcZY+Jdy7t2xaPOZNfTzO7OOw9iYzUpKBVM7KwpJAApxphdxph8YCYwysbzqWrwR01BxKotaFJQKnjYefdRG2Cf2/s04MJy9rteRIYA24FHjDH7yu4gIhOACQAtW7YkOTnZq4AyMzO9PjZYVXTNa9bE4XCcyw8/LMFh458GrVq14/PPOzF37gqaNi2w70Ru9OdcP+g128QYY8sC/Bp41+39eOCNMvs0A8JdrycC31ZVbv/+/Y23kpKSvD42WFV0zXffbUyrVvaff+VKY8CYOXPsP1cJ/TnXD3rN1QOsMx58d1f6N6KInKpiyRCR7RUcnga0c3vfFjhQJiEdM8bkud6+A1Q+357yGTufZnbXr58OjqdUMKmq4WCnMaZhJUsMkFXBsWuBziISJyJhwM3AfPcdROQct7cjAb150U/sfHDNnQ6Op1RwqSopXO9BGeXuY4wpBB4AFmJ92X9ijNksIlNFZKRrt4dEZLOIfA88BNzhWdiqpvxVUwCrs3nDBsjJ8c/5lFLeqzQpGGN2VVVAZfsYY74wxnQxxnQyxvzZte1pY8x81+vHjTE9jDF9jDGXGmO2VfcCVPUZ4/+kUFCgg+MpFQyq6lPIKKcPoXTtryCVb2VkQG6uvUNcuHMfHE8pVbtVdUvqYqAVMBeYaYzZa39Iym7+eEbBXbNm0LWrJgWlgkFVzUejgSuBI8A7IrJERH4jIk39Ep2yhT+eZi5LB8dTKjhU+diSMSbdGPMe1nAVbwFT0Q7hoObvmgJYSeHECdimvUZK1WpVJgURuUhE3gA2AIOBMcaYV2yPTNkmUEkBtAlJqdquqo7mVOAfwH6sYSamA1ki0k9EbJqzS9mtpPkoNtZ/5+zcWQfHUyoYVNXRnAoYrH6FK8t8ZoChNsSkbJaeDg0aQIgf593TwfGUCg6Vfi0YYxL9FIfyo1OnoGFD/5938GD4/HP/PiOhlKoej/5WdM2NcDXQwf0Y7VsITqdOQaNG/j9vSb/CypUwZoz/z6+Uqpqngyb/F+uOo2ZAjNuiglB6emBqCv36QUSENiEpVZt52qrc1hjT29ZIlN8EqvkoPBwSEmDZMv+fWynlGU9rCl+KyBW2RqL8JlBJAeDii63B8bIqGltXKRVQniaF74DPRCRHxz4KfoHqUwC45BIoLITVqwNzfqVU5TxNCi8Dg4CoknkUjDEB+ltT1VSg+hTAGhzP4dAmJKVqK0+Twg7gJ9eUbiqIFRdbo6QGKik0bAi9e2tSUKq28rSj+SCQLCJfAiXTZ+otqUEoM9NaB6r5CKwmpOnTrTkWQkMDF4dS6mye1hR2Yw2jHYbekhrU0tOtdaBqCmB1NmdlwaZNgYtBKVU+j2oKxpg/2h2I8o9TrtsDAp0UwGpCuuCCwMWhlDpbVQPiTamqAE/2UbVHbUgKrVtDx46wfHngYlBKla+qmsI9Vdx6KsDNwBSfRaRsVZIUAtmnAFa/whdfWPNFiwQ2FqXUaVX1KbzDmX0IZZcGrn3KJSLDReRnEUkRkcmV7HeDiBgRGVDdC1DVUxv6FMBKCkeOwM8/BzYOpdSZqhol1eu+BNcgetOAy4E0YK2IzDfGbCmzXwzwEKCPM/lBbWg+gjP7Fbp2DWwsSqnTPL37yBsJQIoxZpcxJh+YCYwqZ78/AX8Fcm2MRbnUlqTQpQu0aAFLlwY2DqXUmeycZqUNsM/tfRpwofsOItIXaGeMWSAi/6+igkRkAtbMb7Rs2ZLk5GSvAsrMzPT62GBV9pp/+KED0IH165Nx2PkngQe6d+/OwoUNSUr6zqf9Cvpzrh/0mu1hZ1Io73/z0ieiRcQBvIo1JHeljDFvA28DDBgwwCQmJnoVUHJyMt4eG6zKXvO8eRATA0OHJlZwhP9s2QLJydC+fSIdO/quXP051w96zfbwdJKdWOBezp5k565KDksD2rm9bwsccHsfA/TEelIaoBUwX0RGGmPWeRKXqr5AjpBaVsnvdnIyPk0KSinvedqAMA9oBCwC/ue2VGYt0FlE4kQkDOvW1fklHxpj0o0xzY0xHYwxHbBGYtWEYLNAjpBaVrduEBtrJQWlVO3gafNRlDHm99Up2BhTKCIPAAsBJzDdGLNZRKYC64wx8ysvQdmhNtUUROBXv4IlS/R5BaVqC0+TwgIRucoY80V1Cnft/0WZbU9XsG9idcpW3klPrz01BbCakGbPhtRUiIsLdDRKKU+bjyZhJYZc1wQ7OslOkKpNNQWwagpg1RaUUoHnUVJwTarjMMZEuF7rJDtBqjb1KQB07w7Nm2u/glK1hce3pIrISGCI622yMWaBPSEpOwW6prDt6Da+3vk1S/YsYdW+VTSOaEz4+B58vqsnmw/fQI8WPQIXnFLKs5qCiDyP1YS0xbVMcm1TQaSoKHCzrhUUFfDE4ifoPq07k76axIaDGxgaN5QuzbqQ12Qj6fF/pNebvRj/2Xh2Ht/p/wCVUoDnNYWrgHhjTDGAiHwAbAQqHORO1T4ls675OynsPrGbsXPGsnr/au7uezdPDXmK9o3bl37+44/Qe+BRhj/7V+Zs+Tszf5rJExc/wTOJz+CQAD92rVQ9U53/4xq7va5FrdLKU4EYNnvLkS30/Wdfth3dxqwbZvHuyHfPSAgAPXpA86jmxG76Kzsf2snYnmOZunQqo2eO5lSe3s+glD95WlP4C7BRRJKwhq8YAjxuW1TKFv4eNjs9N50xs8YQHhLOqrtX0bFJ+Y8tOxwwdCgsXgytGpzDB6M/IKFNAg9/9TAD3x3I/LHzOa/pef4JWql6ztO7jz4GBgJzXcsgY8xMOwNTvufPEVKLTTG3f347O4/v5NNff1phQigxbBjs32/NryAiPJDwAItuW8ThrMP86v1fsevELvuDVkpVOR1nV9e6H3AO1nhG+4DWrm0qiPiz+ej55c8z7+d5vHzFywxpP6TK/S+7zFovWnR6W2KHRJJuTyK3MJfLZlxG2qk0m6JVSpWoqqbwO9f65XKWl2yMS9nAXzWF9QfW84dv/8AtvW7hoQsf8uiYjh2tJ5rdkwJAr5a9WHjrQo7nHOeyGZdxKPOQDRErpUpUmhSMMRNcL0cYYy51X7DuSFJBxF99CpMXT6ZpZFPevPpNpBoDGg0bZj3EVlh45vYBrQfwxS1fkHYqjas/upqcghzfBqyUKuXp3UcrPdymajF/1BQW7VrEol2L+MOQP9AwvHonGjbMSlzr15/92eBzBzPz+plsOLiBe/57D8aYs3dSStVYVX0KrUSkPxApIn1FpJ9rSQSi/BKh8plTp6yRSBs0sKf8YlPM5EWTad+oPfcPuL/axw8daq3LNiGVuPb8a3l26LN89ONHvLjyxRpEqpSqSFW3pF6JNTNaW+AVt+0ZwBM2xaRscuqUNeuaXdNwfrr5U9YfXM+M0TMIDwmv9vHNm0N8vJUUnnyy/H0ev/hxvj/0PZMXTaZni55c1VlbMZXypar6FD5w9R/cUaZPYaQxZq6fYlQ+kp5uX9NRQVEBf0j6A71a9OKWXrd4Xc6wYbByJWRnl/+5iDB95HT6tOrD+M/Gsy99X/k7KqW84ulzCnNE5GoR+T8RebpksTs45Vt2jpA6d+tcUo6n8OzQZ3E6nF6XM2wY5OfD8uUV7xMdFs0nN3xCflE+4+aOo7C4sOKdlVLV4umAeG8BNwEPYj3R/GugfaUHqVrHzhFS/7n+n3Ro3IFrulxTo3IuuQTCwuCbbyrfr3Ozzvzjqn+wbO8ynl36bI3OqZQ6zdPW5YuMMbcBJ4wxfwQGAe3sC0vZwa6ksP3YdpJSk7i33701HsAuKspKDF9+WfW+4/uM57Y+t/GnpX9iSarO0qOUL3j6f3Cua50tIq2BAkAnTwwydvUpvLP+HUIcIdzV9y6flDdiBGzeDPs86C6YdtU0OjXpxK2f3Up6brpPzq9UfeZpUviviDQGXgQ2AKnAx3YFpexhR59CXmEe73//PiPPH0mrBq18UuaIEdbak9pCg7AG/Pu6f3Mw4yCTvprkk/MrVZ9VmRRExAEsNsacNMbMwepL6GqM0Y7mIGNH89Fn2z7jaPZR7ut/n8/K7NYNzj3Xs6QAkNAmgccvfpwPvv+Az7d97rM4lKqPqkwKrol1XnZ7n2eM8aieLiLDReRnEUkRkbMm5BGRiSLyo4hsEpHlItK9WtErjxUVWZPs+Dop/HP9P4lrHMewjsN8VqaIVVtYtMi6E8kTT/3qKfq26suE/07gRP4Jn8WiVH3jafPR1yJyvVRjIBsRcQLTgBFAd2BsOV/6Hxljehlj4oG/cuYDcsqHMjKstS+Two5jO0hOTfZJB3NZI0ZYSayyW1PdhTnDmDFmBul56by641UdBkMpL3n6f/LvgE+BPBE5JSIZIlLVlFgJQIoxZpcxJh+YCYxy38EY415GNKD/J9vEjmGzP93yKWDdBeRrl10GoaGeNyEB9GzRkz9d+ieWHV1WGptSqno8mnnNGBPjRdltsOZeKJEGXFh2JxH5LVbSCQOGlleQiEwAJgC0bNmS5ORkL8KBzMxMr48NViXXvHt3NHAB+/ZtJjn5iE/KnrFhBt1iupGyIYUUUnxSprtevfowe3YYV1+91uNj+pv+dI7qzIR5EwjbH0bjsMZVH1QH1Off7frEL9dsjKlywepornJbmc9/Dbzr9n488EYl+98CfFBVLP379zfeSkpK8vrYYFVyzcuXGwPGLFzom3L3nNxjmIJ5ftnzvimwHC+9ZMW8d2/1jpu+YLoJnRpqbp59sz2B1UL1+Xe7PqnJNQPrjAff91WNkhohIk2B5iLSRESaupYOQOsq8k0aZz7g1hY4UMn+M4HRVZSpvOTr5qPPtn4GwJhuY3xTYDmqc2uqu7joOJ4a8hQzf5qpdyMpVU1V9SncB6wHurrWJcs8rE7kyqwFOotInIiEATcD8913EJHObm+vBnZ4HrqqDl/PpfDZts/o2aInXZp18U2B5ejWDdq3hwULqn/s5Isn06dlH+7/3/2czD3p++CUqqOqGiX1NWNMHPD/jDEdjTFxrqWPMebvVRxbCDwALAS2Ap8YYzaLyFQRGena7QER2Swim7D6FW6v+SWp8vgyKRzJOsKyvcsY09W+WgJYt6aOGmWNg5SVVb1jQ52h/GvkvzicdZjHvn7MngCVqoM87Wh+Q0QuAjq4H2OMmVHFcV8AX5TZ9rTba30E1U98ORXn/J/nU2yKua7bdTUvrAqjR8Prr8PXX8OYauag/q378+igR3lx5Yvc0usWLo271J4glapDPB0l9UPgJeBi4ALXMsDGuJSP+XLWtbnb5tKhcQf6tOxT88KqcMkl0KQJfO5l18CUxCl0atKJe/97r87trJQHPH1OYQAw2BjzG2PMg67lITsDU76VmWklBM8fPyzfqbxTLNq1iOu6Xkc1nmX0WkgIXHMN/Pe/UOjFtAlRoVG8c+077DyxkynJU3wen1J1jadJ4SfAN6OdqYDIzraGpa6pr3d+TX5RPqO7+u9GsdGj4cQJWLbMu+MvjbuUu/vezcurXmbDwQ2+DU6pOsbTpNAc2CIiC0VkfsliZ2DKt7KzITKy5uV8s/MbYsJiGNRuUM0L89CVV0JEBMyb530ZL17+IrHRsdwz/x6dqU2pSniaFKZgPUPwHNbgeCWLChI5Ob6pKSzavYhL4y4lxOHRPQo+ER0Nl19u9St4O6RRk8gm/H3E39n4y0ZeXfWqbwNUqg7xdI7mJVhzKIS6Xq/FmldBBQlf1BR2ndjFrhO7GBbnuxFRPTV6NOzZA99/730Z13W7jlHnj+Lp5KdJOe77YTmUqgs8vfvoXmA28E/XpjaAPioaRHxRU1i8azGAT4fJ9tQ111id5J995n0ZIsK0q6YR5gzjvgX36UiqSpXD0+aj3wKDgVMAxpgdQAu7glK+54uawqJ4URjhAAAgAElEQVTdi2gT04auzbv6JqhqaNHCuj11zpyaldOmYRteGPYC3+7+lvc2veeb4JSqQzxNCnnGGv4aABEJQYe5Dio1rSkUm2IW71rMsI7D/HIranluusmau/mnn2pWzoT+ExjSfgiPfv0oBzMO+iY4peoIT5PCEhF5AogUkcux5lb4r31hKV+r6S2pm37ZxLGcYwFpOipxww3gcMDMmTUrxyEO3rn2HXIKcnjwywd9E5xSdYSnSWEycAT4EWuQvC+AP9gVlPK9nJyaNR8t2rUIgMviLvNRRNXXogUMHWolhZp2B3Rp1oUpiVOYs3UOc7fO9U2AStUBniaFSGC6MebXxpgbgOmubSpI1LSmsGjXInrE9uCcmHN8F5QXbr4Zdu6EDT649+3RQY8S3yqe337xW07k6LzOSoHnSWExZyaBSGCR78NRdqlJTSG3MJdle5dxecfLfRuUF8aMsabprGkTElgjqU4fOZ0jWUf43de/q3mBStUBniaFCGNMZskb12sfPAql/KG4GHJzva8prNy3ktzC3ID2J5Ro2tR6wnnWLOu6aqrvOX2ZfPFk3t/0Pl/uqOZsPkrVQZ4mhSwR6VfyRkT6AzrkZJDIzbXW3tYUlu5ZikMcXHzuxb4LqgZuugn27YPvvvNNeU8NeYrusd2ZsGACp/JO+aZQpYKUp0nhYeBTEVkmIsuAWVgT6KggkJ1trb2tKSzfu5zeLXvTKMJHc3nW0MiR1lhIH3/sm/LCQ8KZPnI6BzIO6IQ8qt7zdJiLtVhTct4P/AboZoxZb2dgyndKkoI3NYXC4kK+S/uOwe0G+zaoGmjYEK691upXyM+ven9PXNj2Qn438He8veFtvtn5jW8KVSoIeVpTAGtind5AX2CsiNxmT0jK13JcDX3e1BS+/+V7sgqyak3TUYk774SjR72bv7kiUy+dStfmXblr/l2k56b7rmClgojOvFYP1KSmsHzvcoBalxSuuAJat4b3fDhSRWRoJB+M/oADGQd4ZOEjvitYqSDi6fjHA4DuRkcQC0o1qSks37ec9o3a07ZhW98GVUNOJ9x2G7z4IvzyC7Ty0RRQCW0SmDx4Ms8tf47rul3HNV2u8U3BSgUJnXmtHvC2pmCMYfne5bWullDizjuhqAg+/NC35T79q6fp1aIX9/73Xo5lH/Nt4UrVcrbOvCYiw0XkZxFJEZHJ5Xz+OxHZIiI/iMhiEWlf3QtQVfO2prD75G5+yfylVnUyu+vSBS66CKZPr/mwF+7CQ8L5cMyHHMs+pkNsq3rHtpnXRMQJTANGAN2xOqe7l9ltIzDAGNMba76Gv3ocufKYtzWF2tqf4O7OO2HbNli92rfl9mnVh2eHPsucrXOY8f0M3xauVC1WnZnXtgExrmWra1tlEoAUY8wu17DbM4FRZcpNMsa4vrL4DqhdDdd1hLc1heV7l9MovBE9WvTwfVA+cuONVrLzZYdziUcHPcqQ9kN48MsH2X1it+9PoFQt5FFHs4jcCLwIJAMCvCEijxljZldyWBtgn9v7NODCSva/Gyh3nAERmQBMAGjZsiXJycmehH2WzMxMr48NVpmZmWzatB3owoYNK0hNLfD42K+3fU3X6K4sXbLUvgB94Fe/Op8ZM1pw7bUradCgyKc/5/tb3c89afcw6v1RvBr/Kk5x+qRcX6uvv9t6zTYwxlS5AN8DLdzexwLfV3HMr4F33d6PB96oYN9bsWoK4VXF0r9/f+OtpKQkr48NVklJSeall4wBY06d8vy4o1lHDVMwzy19zr7gfGTdOuv6XnvNeu/rn/O/v/+3YQpmStIUn5brS/X1d7u+qck1A+uMB9/3nvYpOIwxh93eH6Pqpqc0oJ3b+7bAgbI7icgw4ElgpDEmz8N4VDV406ewct9KAAafWzs7md317w8DB8K0ab4ZJK+scb3HMb73eKYuncrSPbW71qRUTXmaFL5y3Xl0h4jcAfyPCpp63KwFOotInIiEATcDZ9yxJCJ9gX9iJYTD5ZShfCAnxxpuOsTTp1KAVWmrCHGEMKB1cDyj+MADsH07LLJpQPdpV02jY5OOjJs7Tm9TVXWapx3Nj2F9efcG+gBvG2P+r4pjCrEGzVsIbAU+McZsFpGpIjLStduLQAOswfY2eXKbq6o+bybYWb1/Nb1b9iYqNDhGSL/hBmtmtr//3Z7yY8JjmHn9TA5lHuLu+Xfrbaqqzqo0KYjIeSIyGMAYM9cY8ztjzCPAMRHpVFXhxpgvjDFdjDGdjDF/dm172hgz3/V6mDGmpTEm3rWMrLxE5Y3qTrBTVFzE2v1rubBNZfcF1C7h4XDffdZYSAcPRthyjv6t+/PXy//KvJ/n8drq12w5h1KBVlVN4W9ARjnbs12fqSBQ3ZrCtqPbyMjPCKqkAFZScDhg3rzWtp1j0oWTGN11NI9981hpv4tSdUlVSaGDMeaHshuNMeuADrZEpHwuO7t6NYXV+60nwS5sG1xJoU0buP56WLCgNek2DXIqIrw36j3aN2rPjZ/eyJGsI/acSKkAqSopVFYP93IeL+VvOTnVqyl8l/YdjSMa06VZF/uCssnvfw9ZWSG8+aZ952gc0ZjZN87maPZRbpl7C0XFRfadTCk/qyoprBWRe8tuFJG7AZ1kJ0h4U1NIaJOAQ6oz3Ubt0K8fDBhwnFdfPf0ktx3iW8Uz7appLNq1iCe/fdK+EynlZ1X9X/8wcKeIJIvIy65lCXAPMMn+8JQvVKemkJmfyU+Hfwq6/gR348bt5fBhe4a+cHd3v7uZ2H8iL6x4gZk/zbT3ZEr5SaVJwRhzyBhzEfBHINW1/NEYM8gY84v94SlfqE5NYf2B9RSb4qBOCn36nGTgQGuuhcJCe8/12ojXuPjci7lr3l1sPLjR3pMp5QeePqeQZIx5w7V8a3dQyreqU1Mo6WROaJNgY0T2EoHHH4fUVGseZzuFOcOY/evZNItqxuhZozmcpc9gquAWfI3GqtqqU1P4Lu07OjXpRGx0rL1B2eyaa6BHD3juOWsiHju1bNCSz276jCNZRxg9czS5hbn2nlApG2lSqAeqW1MItltRy+NwwDPPwNat8O9/23++Aa0H8OGYD1mVtoo7Pr+DYmPDIExK+YEmhXrA05pC2qk0DmQcCOr+BHc33GANlvf005Dnh6EWr+9+PS8Me4FZm2fxdNLT9p9QKRtoUqjjioogP9+zmsLqNNdDa3UkKYjA88/D3r3w1lv+OedjFz3GPX3v4c/L/sw769/xz0mV8iFNCnVcfr41KYwnNYU1+9cQ5gwjvlW8zVH5z7BhcNll8OyzkFHegC0+JiL84+p/MOK8EUz830Q+2/qZ/SdVyoc0KdRxeXnWj9ijmsL+1cS3iic8JNzmqPzrL3+Bo0fhlVf8c75QZyif/vpTEtokMHbOWJakVjVzrVK1hyaFOi431/oRV1VTKCouYt2BdSS0Dt5bUStywQVW/8KLL0Jamn/OGR0WzYKxC+jYpCMjZ45kw8EN/jmxUjWkSaGOy8uzmo+qqilsPbqVrIKsoH4+oTIvvmj1rzz6qP/O2SyqGQtvXUjjiMZc8eEV/HjoR/+dXCkvaVKo4zxtPlqzfw0QfCOjeqpDB3jiCfjkE/tmZytPu0bt+Pa2b4kIiWDYh8PYdnSb/06ulBc0KdRxJTWFqpqPVqetpnFEY85rep4fogqMxx6DTp3gwQetO7L8pVPTTiy+bTGCMPSDoWw/tt1/J1eqmjQp1HEe1xQOrOGC1hcE5cionoqIgDfegG3b4G9+niLq/Obns/i2xRQWFzLkvSH8dPgn/waglIfq7jeAAjzraM4uyObHQz/W2f4EdyNGwOjR8Mc/QkqKf8/do0UPltyxBIc4SHw/UQfQU7WSJoU6ruQ5hcpqChsPbqTIFNWZh9aq8ve/Q1gY3HGH/eMildUtthtL71xKdFg0l35wqU7pqWodW5OCiAwXkZ9FJEVEJpfz+RAR2SAihSJyg52x1Fee1BRKOpkvaHOBP0IKuDZtrMSwYgW8+qr/z39e0/NYesdSWkS34LIZlzH/5/n+D0KpCtiWFETECUwDRgDdgbEi0r3MbnuBO4CP7IqjvvOkT2H1/tWc2+hcWjVo5aeoAu+WW2DMGHjySdi82f/nb9+4PSvuWkGvFr0YM2sMb69/2/9BKFUOO2sKCUCKMWaXMSYfmAmMct/BGJNqjPkB0CElbeLJ3Udr9q+pN01HJUSs8ZAaNoTbbvPPgHllxUbHknR7EsPPG859C+7jicVP6OiqKuBCbCy7DbDP7X0a4NU3j4hMACYAtGzZkuTkZK8CyszM9PrYYJWRcQ4Aq1cn43Se/fnJ/JPsPrmbK5teWWf+barzc540qRlPPdWLG2/czyOP7LA3sAo80uoRHJkO/rL8Lyzbuownuj1BpLMak2pTP3+39ZptYoyxZQF+Dbzr9n488EYF+74P3OBJuf379zfeSkpK8vrYYDV27B4TFlbx5//b/j/DFMyS1CX+C8pm1f05P/aYMWDMjBn2xOOJ4uJi89p3rxnHHx2m95u9TeqJ1GodXx9/t/WaqwdYZzz4jrWz+SgNaOf2vi1wwMbzqXLk5Tkq7U/4Lu07nOKk/zn9/RdULfPcczBkCNx3H/wYoJEoRISHLnyIL8d9yZ6Te+j3dj++3PFlYIJR9ZqdSWEt0FlE4kQkDLgZ0Nss/Cwvz1Fpf8KKfSvo06oP0WHR/guqlgkJgVmzoFEjuO46OHYscLFc0ekK1k1YR9uGbbn6o6t5Oulpior9fN+sqtdsSwrGmELgAWAhsBX4xBizWUSmishIABG5QETSsJqa/ikiAbgPpG7Ly3NWWFMoLC5kddpqBrcb7N+gaqFWrWD2bNi3D0aNgtwATrN8XtPzWHX3Km6Pv50/Lf0Twz4cRtopPw3vquo9W59TMMZ8YYzpYozpZIz5s2vb08aY+a7Xa40xbY0x0caYZsaYHnbGUx9VVlP4/pfvySrI0qTgMngwzJhhPb8wfjwUB/BGoKjQKKaPnM70kdNZu38tvd/szZwtcwIXkKo39InmOi43t+Kawop9KwAYfK4mhRI33ggvv2zVGh59FKz7IAJDRLiz751svG8j5zU9jxs+vYHbPruN4znHAxeUqvM0KdRxldUUVuxbQbuG7WjbsK1/g6rlHnkEJk2yBs37wx8CmxgAOjfrzIq7VvDUkKf4+KeP6T6tu07zqWyjSaGOq+juI2MMK/au0FpCOUSsqTsnTLDuTHryycAnhlBnKFMvncrae9dyTsw5XPfJddzwyQ3sS99X9cFKVYMmhTouL89Zbk1hb/pe9mfs1/6ECjgc8Oab1m2qf/kLPP544BMDQHyreNbcs4Y/D/0zX+z4gm7TuvHXFX+loLgg0KGpOkKTQh1XUU2htD9Bk0KFHA74xz9g4kR44QW45x4oqAXfvaHOUJ645Am2/HYLl3W8jN8v+j13r7ubedvmlTwMqpTXNCnUcRUmhb0riA6NplfLXv4PKoiUJIannoLp0+HqqyE9PdBRWTo07sC8m+exYOwCRITRs0aT+EEiq9NWBzo0FcQ0KdRxFTUfrUxbycC2Awlx2Dn8Vd0gAlOnWkkhKQkuvhh27gx0VKdd3eVqpg+YzptXv8nWI1sZ+K+BXPvxtTqJj/KKJoU6rryaQkZeBj8c+kGbjqrpzjvhyy9h/37o1w/mzg10RKc5xcnEARPZ+dBOnr30WZbvXU6/t/sxeuZovkv7LtDhqSCiSaEOKyyEwsKzb0n9Lu07ik2x3nnkhWHDYONG6NoVrr8eHn44sE8/lxUTHsOTQ54kdVIqz/zqGZbuWcqgfw0i8f1E/rf9fzo0t6qSJoU6LCfHWpetKSzdsxSHOBjYdqD/g6oD2reHZcushPDaa9C3L6xaFeioztQoohFTEqew95G9vHLFK6QcT+Gaj6/h/L+fz2vfvUZ6bi3pGFG1jiaFOiw721qXrSks2LGAQW0H0TC8of+DqiPCwqypPBcutP6dBw+G3/0OMjICHdmZGoQ14JFBj7Br0i4+uu4jYqNieXjhw7R+pTV3zbuLlftW6h1L6gyaFOqw8moK+9L3semXTVzb5drABFXHXHGFNdz2ffdZSaJLF3j//cCOm1SeMGcYY3uNZeXdK1l771pu6XkLn2z+hMHTB9P9H915dumz7D6xO9BhqlpAk0IdVl5NYcH2BQBce74mBV9p2NB60G3VKqtp6c47ISHBqkXUxj/CB7QewDsj3+Hgowd559p3iI2K5amkp+j4ekcu+tdFvLLqFVJPpgY6TBUgmhTqsJKk4F5TWLBjAR2bdKRb826BCaoOGzgQVq6Ef/8bDh+G4cNh0CDrjqXamBxiwmO4p989LL1zKamTUnlu6HPkFObw6NePEvdaHP3f7s+U5CmsO7BOO6jrEU0KdVhJ81FJTSErP4vFuxZzbZdrEZHABVaHORwwbhzs2AFvvQUHD8JVV0GvXvDPf0JWVqAjLF/7xu15/JLH2XjfRlIeTOGFYS8QERLB1CVTueCdC2j9cmtunXsrM76fwYEMnUCxLtOkUIeVrSks2rWIvKI87U/wg/Bwq59hxw547z2rY3riRGjbFh54ANaurZ21B4BOTTvxf4P/jxV3reDQ/zvEjNEzuDTuUhbuXMjtn99Om1fa0OWNLtwz/x5mfD+DlOMp2lldh+jjrHVY2ZrCf7f/l4bhDbmk/SWBC6qeCQuDO+6A22+3Ju+ZNg3efddad+8ON91kTQHao4f15HRtExsdy/g+4xnfZzzFppgfDv3A4l2LWbJnCXO2zuFfG/9l7RcVy8C2A7mg9QUMaD2AAa0HEBsdG+DolTc0KdRh7jWFYlPMgu0LuLLTlYQ5wwIbWB1lig2FuYWnl7xCivKKKMovojCvkHMp4rl7i/j99UUsTSpiybdFfPpMMXOfKeKcFkX07llMt/OL6dihCKcUU1x49mKKDMVFZ742RYaD+w9y8r2T1vtic8aC4axtxrh9VuY1rj/6S167bwNoYpowmtGMYhTZ+dlk5GWQkZ9BRl4Gewr2sIc9zGEOoc5QosOiiQqNIiokiqjQKCJDI3E6nOX/A7olxdLmTeHMpk45/fnJ9JPsabzH+txtu0ev3cuW8vetaO3JPqX7lt1WwXk9Xed0yIFET38jvaNJoQ5zrymsO7COQ1mH6n3TkTGGgqwC8k7lWUuGtc7PyCcvw1rnZ7qWLGtdkFVAQXbB6XV2AQU51rowx0oABTkFFBdUrzO2t2sB4DDwLez5FvaUs684BYfTgSPEWkrel6zzC/PJicwp3SYOKf0icTgdiOP065IvmZJtZV+X/QJzOBxnbnNtB+shucbSuHRzoSkkIy+DU3mnrESRn8GRvCOY3NNZJSIkguhQV7JwJYpIZySRoZE4xHFWUnL/2VkvoLjYlRQLis/Y7tXryrZ5s48dx7rWHX/bsZLfKN/QpFCHudcU3vvuPRzi4KrOVwU2KB8wxpCfmU/OsRyyj2WTcyyHnOM55Jyw1ju/30n6jHRyT+SSe9K1pFvrvFN5mCLP2r9Do0MJiw47Yx0aFUqDVg0IjQolJDKEkMgQQiNdryPclnBr7Qx34gxzEhJ++nXpEmqtHaEOnKFO8gocrF7rIGmpkxWrHGz83kGRcVCM0KWTcMEFEB8PvXtbHdetWp1uckpOTiYxMdG+f/QaKCgqIOV4CpuPbGbLkS38fOxnNhzdwPZj28nIP/NpvxbRLWjfqD3nNjq3dFbANg3b0DqmNa1jWtOqQSsahDUAavc12yU5Odn2c2hSqMNKagpLD37BW+vf4oELHqBZVLPABlWO4sJiso9lk3U4i+wj2WQdcVsfzSb7SLa1dlsq+6vcEeEgvVk6kU0iiWgcQcN2DWnRqwXhjcKJaBRBeKNwwhu6LTHhhMWEWesGYYTFhBEaGWr99exn13aCa2+2XmdkWM8+rF1rLcnJ8J//nN63cWM4/3zrgTmnsz1paRAXZz0rcc454KyglcbfQp2hdIvtRrfYM2+DNsZwNPsoKcdT2HliJ3tO7mFP+h5ST6ay+chmvkr5iqyCs2/Xig6NpmWDlkQURtDpYCdio2JpHtWcZlHNaBbZjKaRTWka2ZQmkU1oEtGERhGNaBDWwKqFqCrZmhREZDjwGuAE3jXGPF/m83BgBtAfOAbcZIxJtTOm+iQ7G2hwkIlf3UGvFr148YoX/XLeki/5ki/z0i/5w1nW68PWtqzD1pJzPOeMZoJSApFNI4mOjSaqeRRNOjahzYVtiGoeRVSzKCKbRVrrppGlS0STCJavWl4n/oKMibGemL7iitPbjh2znqD+4QfYtg1+/hm+/Rb274/j/fdP7+d0QuvW0KaNVaNo1QpatoTY2NNL06bQpIm1NGjg/45uESE2OpbY6FgGtRt01ufGGE7mnuRg5kEOZBxg/6n9HMo6xC+Zv3Ao6xDb07azN30v6w+u52j2UfKL8is+F0LD8IY0imhEw/CGxITFEBMeQ0xYDA3CGhAdGk10WHTpurRpKySytIkrIiSCyBBrHRESQXhIOBEhEYQ5wwh3hhPmDKsTt3rblhRExAlMAy4H0oC1IjLfGLPFbbe7gRPGmPNE5GbgBeAmu2Kqb7JzipHrxpOZn8nMG2YSERJR5THGWJ2lJW3o+Zmn29rzTuWRm55LXnoeOSdyyD2RazXblCzHcsg+mk3uyYqHDY1sGkl0y2iiY6OJ7R5Lh8QORLeIJio2iujY6DNeRzaNxBGif925a9YMEhOtxd3ChUtp334Iu3fD3r2wb5+1HDgAKSnWAH7HjlVcrsMBjRpZS0yMlSRiYiA6+vQSFWX1T0VFQUTE6SU8/PQSFmYtoaGn1yVLSMjpxek8vXZfHI7Ta4dDrL/2I5vQPbb7WTG7Nx8ZY8gqyOJY9jGO5xznRO4Jjucc52TuSdJz0zmRe6K0nyM9N710vf/UfjLyM8jKzyKrIIvcwpoNeRvqCCXMGVa6hDpDCXWElrsOcYSctTjFaa0dztOvxYlDHDgdTroVdiPR5p5mO2sKCUCKMWYXgIjMBEYB7klhFDDF9Xo28HcREWPDTc8P3vI6ZmEqs5nv66JrLYPhN46unLPsUpL+k0SSSSq9+6TkrpXiwmKKCoooyisqvVvGIwIRjSKIaBJR+td6k45NiGru+gu+ufXFXvIFHxVr/XWvX/L2CA8vpmtXa0jvihQWWonhyBE4ehROnIDjx60lPf30kpFhLSdPWnNHZGVZS06OVfv057hOVnI4cxGx1sXFFxMSUrJNEGmAw9EAkfaIULoAZ7x331aigUCMgJEiCMnBhGRTHJJ1+rUzB0JyMc5cjDMH48yzFkculL7OA2cBxpGPceSR6yggx5GPceSDoxDjKMBIATgKMI5C1+s8jGRZn0sRSCFGXK8dRRgpBCkCKcZIEVeHncuk0fb+m9uZFNoA+9zepwEXVrSPMaZQRNKBZsBR951EZAIwAaBly5ZedbbkkU9246I6Ub2rjihpROM2jRHX7SLidN2V4nC9di2OMAeOUNcS4cAZ7sQR7sAZ6bSWKCch0SGENAjBGe0kJCoEcVb8b5nt+g+wfppHK9zV5zIzM/3SIVebeHPNJU1HnTp5fowxUFgo5Oc7SpeCAmvJzxeKihwUFAgFBQ4KC4XCQmtbUZFQVGS9Ly6W0vdFRVBcbG0zBoqKTr92317yGVC6LT+/gJCQMNfnp/dzf3067rO3lf2s5PpOi3QtTTFG3PYHiq3Fkz9f3Y8t+29ZXQOG7rL9d9vOpFDev0TZfwZP9sEY8zbwNsCAAQOMN+3FiYmJ9fZuBb3muk+vuX5ITt5i+zXbWZdPA9q5vW8LlB00pXQfEQkBGgHHbYxJKaVUJexMCmuBziISJyJhwM1wVoP+fOB21+sbgG/t6E9QSinlGduaj1x9BA8AC7FuSZ1ujNksIlOBdcaY+cC/gA9FJAWrhnCzXfEopZSqmq3PKRhjvgC+KLPtabfXucCv7YxBKaWU5/T+QKWUUqU0KSillCqlSUEppVQpTQpKKaVKSbDdASoiRyh/yHlPNMevz9bWCnrN9YNec/1Qk2tub4ypcjq8oEsKNSEi64wxAwIdhz/pNdcPes31gz+uWZuPlFJKldKkoJRSqlR9SwpvBzqAANBrrh/0musH26+5XvUpKKWUqlx9qykopZSqhCYFpZRSpepNUhCR4SLys4ikiMjkQMdjNxFpJyJJIrJVRDaLyKRAx+QPIuIUkY0isiDQsfiDiDQWkdkiss31sx4U6JjsJiKPuH6nfxKRj0Wk6snHg4yITBeRwyLyk9u2piLyjYjscK2b2HHuepEURMQJTANGAN2BsSJy9kzgdUsh8KgxphswEPhtPbhmgEnA1kAH4UevAV8ZY7oCfajj1y4ibYCHgAHGmJ5Yw/LXxSH33weGl9k2GVhsjOkMLHa997l6kRSABCDFGLPLGJMPzARGBTgmWxljDhpjNrheZ2B9WbQJbFT2EpG2wNXAu4GOxR9EpCEwBGteEowx+caYk4GNyi9CgEjXbI1RnD2jY9Azxizl7FkoRwEfuF5/AIy249z1JSm0Afa5vU+jjn9BuhORDkBfYHVgI7Hd34D/w5pWvT7oCBwB3nM1mb0rItGBDspOxpj9wEvAXuAgkG6M+TqwUflNS2PMQbD+6ANa2HGS+pIUpJxt9eJeXBFpAMwBHjbGnAp0PHYRkWuAw8aY9YGOxY9CgH7Am8aYvkAWNjUp1BaudvRRQBzQGogWkVsDG1XdUl+SQhrQzu19W+pglbMsEQnFSgj/McbMDXQ8NhsMjBSRVKzmwaEi8u/AhmS7NCDNGFNSA5yNlSTqsmHAbmPMEWNMATAXuCjAMfnLIRE5B8C1PmzHSepLUlgLdBaROBEJw+qYmh/gmGwlIoLV1rzVGPNKoOOxmzHmcWNMW2NMB6yf77fGmDr9F6Qx5hdgn4ic79p0GbAlgCH5w15goIhEuX7HL6OOd667meHviSoAAAJOSURBVA/c7np9OzDPjpPYOkdzbWGMKRSRB4CFWHcrTDfGbA5wWHYbDIwHfhSRTa5tT7jmzVZ1x4PAf1x/7OwC7gxwPLYyxqwWkdnABqw77DZSB4e7EJGPgUSguYikAc8AzwOfiMjdWMnRlvntdZgLpZRSpepL85FSSikPaFJQSilVSpOCUkqpUpoUlFJKldKkoJRSqpQmBaWUUqU0Kah6S0Saicgm1/KLiOx3e7/ShvPdISJHRKTCAftEJNJ1/nwRae7rGJSqSr14eE2p8hhjjgHxACIyBcg0xrxk82lnGWMeqCSmHCDeNVyHUn6nNQWlyiEima51oogsEZFPRGS7iDwvIuNEZI2I/CginVz7xYrIHBFZ61oGe3COHq5yNonIDyLS2e7rUqoqWlNQqmp9gG5Y49vvAt41xiS4ZrN7EHgYa7KbV40xy0XkXKwhVbpVUe5E4DVjTMkwFU7brkApD2lSUKpqa0vGsReRnUDJ+P0/Ape6Xg8DultjtAHQUERiXBMcVWQV8KRrcqC5xpgdvg9dqerR5iOlqpbn9rrY7X0xp/+wcgCDjDHxrqVNFQkBY8xHwEggB1goIkN9HLdS1aZJQSnf+Boo7UAWkfiqDhCRjsAuY8zrWMMi97YvPKU8o0lBKd94CBjg6jDegtVfUJWbgJ9cQ5t3BWbYGaBSntChs5XyExG5AxhQ2S2pbvumuvY9andcSrnTmoJS/pMDjPDk4TUgFKvPQim/0pqCUkqpUlpTUEopVUqTglJKqVKaFJRSSpXSpKCUUqrU/wdSXA4hFigsJAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "test = pk2Comp(4,.2,.2,.7,1/60)\n", + "test.main()\n", + "test.getPlot()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/pk_optimizer/pkOptimizer.ipynb b/pk_optimizer/pkOptimizer.ipynb new file mode 100644 index 0000000..fa5b9f9 --- /dev/null +++ b/pk_optimizer/pkOptimizer.ipynb @@ -0,0 +1,175 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.stats import gamma\n", + "from scipy.integrate import odeint \n", + "from scipy.optimize import minimize\n", + "from scipy.optimize import curve_fit\n", + "\n", + "import os\n", + "import csv\n", + "import re\n", + "import math as math\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "#%matplotlib inline\n", + "\n", + "class pkOptimizer:\n", + " \"\"\"The pkOptimizer object is an optimizer for parameters in pk models.\"\"\"\n", + " \n", + " def __init__ (self, wd, Flow = 1/60, Vp = 0.05, Visf = 0.15, PS = 1/60):\n", + " \"\"\"Initializes the model with initial guess parameter values for flow, Vp, Visf, and PS.\n", + " Parameters\n", + " ---------- \n", + " Flow : double\n", + " Flow is the flow of plasma through the blood vessel in mL/(mL*min). Defaults to 1/60.\n", + " \n", + " Vp : double\n", + " Vp is the volume of plasma in mL. Defaults to 0.05.\n", + " \n", + " Visf : double\n", + " Visf is the volume of interstitial fluid in mL. Defaults to 0.15.\n", + " \n", + " PS : double\n", + " PS is the permeability-surface area constant in mL/(g*min). Defaults to 1/60. \n", + " \"\"\"\n", + " \n", + " def getData(self, wd):\n", + " \"\"\"Imports data from all .csv files in directory.\n", + " Parameters\n", + " ---------- \n", + " wd : str\n", + " wd is the working directory path\n", + " \n", + " Attributes\n", + " ----------\n", + " t : double[]\n", + " list of all timepoints\n", + " aorta : double[]\n", + " concentration of tracer in aorta (input function)\n", + " myo : double[]\n", + " concentration of tracer in myocardial tissue (Cisf)\n", + " \n", + " Returns\n", + " -------\n", + " t : double[]\n", + " list of all timepoints\n", + " aorta : double[]\n", + " concentration of tracer in aorta (input function)\n", + " myo : double[]\n", + " concentration of tracer in myocardial tissue (Cisf)\n", + " \"\"\"\n", + " \n", + " os.chdir(wd)\n", + " #os.chdir(r\"C:\\Users\\Ethan\\OneDrive - Michigan State University\\MSU\\Classwork\\Computational Modeling\\Models\\Data\")\n", + " #create directory of all csv files,\n", + " data = list(csv.reader(open('CTPERF005_stress.csv'), delimiter = '\\t'))\n", + "\n", + " t = []\n", + " aorta = []\n", + " myo = []\n", + " \n", + " for i in range(12):\n", + " t.append(float(re.compile('\\d+[.]+\\d+|\\d+').findall(data[i+1][0])[0]))\n", + " aorta.append(float(re.compile('\\d+[.]+\\d+|\\d+').findall(data[i+1][1])[0]))\n", + " myo.append(float(re.compile('\\d+[.]+\\d+|\\d+').findall(data[i+1][2])[0]))\n", + "\n", + " return t, aorta, myo\n", + "\n", + " def gammaFunc(self, time, a, l, s):\n", + " \"\"\"Creates a gamma variate probability density function with given alpha, location, and scale values.\n", + " Parameters\n", + " ---------- \n", + " time : double[]\n", + " array of timepoints\n", + " a : double\n", + " alpha value of gamma PDF\n", + " l : double\n", + " location of 50th percentile of function\n", + " s : double\n", + " scale parameter \n", + " \n", + " Returns\n", + " -------\n", + " rv.pdf(time)\n", + " probability density function of your gamma variate.\n", + " \"\"\"\n", + " rv = gamma(a, loc = l, scale = s) #input function\n", + " return rv.pdf(time)\n", + " \n", + " def curveFit(self, t, aorta, myo, model):\n", + " \"\"\"Takes in data and fits gamma curve to aorta and Cisf from model to myo. Returns parameters for best fit.\n", + " \n", + " Parameters\n", + " ---------- \n", + " t : double[]\n", + " list of all timepoints\n", + " aorta : double[]\n", + " concentration of tracer in aorta (input function)\n", + " myo : double[]\n", + " concentration of tracer in myocardial tissue (Cisf)\n", + " model : pkModel object\n", + " a pk model, either 1Comp or 2Comp\n", + " \n", + " Returns\n", + " -------\n", + " Flow : double\n", + " Flow is the flow of plasma through the blood vessel in mL/(mL*min).\n", + " \n", + " Vp : double\n", + " Vp is the volume of plasma in mL.\n", + " \n", + " Visf : double\n", + " Visf is the volume of interstitial fluid in mL.\n", + " \n", + " PS : double\n", + " PS is the permeability-surface area constant in mL/(g*min).\n", + " \"\"\"\n", + " \n", + " def getPlot(self):\n", + " \"\"\"Plots the original data to the fitted curve.\"\"\"\n", + " plt.plot(t, aorta, 'bo', label='data')\n", + " #plt.plot(t, y, 'b-', label='data')\n", + " popt, pcov = curve_fit(gammaFunc, t, aorta, p0 = [2, 8, 10000], method = 'trf')\n", + "\n", + " print(f'alpha = {popt[0]}, loc = {popt[1]}, scale = {popt[2]}')\n", + "\n", + " plt.plot(t, gammaFunc(t, *popt), 'r-', label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt))\n", + " plt.plot(time, gammaFunc(time, .1313, 8.533, 10000), 'b-')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} -- GitLab