diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml new file mode 100644 index 0000000..2d674c8 --- /dev/null +++ b/.github/workflows/basic.yml @@ -0,0 +1,41 @@ +name: Coverage + +on: + push: + branches: [ "dev" ] + pull_request: + branches: [ "dev" ] + workflow_dispatch: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + + - uses: actions/checkout@v3 + + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install .[dev] + python -m pip install pytest + python -m pip install pytest-cov + + - name: Run coverage + run: | + pytest --cov-config=.coveragerc --cov=./pkmodel --cov-report=xml --cov-branch + cat coverage.xml + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + files: coverage.xml diff --git a/pkmodel/model.py b/pkmodel/model.py index 1b5fd40..57f0b3b 100644 --- a/pkmodel/model.py +++ b/pkmodel/model.py @@ -1,6 +1,10 @@ # # Model class # +import matplotlib.pylab as plt +import numpy as np +import scipy.integrate +from scipy.integrate import odeint class Model: """A Pharmokinetic (PK) model @@ -13,5 +17,119 @@ class Model: """ def __init__(self, value=42): - self.value = value + self.value = value +class IV(Model): + # Your class definition here + def __init__(self, parameters=[0, 0, 0, 0, 0]): + super().__init__() + self.parameters = parameters + + def paramIV(self, y, t): + Q_p1, V_c, V_p1, CL, X = self.parameters + q_c, q_p1 = y + transition = Q_p1 * (q_c / V_c - q_p1 / V_p1) + dqc_dt = X - q_c / V_c * CL - transition + dqp1_dt = transition + return [dqc_dt, dqp1_dt] + + def integrate(): + iv_instance = IV() + t_eval = np.linspace(0, 1, 1000) + y0 = [0.0, 0.0] + iv_instance.parameters = parameters + solution = odeint(iv_instance.paramIV, y0, t_eval) + q_c, q_p1 = solution.T + return np.array([q_c,q_p1]) + +############TESTING IF CLASS WORKS +# parameters = [0.7, 1, 2, 3, 4] +# test1=IV.integrate() +# print('test1',test1.shape) + +############INTEGRATION OUTSIDE THE CLASS +# # Create an instance of the IV class +# iv_instance = IV() + +# # Define the time points at which you want to evaluate the solution +# t_eval = np.linspace(0, 1, 1000) + +# # Initial conditions +# y0 = [0.0, 0.0] + +# # Parameters +# parameters = [0.7, 1, 2, 3, 4] + +# # Set the parameters in the instance +# iv_instance.parameters = parameters + +# # Solve the ODEs using odeint +# solution = odeint(iv_instance.paramIV, y0, t_eval) + +# # Extract the results +# q_c, q_p1 = solution.T + +# Print the results +# print(q_c) +# print(q_p1) + + +class SC(Model): + "subcutaneous model" + def __init__(self, parameters=[0, 0, 0, 0, 0,0]): + super().__init__() + self.parameters = parameters + + def paramSC(self, y, t): + Q_p1, V_c, V_p1, CL, X, ka = self.parameters + q_c, q_p1, q_0= y + dq0_dt = X - ka*q_0 + transition = Q_p1 * (q_c / V_c - q_p1 / V_p1) + dqc_dt = ka*q_0 - q_c / V_c * CL - transition + dqp1_dt = transition + return [dqc_dt, dqp1_dt,dq0_dt] + + def integrate(): + sc_instance = SC() + t_eval = np.linspace(0, 1, 1000) + y0 = [0.0, 0.0, 0.0] + sc_instance.parameters = parameters + solution = odeint(sc_instance.paramSC, y0, t_eval) + q_c, q_p1, q_0 = solution.T + return np.array([q_c,q_p1,q_0]) + + +############TESTING IF CLASS WORKS +# parameters = [0.7, 1, 2, 3, 4, 5] +# test2=SC.integrate() +# print('test2',test2.shape) + +############INTEGRATION OUTSIDE THE CLASS +# # Create an instance of the IV class +# sc_instance = SC() + +# # Define the time points at which you want to evaluate the solution +# t_eval = np.linspace(0, 1, 1000) + +# # Initial conditions +# y0 = [0.0, 0.0,0] + +# # Parameters +# parameters = [0.7, 1, 2, 3, 4,7] + +# # Set the parameters in the instance +# sc_instance.parameters = parameters + +# # Solve the ODEs using odeint +# solution = odeint(sc_instance.paramSC, y0, t_eval) + +# print(solution.shape) + +# # Extract the results +# # print(solution.T.shape) +# q_c, q_p1, q_0 = solution.T + +# # Print the results +# # print(q_c.shape) +# # print(q_p1.shape) +# # print(solution.y[0, :]) \ No newline at end of file diff --git a/pkmodel/solution.py b/pkmodel/solution.py index d08710b..5c46a6f 100644 --- a/pkmodel/solution.py +++ b/pkmodel/solution.py @@ -1,17 +1,27 @@ -# -# Solution class -# +import numpy as np +import matplotlib.pylab as plt +import model -class Solution: - """A Pharmokinetic (PK) model solution +def visualise(modeldata): + time=np.linspace(0,1,1000) + fig, ax = plt.subplots() + # Plot q_c + plt.plot(time, modeldata[0,:], label = 'q_c') + # Plot q_p1 + plt.plot(time, modeldata[1,:], label = 'q_p1') + # Plot q_0 + if np.shape(modeldata) == (3, 1000): + plt.plot(time, modeldata[2,:], label = 'q_0') + plt.title('Subcutaneous model') + else: + plt.title('IV model') + plt.legend() + ax.set_ylabel('drug mass (ng)') + ax.set_xlabel('time [h]') + #plt.savefig('plot.png') + plt.show() - Parameters - ---------- - - value: numeric, optional - an example paramter - - """ - def __init__(self, value=44): - self.value = value +parameter=[1,2,3,4,5] +result=visualise(model.IV.integrate()) +print(result) \ No newline at end of file diff --git a/prototype.py b/prototype.py index 96b0083..789179d 100644 --- a/prototype.py +++ b/prototype.py @@ -5,13 +5,15 @@ def dose(t, X): return X -def rhs(t, y, Q_p1, V_c, V_p1, CL, X): +#intravenous injection model + +def IV(t, y, Q_p1, V_c, V_p1, CL, X): q_c, q_p1 = y transition = Q_p1 * (q_c / V_c - q_p1 / V_p1) dqc_dt = dose(t, X) - q_c / V_c * CL - transition dqp1_dt = transition return [dqc_dt, dqp1_dt] - +print(IV(2,[1,2],3,4,5,6,7)) model1_args = { 'name': 'model1', 'Q_p1': 1.0, @@ -21,6 +23,16 @@ def rhs(t, y, Q_p1, V_c, V_p1, CL, X): 'X': 1.0, } +#subcutaneous dosing +def SC(t, y, Q_p1, V_c, V_p1, CL, X, ka): + q_c, q_p1, q0= y + dq0_dt = dose(t,X) - ka*q0 + transition = Q_p1 * (q_c / V_c - q_p1 / V_p1) + dqc_dt = ka*q0 - q_c / V_c * CL - transition + dqp1_dt = transition + return [dqc_dt, dqp1_dt,dq0_dt] + + model2_args = { 'name': 'model2', 'Q_p1': 2.0, @@ -28,25 +40,43 @@ def rhs(t, y, Q_p1, V_c, V_p1, CL, X): 'V_p1': 1.0, 'CL': 1.0, 'X': 1.0, + 'ka':1.0 } t_eval = np.linspace(0, 1, 1000) y0 = np.array([0.0, 0.0]) fig = plt.figure() -for model in [model1_args, model2_args]: - args = [ - model['Q_p1'], model['V_c'], model['V_p1'], model['CL'], model['X'] - ] - sol = scipy.integrate.solve_ivp( - fun=lambda t, y: rhs(t, y, *args), - t_span=[t_eval[0], t_eval[-1]], - y0=y0, t_eval=t_eval - ) - plt.plot(sol.t, sol.y[0, :], label=model['name'] + '- q_c') - plt.plot(sol.t, sol.y[1, :], label=model['name'] + '- q_p1') + +args = [ + model1_args['Q_p1'], model1_args['V_c'], model1_args['V_p1'], model1_args['CL'], model1_args['X'] +] +sol = scipy.integrate.solve_ivp( + fun=lambda t, y: IV(t, y, *args), + t_span=[t_eval[0], t_eval[-1]], + y0=y0, t_eval=t_eval +) +plt.plot(sol.t, sol.y[0, :], label=model1_args['name'] + '- q_c') +plt.plot(sol.t, sol.y[1, :], label=model1_args['name'] + '- q_p1') + + + +y0 = np.array([0.0, 0.0, 0.0]) + +args = [ + model2_args['Q_p1'], model2_args['V_c'], model2_args['V_p1'], model2_args['CL'], model2_args['X'], model2_args['ka'] +] +sol = scipy.integrate.solve_ivp( + fun=lambda t, y: SC(t, y, *args), + t_span=[t_eval[0], t_eval[-1]], + y0=y0, t_eval=t_eval +) +plt.plot(sol.t, sol.y[0, :], label=model2_args['name'] + '- q_c') +plt.plot(sol.t, sol.y[1, :], label=model2_args['name'] + '- q_p1') plt.legend() plt.ylabel('drug mass [ng]') plt.xlabel('time [h]') -plt.show() + +plt.savefig('plot.png') + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0e8bab6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "pkmodel" +version = "0.0.1" +authors = [ + { name="Rhiannon Ackland", email="author@example.com" }, + { name="Sarah Bull", email="author@example.com" }, + { name="Sixtine Dromigny", email="author@example.com" }, + { name="Robert Doane-Solomon", email="author@example.com" }, + { name="Tristram Walsh", email="author@example.com" }, + ] +description = "pkmodel is a Pharmokinetic modelling library." +readme = "README.md" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: Linux", +] +dependencies =[ + 'numpy', + 'matplotlib', + 'scipy', + 'pytest', +] + +[project.urls] +"Homepage" = "https://github.com/robertdoanesolomon/software-engineering-projects-pk" +"Bug Tracker" = "https://github.com/robertdoanesolomon/software-engineering-projects-pk/issues" \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index e35b8c8..0000000 --- a/setup.py +++ /dev/null @@ -1,80 +0,0 @@ -# -# pkmodel setuptools script -# -from setuptools import setup, find_packages - - -def get_version(): - """ - Get version number from the pkmodel module. - - The easiest way would be to just ``import pkmodel ``, but note that this may - fail if the dependencies have not been installed yet. Instead, we've put - the version number in a simple version_info module, that we'll import here - by temporarily adding the oxrse directory to the pythonpath using sys.path. - """ - import os - import sys - - sys.path.append(os.path.abspath('pkmodel')) - from version_info import VERSION as version - sys.path.pop() - - return version - - -def get_readme(): - """ - Load README.md text for use as description. - """ - with open('README.md') as f: - return f.read() - - -# Go! -setup( - # Module name (lowercase) - name='pkmodel', - - # Version - version=get_version(), - - description='An example Python project.', - - long_description=get_readme(), - - license='MIT license', - - # author='', - - # author_email='', - - maintainer='Martin Robinson', - - maintainer_email='martin.robinson@cs.ox.ac.uk', - - url='https://github.com/SABS-R3/2020-software-engineering-projects-pk', - - # Packages to include - packages=find_packages(include=('pkmodel', 'pkmodel.*')), - - # List of dependencies - install_requires=[ - # Dependencies go here! - 'numpy', - 'matplotlib', - 'scipy', - ], - extras_require={ - 'docs': [ - # Sphinx for doc generation. Version 1.7.3 has a bug: - 'sphinx>=1.5, !=1.7.3', - # Nice theme for docs - 'sphinx_rtd_theme', - ], - 'dev': [ - # Flake8 for code style checking - 'flake8>=3', - ], - }, -) diff --git a/pkmodel/__init__.py b/src/pkmodel/__init__.py similarity index 100% rename from pkmodel/__init__.py rename to src/pkmodel/__init__.py diff --git a/src/pkmodel/model.py b/src/pkmodel/model.py new file mode 100644 index 0000000..6096cdf --- /dev/null +++ b/src/pkmodel/model.py @@ -0,0 +1,137 @@ +# +# Model class +# +import matplotlib.pylab as plt +import numpy as np +import scipy.integrate +from scipy.integrate import odeint + +class Model: + """A Pharmokinetic (PK) model + + Parameters + ---------- + + value: numeric, optional + an example paramter + + """ + def __init__(self, value=42): + self.value = value + +class IV(Model): + # Your class definition here + def __init__(self, parameters=[0, 0, 0, 0, 0]): + super().__init__() + self.parameters = parameters + + def paramIV(self, y, t): + Q_p1, V_c, V_p1, CL, X = self.parameters + q_c, q_p1 = y + transition = Q_p1 * (q_c / V_c - q_p1 / V_p1) + dqc_dt = X - q_c / V_c * CL - transition + dqp1_dt = transition + return [dqc_dt, dqp1_dt] + + def integrate(self): + t_eval = np.linspace(0, 1, 1000) + y0 = [0.0, 0.0] + solution = odeint(self.paramIV, y0, t_eval) + q_c, q_p1 = solution.T + return np.array([q_c,q_p1]) + +###########TESTING IF CLASS WORKS +parameters = [0.7, 1, 2, 3, 4] +test0 = IV(parameters=parameters) +test1 = test0.integrate() +#test1=IV.integrate() +print('test1',test1.shape) + +############INTEGRATION OUTSIDE THE CLASS +# # Create an instance of the IV class +# iv_instance = IV() + +# # Define the time points at which you want to evaluate the solution +# t_eval = np.linspace(0, 1, 1000) + +# # Initial conditions +# y0 = [0.0, 0.0] + +# # Parameters +# parameters = [0.7, 1, 2, 3, 4] + +# # Set the parameters in the instance +# iv_instance.parameters = parameters + +# # Solve the ODEs using odeint +# solution = odeint(iv_instance.paramIV, y0, t_eval) + +# # Extract the results +# q_c, q_p1 = solution.T + +# Print the results +# print(q_c) +# print(q_p1) + + +class SC(Model): + "subcutaneous model" + def __init__(self, parameters=[0, 0, 0, 0, 0,0]): + super().__init__() + self.parameters = parameters + + def paramSC(self, y, t): + Q_p1, V_c, V_p1, CL, X, ka = self.parameters + q_c, q_p1, q_0= y + dq0_dt = X - ka*q_0 + transition = Q_p1 * (q_c / V_c - q_p1 / V_p1) + dqc_dt = ka*q_0 - q_c / V_c * CL - transition + dqp1_dt = transition + return [dqc_dt, dqp1_dt,dq0_dt] + + def integrate(self): + + t_eval = np.linspace(0, 1, 1000) + y0 = [0.0, 0.0, 0.0] + + solution = odeint(self.paramSC, y0, t_eval) + q_c, q_p1, q_0 = solution.T + return np.array([q_c,q_p1,q_0]) + + +############TESTING IF CLASS WORKS +parameters = [0.7, 1, 2, 3, 4,6] +test0 = SC(parameters=parameters) +test1 = test0.integrate() +#test1=IV.integrate() +print('test1',test1.shape) + +############INTEGRATION OUTSIDE THE CLASS +# # Create an instance of the IV class +# sc_instance = SC() + +# # Define the time points at which you want to evaluate the solution +# t_eval = np.linspace(0, 1, 1000) + +# # Initial conditions +# y0 = [0.0, 0.0,0] + +# # Parameters +# parameters = [0.7, 1, 2, 3, 4,7] + +# # Set the parameters in the instance +# sc_instance.parameters = parameters + +# # Solve the ODEs using odeint +# solution = odeint(sc_instance.paramSC, y0, t_eval) + +# print(solution.shape) + +# # Extract the results +# # print(solution.T.shape) +# q_c, q_p1, q_0 = solution.T + +# # Print the results +# # print(q_c.shape) +# # print(q_p1.shape) +# # print(solution.y[0, :]) diff --git a/pkmodel/protocol.py b/src/pkmodel/protocol.py similarity index 100% rename from pkmodel/protocol.py rename to src/pkmodel/protocol.py diff --git a/src/pkmodel/solution.py b/src/pkmodel/solution.py new file mode 100644 index 0000000..1f7b99c --- /dev/null +++ b/src/pkmodel/solution.py @@ -0,0 +1,26 @@ +import numpy as np +import matplotlib.pylab as plt +import model + +def visualise(modeldata): + time=np.linspace(0,1,1000) + fig, ax = plt.subplots() + # Plot q_c + plt.plot(time, modeldata[0,:], label = 'q_c') + # Plot q_p1 + plt.plot(time, modeldata[1,:], label = 'q_p1') + # Plot q_0 + if np.shape(modeldata) == (3, 1000): + plt.plot(time, modeldata[2,:], label = 'q_0') + plt.title('Subcutaneous model') + else: + plt.title('IV model') + plt.legend() + ax.set_ylabel('drug mass (ng)') + ax.set_xlabel('time [h]') + plt.savefig('plot_vis.png') + #plt.show() + +parameters=[1,2,3,4,5,6] +test0 = model.SC(parameters=parameters) +result=visualise(model.SC.integrate(test0)) diff --git a/pkmodel/version_info.py b/src/pkmodel/version_info.py similarity index 100% rename from pkmodel/version_info.py rename to src/pkmodel/version_info.py diff --git a/pkmodel/tests/__init__.py b/tests/__init__.py similarity index 100% rename from pkmodel/tests/__init__.py rename to tests/__init__.py diff --git a/pkmodel/tests/test_model.py b/tests/test_model.py similarity index 99% rename from pkmodel/tests/test_model.py rename to tests/test_model.py index 274ba9c..4b1a528 100644 --- a/pkmodel/tests/test_model.py +++ b/tests/test_model.py @@ -1,7 +1,6 @@ import unittest import pkmodel as pk - class ModelTest(unittest.TestCase): """ Tests the :class:`Model` class. diff --git a/pkmodel/tests/test_protocol.py b/tests/test_protocol.py similarity index 100% rename from pkmodel/tests/test_protocol.py rename to tests/test_protocol.py diff --git a/pkmodel/tests/test_solution.py b/tests/test_solution.py similarity index 100% rename from pkmodel/tests/test_solution.py rename to tests/test_solution.py diff --git a/workflows/basic.yml b/workflows/basic.yml new file mode 100644 index 0000000..0287725 --- /dev/null +++ b/workflows/basic.yml @@ -0,0 +1,39 @@ +name: Coverage + +on: + push: + branches: [ "dev" ] + pull_request: + branches: [ "dev" ] + workflow_dispatch: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + + - uses: actions/checkout@v3 + + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install .[dev] + + - name: Run coverage + run: | + pytest --cov-config=.coveragerc --cov=./src/pkmodel --cov-report=xml --cov-branch + cat coverage.xml + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + files: coverage.xml