From 0985987d72c24645f45816bb0f7cade952092050 Mon Sep 17 00:00:00 2001 From: Ryan McGee Date: Thu, 27 Feb 2020 16:33:46 -0800 Subject: [PATCH] Implementation of B-lactamase dynamics. Includes significant reorganization and reimplementation of standard objects/classes. --- pycrobe/Untitled.ipynb | 32 --- pycrobe/culture_dynamics.py | 536 ----------------------------------- pycrobe/culture_dynamics.pyc | Bin 11293 -> 0 bytes 3 files changed, 568 deletions(-) delete mode 100644 pycrobe/Untitled.ipynb delete mode 100644 pycrobe/culture_dynamics.py delete mode 100644 pycrobe/culture_dynamics.pyc diff --git a/pycrobe/Untitled.ipynb b/pycrobe/Untitled.ipynb deleted file mode 100644 index 3091d0f..0000000 --- a/pycrobe/Untitled.ipynb +++ /dev/null @@ -1,32 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.13" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/pycrobe/culture_dynamics.py b/pycrobe/culture_dynamics.py deleted file mode 100644 index fe208e3..0000000 --- a/pycrobe/culture_dynamics.py +++ /dev/null @@ -1,536 +0,0 @@ -from __future__ import division - -import numpy -import pandas -import scipy.stats - -# from standard import * -# from betalactamase import * - - -################################################## -################################################## - - -class CultureDynamics(object): - - def __init__(self): - - self.data = pandas.DataFrame(columns=['run', 'strain', 't', 't_run', 'N', 'r', 'S']) - - self.num_runs = 0 - - - #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - def run(self, time, dt, inoculums, media, temp, noise_r=0.02, downsample_output_dt=None): - - I = len(inoculums) - - T = int(time/dt) - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # LOAD PARAMETER VALUES: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - rho = numpy.zeros(I) - k_r = numpy.zeros(I) - sigma = numpy.zeros(I) - - optimalTemp = numpy.zeros(I) - meanLagExitTime = numpy.zeros(I) - stdevLagExitTime = numpy.zeros(I) - - #-------------------------------------------------- - - for i, inoculum in enumerate(inoculums): - - rho[i] = inoculum.strain.maxGrowthRate - k_r[i] = inoculum.strain.halfmaxGrowthNutrientConc - sigma[i] = inoculum.strain.nutrientConsumptionRate - - optimalTemp[i] = inoculum.strain.optimalTemp - meanLagExitTime[i] = inoculum.strain.meanLagExitTime - stdevLagExitTime[i] = inoculum.strain.stdevLagExitTime - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # LOAD INITIAL VARIABLE VALUES: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - N = numpy.zeros(shape=(T+1,I)) - r = numpy.zeros(shape=(T+1,I)) - - growthPhase = [''] * I - growthCycleTimer = numpy.zeros(I) - - S = numpy.zeros(shape=(T+1,1)) - - #-------------------------------------------------- - - for i, inoculum in enumerate(inoculums): - - N[0][i] = inoculum.cellCount - r[0][i] = inoculum.growthRate - - growthPhase[i] = inoculum.growthPhase - growthCycleTimer[i] = inoculum.growthCycleTimer - - #-------------------------------------------------- - - S[0] = media.nutrient.concentration * media.volume - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # RUN GROWTH MODEL: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - for t in range(T): - - dN = r[t]*N[t] - N[t+1] = N[t] + dN*dt - - dS = -sigma * numpy.sum(r[t]*N[t]) - S[t+1] = max(S[t] + dS*dt, 0) - - # Modulate the effective max growth rate by growth phase and temperature: - r_max = numpy.zeros(I) - for i in range(I): - r_max[i] = rho[i] # intrinsic max exponential growth rate - r_max[i] *= scipy.stats.norm.cdf(growthCycleTimer[i], loc=meanLagExitTime[i], scale=stdevLagExitTime[i]) # modeling lag phase exit (cells exit lag phase (r=0) and enter exponential phase (r=maxrate*otherfactors) at lag-exit-times that are normally distributed from cell to cell; this is approximated by the population's average growth rate following the normal distn cdf centered at the mean lag exit time - r_max[i] *= temp/optimalTemp[i] # growth rate dependence on temperature - # Apply additional random noise: - r_max[i] = numpy.random.normal(r_max[i], r_max[i]*noise_r) - # Calculate the current growth rate as a function of nutrient availability: - r[t+1] = r_max * ( S[t] / (k_r + S[t]) ) - # Update the current growth cycle phase: - for i in range(I): - growthCycleTimer[i] += dt - if( (S[t] / (k_r + S[t])) <= 0.01 ): - growthPhase[i] = 'stationary' - elif( growthPhase[i] == 'stationary' and (S[t] / (k_r + S[t])) > 0.01 ): - growthPhase[i] = 'lag' - growthCycleTimer[i] = 0.0 - elif( growthPhase[i] == 'lag' and (rho[i]-r[i])/rho[i] < 0.5 ): - growthPhase[i] = 'exponential' - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # SAVE OUT FINAL VARIABLE VALUES: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - for i, inoculum in enumerate(inoculums): - - inoculum.cellCount = N[-1][i] - inoculum.growthRate = r[-1][i] - - inoculum.growthPhase = growthPhase[i] - inoculum.growthCycleTimer = growthCycleTimer[i] - - media.nutrient.concentration = S[-1] / media.volume - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # UPDATE THE DATAFRAME HOLDING THE VARIABLE TIME SERIES: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - for i, strainName in enumerate([inoculum.strain.name for inoculum in inoculums]): - t_vals = numpy.arange(start=0, stop=time+dt, step=dt) - t_vals_macro = t_vals if self.num_runs==0 else t_vals+self.data['t'].max() # in terms of an overall timer, assume that each run starts immediately where the previous run left off - run_timeseries = { - 'run': [self.num_runs]*(T+1), - 'strain': [strainName]*(T+1), - 't': t_vals_macro, - 't_run': t_vals, - 'N': N[:,i], - 'r': r[:,i], - 'S': S[:,i] - } - - strain_run_data = pandas.DataFrame.from_dict(run_timeseries) - - self.data = self.data.append(strain_run_data) - self.data = self.data.drop_duplicates(subset=['t', 'strain'], keep='first') - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - self.num_runs += 1 - - return - - - #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - def figure(self): - import matplotlib.pyplot as pyplot - import seaborn - - fig = pyplot.figure(figsize=(16,9)) - ax1 = pyplot.subplot(221) - ax2 = pyplot.subplot(223) - ax3 = pyplot.subplot(222) - - seaborn.lineplot(data=self.data, ax=ax1, x="t", y="N", hue="strain") - - seaborn.lineplot(data=self.data, ax=ax2, x="t", y="r", hue="strain") - - seaborn.lineplot(data=self.data, ax=ax3, x="t", y="S") - - seaborn.set_style("ticks") - seaborn.despine() - - fig.tight_layout() - - pyplot.show() - - -################################################### -################################################### - - -class BetaLactamaseDynamics(CultureDynamics): - - def __init__(self): - super(BetaLactamaseDynamics, self).__init__() - return - - - def run(self, time, dt, inoculums, media, temp, noise_r=0.02, downsample_output_dt=None): - - I = len(inoculums) - - T = int(time/dt) - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # LOAD PARAMETER VALUES: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - rho = numpy.zeros(I) - k_r = numpy.zeros(I) - lamda = numpy.zeros(I) - k_l = numpy.zeros(I) - eta = numpy.zeros(I) - alpha_B = numpy.zeros(I) - delta_B = numpy.zeros(I) - delta_Bext = numpy.zeros(I) - epsilon_B = numpy.zeros(I) - xi_B = numpy.zeros(I) - delta_A = numpy.zeros(I) - epsilon_A = numpy.zeros(I) - xi_A = numpy.zeros(I) - Vmax_B = numpy.zeros(I) - k_B = numpy.zeros(I) - Vmax_Bext = numpy.zeros(I) - k_Bext = numpy.zeros(I) - Vol = numpy.zeros(I) - sigma = numpy.zeros(I) - - optimalTemp = numpy.zeros(I) - meanLagExitTime = numpy.zeros(I) - stdevLagExitTime = numpy.zeros(I) - - #-------------------------------------------------- - - for i, inoculum in enumerate(inoculums): - - rho[i] = inoculum.strain.maxGrowthRate - k_r[i] = inoculum.strain.halfmaxGrowthNutrientConc - lamda[i] = inoculum.strain.maxLysisRate - k_l[i] = inoculum.strain.halfmaxLysisDrugConc - eta[i] = inoculum.strain.lysisHillCoefficient - alpha_B[i] = inoculum.strain.betalactamaseProductionRate - delta_B[i] = inoculum.strain.betalactamase.decayRate_intracellular - delta_Bext[i] = inoculum.strain.betalactamase.decayRate_extracellular - epsilon_B[i] = inoculum.strain.betalactamaseLeakRate - xi_B[i] = inoculum.strain.betalactamaseDebrisSinkFraction - epsilon_A[i] = inoculum.strain.betalactamDiffusionRate - xi_A[i] = inoculum.strain.betalactamDebrisSinkFraction - Vmax_B[i] = inoculum.strain.betalactamase.maxHydrolysisRate - k_B[i] = inoculum.strain.betalactamase.halfmaxHydrolysisDrugConc - Vmax_Bext[i] = inoculum.strain.betalactamase.maxHydrolysisRate - k_Bext[i] = inoculum.strain.betalactamase.halfmaxHydrolysisDrugConc - Vol[i] = inoculum.strain.periplasmVolume - sigma[i] = inoculum.strain.nutrientConsumptionRate - - optimalTemp[i] = inoculum.strain.optimalTemp - meanLagExitTime[i] = inoculum.strain.meanLagExitTime - stdevLagExitTime[i] = inoculum.strain.stdevLagExitTime - - #-------------------------------------------------- - - for drug in media.drugs: - if(isinstance(drug, BetaLactam)): - delta_A = drug.decayRate - # Assume Media objects hold only one drug object per type (BetaLactam) - break - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # LOAD INITIAL VARIABLE VALUES: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - N = numpy.zeros(shape=(T+1,I)) - r = numpy.zeros(shape=(T+1,I)) - l = numpy.zeros(shape=(T+1,I)) - B = numpy.zeros(shape=(T+1,I)) - Bext = numpy.zeros(shape=(T+1,I)) - d_B = numpy.zeros(shape=(T+1,I)) - d_Bext = numpy.zeros(shape=(T+1,I)) - A = numpy.zeros(shape=(T+1,I)) - Aext = numpy.zeros(shape=(T+1,1)) - S = numpy.zeros(shape=(T+1,1)) - - growthPhase = [''] * I - growthCycleTimer = numpy.zeros(I) - - #-------------------------------------------------- - - for i, inoculum in enumerate(inoculums): - - N[0][i] = inoculum.cellCount - r[0][i] = inoculum.growthRate - l[0][i] = inoculum.lysisRate - - growthPhase[i] = inoculum.growthPhase - growthCycleTimer[i] = inoculum.growthCycleTimer - - for solute in inoculum.periplasm.solutes: - if(isinstance(solute, BetaLactamase)): - B[0][i] = solute.concentration - d_B[0][i] = solute.hydrolysisRate - - # Find the corresponding extracellular BetaLactamase: - matchingBlaFound = False - for solute_ext in media.solutes: - if(isinstance(solute_ext, BetaLactamase) and solute == solute_ext): - Bext[0][i] = solute_ext.concentration - d_Bext[0][i] = solute_ext.hydrolysisRate - matchingBlaFound = True - break - if(not matchingBlaFound): - # Make new object for Bla type that doesn't currently exist in extracellular media: - betalactamase_ext = BetaLactamase(name=solute.name) - betalactamase_ext.__dict__.update(solute.__dict__) - betalactamase_ext.concentration = 0.0 - media.solutes.append(betalactamase_ext) - - # Assume Media objects hold only one solute object per type (BetaLactamase) - break - - A[0][i] = 0.0 - for drug in inoculum.periplasm.drugs: - if(isinstance(drug, BetaLactam)): - A[0][i] = drug.concentration - # Assume Media objects hold only one drug object per type (BetaLactam) - break - - #-------------------------------------------------- - - Aext[0] = 0.0 - for drug in media.drugs: - if(isinstance(drug, BetaLactam)): - Aext[0] = drug.concentration - # Assume Media objects hold only one drug object per type (BetaLactam) - break - - S[0] = media.nutrient.concentration * media.volume - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # RUN GROWTH MODEL: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - for t in range(T): - - dN = (r[t] - l[t])*N[t] - N[t+1] = N[t] + dN*dt - - dB = -delta_B*B[t] - r[t]*B[t] - epsilon_B*(B[t] - Bext[t]) + alpha_B - B[t+1] = B[t] + dB*dt - - dBext = -delta_Bext*Bext[t] + numpy.sum(epsilon_B*(B[t] - Bext[t])) + l[t]*xi_B*Vol*B[t]*N[t] - Bext[t+1] = Bext[t] + dBext*dt - - dA = -delta_A*A[t] + epsilon_A*(Aext[t] - A[t]) - d_B[t]*B[t] - A[t+1] = A[t] + dA*dt - - dAext = -delta_A*Aext[t] - numpy.sum(epsilon_A*(Aext[t] - A[t])) - numpy.sum(d_Bext*Bext[t]) + l[t]*xi_A*Vol*A[t]*N[t] - Aext[t+1] = Aext[t] + dAext*dt - - dS = -sigma * numpy.sum(r[t]*N[t]) - S[t+1] = S[t] + dS*dt - - # Modulate the effective max growth rate by growth phase and temperature: - r_max = numpy.zeros(I) - for i in range(I): - r_max[i] = rho[i] # intrinsic max exponential growth rate - r_max[i] *= scipy.stats.norm.cdf(growthCycleTimer[i], loc=meanLagExitTime[i], scale=stdevLagExitTime[i]) # modeling lag phase exit (cells exit lag phase (r=0) and enter exponential phase (r=maxrate*otherfactors) at lag-exit-times that are normally distributed from cell to cell; this is approximated by the population's average growth rate following the normal distn cdf centered at the mean lag exit time - r_max[i] *= temp/optimalTemp[i] # growth rate dependence on temperature - # Apply additional random noise: - r_max[i] = numpy.random.normal(r_max[i], r_max[i]*noise_r) - # Calculate the current growth rate as a function of nutrient availability: - r[t+1] = r_max * ( S[t] / (k_r + S[t]) ) - # Update the current growth cycle phase: - for i in range(I): - growthCycleTimer[i] += dt - if( (S[t] / (k_r + S[t])) <= 0.01 ): - growthPhase[i] = 'stationary' - elif( growthPhase[i] == 'stationary' and (S[t] / (k_r + S[t])) > 0.01 ): - growthPhase[i] = 'lag' - growthCycleTimer[i] = 0.0 - elif( growthPhase[i] == 'lag' and (rho[i]-r[i])/rho[i] < 0.5 ): - growthPhase[i] = 'exponential' - - l[t+1] = lamda * ( A[t]**eta / (k_l**eta + A[t]**eta) ) * r[t] - - d_B[t+1] = (Vmax_B * A[t])/(k_B + A[t]) - - d_Bext[t+1] = (Vmax_Bext * Aext[t])/(k_Bext + Aext[t]) - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # SAVE OUT FINAL VARIABLE VALUES: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - for i, inoculum in enumerate(inoculums): - - inoculum.cellCount = N[-1][i] - inoculum.growthRate = r[-1][i] - inoculum.lysisRate = l[-1][i] - - for solute in inoculum.periplasm.solutes: - if(isinstance(solute, BetaLactamase)): - solute.concentration = B[-1][i] - solute.hydrolysisRate = d_B[-1][i] - - # Find the corresponding extracellular BetaLactamase: - for solute_ext in media.solutes: - if(isinstance(solute_ext, BetaLactamase) and solute == solute_ext): - solute_ext.concentration = Bext[-1][i] - solute_ext.hydrolysisRate = d_Bext[-1][i] - break - - # Assume Media objects hold only one solute object per type (BetaLactamase) - break - - for drug in inoculum.periplasm.drugs: - if(isinstance(drug, BetaLactam)): - drug.concentration = A[-1][i] - # Assume Media objects hold only one drug object per type (BetaLactam) - break - - #-------------------------------------------------- - - for drug in media.drugs: - if(isinstance(drug, BetaLactam)): - drug.concentration = Aext[-1] - # Assume Media objects hold only one drug object per type (BetaLactam) - break - - media.nutrient.concentration = S[-1] / media.volume - - - for i, strainName in enumerate([inoculum.strain.name for inoculum in inoculums]): - t_vals = numpy.arange(start=0, stop=time+dt, step=dt) - t_vals_macro = t_vals if self.num_runs==0 else t_vals+self.data['t'].max() # in terms of an overall timer, assume that each run starts immediately where the previous run left off - run_timeseries = { - 'run': [self.num_runs]*(T+1), - 'strain': [strainName]*(T+1), - 't': t_vals_macro, - 't_run': t_vals, - 'N': N[:,i], - 'r': r[:,i], - 'l': l[:,i], - 'B': B[:,i], - 'Bext': Bext[:,i], - 'd_B': d_B[:,i], - 'd_Bext': d_Bext[:,i], - 'A': A[:,i], - 'Aext': Aext[:,i], - 'S': S[:,i] - } - - - - - strain_run_data = pandas.DataFrame.from_dict(run_timeseries) - - self.data = self.data.append(strain_run_data) - self.data = self.data.drop_duplicates(subset=['t', 'strain'], keep='first') - - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - self.num_runs += 1 - - return - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # UPDATE THE DATAFRAME HOLDING THE VARIABLE TIME SERIES: - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # TODO: make this return a DataFrame instead - - output_dt = downsample_output_dt if downsample_output_dt is not None else dt - - timeseries = { - 'N': N[::output_dt], - 'r': r[::output_dt], - 'l': l[::output_dt], - 'B': B[::output_dt], - 'Bext': Bext[::output_dt], - 'd_B': d_B[::output_dt], - 'd_Bext': d_Bext[::output_dt], - 'A': A[::output_dt], - 'Aext': Aext[::output_dt], - 'S': S[::output_dt] - } - - #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~s - - self.num_runs += 1 - - return - - - #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - def figure(self): - import matplotlib.pyplot as pyplot - import seaborn - - fig = pyplot.figure(figsize=(16,9)) - ax1 = pyplot.subplot(221) - ax2 = pyplot.subplot(223) - ax3 = pyplot.subplot(222) - - seaborn.lineplot(data=self.data, ax=ax1, x="t", y="N", hue="strain") - - seaborn.lineplot(data=self.data, ax=ax2, x="t", y="r", hue="strain") - seaborn.lineplot(data=self.data, ax=ax2, x="t", y="l", hue="strain") - - seaborn.lineplot(data=self.data, ax=ax3, x="t", y="S") - - seaborn.set_style("ticks") - seaborn.despine() - - fig.tight_layout() - - pyplot.show() - - - - - - - - - - - - - - diff --git a/pycrobe/culture_dynamics.pyc b/pycrobe/culture_dynamics.pyc deleted file mode 100644 index b2d6044be85db2f39f66deddc49cce06701b9741..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11293 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