Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/jennyhelyanwe/post_MI_postprocessing
06 December 2024, 10:21:48 UTC
  • Code
  • Branches (1)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/main
    • 48c36cc0c5a8e853a4d97b18309493957e608091
    No releases to show
  • f27d72d
  • /
  • postprocessing_scripts
  • /
  • ECGPV_visualisation.py
Raw File Download
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge Iframe embedding
swh:1:cnt:b198a1f330160f957d8292203ba1d0d204eb0c0c
origin badgedirectory badge Iframe embedding
swh:1:dir:a8a7d5884d0a84fa38356ad37ebde46e91c9e6ee
origin badgerevision badge
swh:1:rev:48c36cc0c5a8e853a4d97b18309493957e608091
origin badgesnapshot badge
swh:1:snp:186a3806b6eb2061558f5264a47b071c2be0e794
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Tip revision: 48c36cc0c5a8e853a4d97b18309493957e608091 authored by Jenny on 03 December 2024, 11:43:08 UTC
Cell model copyright and clean version
Tip revision: 48c36cc
ECGPV_visualisation.py
import os, sys
import numpy
import matplotlib
#matplotlib.use('tkagg')  # For use on CSCS daint
from matplotlib import pyplot as plt
from matplotlib.gridspec import GridSpec
import matplotlib.animation as animation
import time
import argparse
import pickle
from scipy.signal import lfilter, resample
from scipy import io

class ECGPV_visualisation:
    def __init__(self, CL):
        self.CL = CL
        self.beat_fig_size = [5, 5]
        self.pv_fig_size = [6, 7]

    def read_ecg_pv(self, name, dir):
        meshname = dir+name
        print(meshname)
        if os.path.exists(dir+'ecgs.pl'):
            ecgs = pickle.load(open(dir+'ecgs.pl', 'rb'))
        else:
            ecgs = self._read_ECG(meshname)
            pickle.dump(ecgs, open(dir+'ecgs.pl', 'wb'))
        if os.path.exists(dir+'pvs.pl'):
            pvs = pickle.load(open(dir+'pvs.pl', 'rb'))
        else:
            pvs = self._read_PV(meshname)
            pickle.dump(pvs, open(dir+'pvs.pl', 'wb'))
        # Save as .mat file for delineation using Karlsruhe code:
        raw_leads = numpy.vstack([ecgs['I']/ecgs['max_all_leads'],ecgs['II']/ecgs['max_all_leads'],ecgs['III']/ecgs['max_all_leads'],ecgs['aVL']/ecgs['max_all_leads'],ecgs['aVR']/ecgs['max_all_leads'],ecgs['aVF']/ecgs['max_all_leads'],ecgs['V1']/ecgs['max_all_leads'],ecgs['V2']/ecgs['max_all_leads'],ecgs['V3']/ecgs['max_all_leads'],ecgs['V4']/ecgs['max_all_leads'],ecgs['V5']/ecgs['max_all_leads'],ecgs['V6']/ecgs['max_all_leads']]).T

        raw_leads_resampled = numpy.zeros((int(ecgs['t'][-1]/0.002), 12))
        # Down sample to 500 Hz
        for i in range(0, 12):
            nsample = int(ecgs['t'][-1]/0.002)
            raw_leads_resampled[:,i] = resample(raw_leads[:,i], nsample)
        io.savemat('raw_ecg_leads.mat', {'signal':raw_leads_resampled})
        io.savemat('ecgs.mat', {'ecgs': ecgs})
        io.savemat('pvs.mat', {'pvs': pvs})
        return ecgs, pvs

    def _read_ECG(self, meshname):
        filename = meshname + '.exm.vin'
        with open(filename,'r') as f:
            data = f.readlines()
        LA = RA= LL = RL = V1 = V2 = V3 = V4 = V5 = V6 = t= []

        # First 7 lines are header lines.
        if len(data) > 7:
            LA = numpy.zeros(len(data)-7)
            RA = numpy.zeros(len(data)-7)
            LL = numpy.zeros(len(data)-7)
            RL = numpy.zeros(len(data)-7)
            V1 = numpy.zeros(len(data)-7)
            V2 = numpy.zeros(len(data)-7)
            V3 = numpy.zeros(len(data)-7)
            V4 = numpy.zeros(len(data)-7)
            V5 = numpy.zeros(len(data)-7)
            V6 = numpy.zeros(len(data)-7)
            t = numpy.zeros(len(data)-7)

        for i in range(7, len(data)):
        	t[i-7] = float(data[i].split()[-12])
        	LA[i-7] = float(data[i].split()[-10])
        	RA[i-7] = float(data[i].split()[-9])
        	LL[i-7] = float(data[i].split()[-8])
        	RL[i-7] = float(data[i].split()[-7])
        	V1[i-7] = float(data[i].split()[-6])
        	V2[i-7] = float(data[i].split()[-5])
        	V3[i-7] = float(data[i].split()[-4])
        	V4[i-7] = float(data[i].split()[-3])
        	V5[i-7] = float(data[i].split()[-2])
        	V6[i-7] = float(data[i].split()[-1])

        sort_i = numpy.argsort(t)
        t = t[sort_i]
        LA = LA[sort_i]
        RA = RA[sort_i]
        LL = LL[sort_i]
        RL = RL[sort_i]
        V1 = V1[sort_i]
        V2 = V2[sort_i]
        V3 = V3[sort_i]
        V4 = V4[sort_i]
        V5 = V5[sort_i]
        V6 = V6[sort_i]
        
        # Ealuate Wilson's central terminal
        VW = 1.0/3.0*(RA + LA + LL)

        # Evaluate simulated ECG lead traces
        V1 = V1 - VW
        V2 = V2 - VW
        V3 = V3 - VW
        V4 = V4 - VW
        V5 = V5 - VW
        V6 = V6 - VW
        I = LA - RA
        II = LL - RA
        III = LL - LA
        aVL = LA - (RA + LL)/2.0
        aVF = LL - (LA + RA)/2.0
        aVR = RA - (LA + LL)/2.0

        all_leads = numpy.concatenate((V1,V2,V3,V4,V5,V6,I,II,III,aVR,aVL,aVF))
        precord_leads = numpy.concatenate((V1,V2,V3,V4,V5,V6))
        limb_leads = numpy.concatenate((I,II,III,aVR,aVL,aVF))
        max_all_leads = max(abs(all_leads))
        max_precord_leads = max(abs(precord_leads))
        max_limb_leads = max(abs(limb_leads))

        # Divide into beats
        ts, V1s = self._divide_signal(t, V1, self.CL)
        ts, V2s = self._divide_signal(t, V2, self.CL)
        ts, V3s = self._divide_signal(t, V3, self.CL)
        ts, V4s = self._divide_signal(t, V4, self.CL)
        ts, V5s = self._divide_signal(t, V5, self.CL)
        ts, V6s = self._divide_signal(t, V6, self.CL)
        ts, aVRs = self._divide_signal(t, aVR, self.CL)
        ts, aVLs = self._divide_signal(t, aVL, self.CL)
        ts, aVFs = self._divide_signal(t, aVF, self.CL)
        ts, Is = self._divide_signal(t, I, self.CL)
        ts, IIs = self._divide_signal(t, II, self.CL)
        ts, IIIs = self._divide_signal(t, III, self.CL)

        output_dict = {'t':t,'ts':ts,'V1':V1,'V2':V2,'V3':V3,'V4':V4,'V5':V5,'V6':V6,
                    'aVR':aVR,'aVL':aVL,'aVF':aVF,'I':I,'II':II,'III':III,
                    'V1s':V1s,'V2s':V2s,'V3s':V3s,'V4s':V4s,'V5s':V5s,'V6s':V6s,
                    'aVRs':aVRs,'aVLs':aVLs,'aVFs':aVFs,'Is':Is,'IIs':IIs,'IIIs':IIIs,
                    'max_all_leads':max_all_leads,'max_precord_leads':max_precord_leads,
                    'max_limb_leads':max_limb_leads}
        return output_dict

    def _read_PV(self,meshname):
        filename = meshname + '-cardiac-cycle.sld.res'
        with open(filename, 'r') as f:
        	data = f.readlines()
        if (len(data)>18):
        	pl = numpy.zeros(len(data)-17)
        	pr = numpy.zeros(len(data)-17)
        	vl= numpy.zeros(len(data)-17)
        	vr = numpy.zeros(len(data)-17)
        	curtime = numpy.zeros(len(data)-17)
        	phasel = numpy.zeros(len(data)-17)
        	phaser = numpy.zeros(len(data)-17)
        	vl[0] = float(data[18].split()[5])
        	vr[0] = float(data[18].split()[-3])

        	for i in range(18, len(data)):
        		pl[i-17] = float(data[i].split()[6])/10000
        		pr[i-17] = float(data[i].split()[-2])/10000
        		vl[i-17] = float(data[i].split()[5])
        		vr[i-17] = float(data[i].split()[-3])
        		curtime[i-17] = float(data[i].split()[1])
        		phasel[i-17] = float(data[i].split()[4])
        		phaser[i-17] = float(data[i].split()[10])

        sort_i = numpy.argsort(curtime)
        curtime = curtime[sort_i]
        pl = pl[sort_i]
        pr = pr[sort_i]
        vl = vl[sort_i]
        vr = vr[sort_i]
        phasel = phasel[sort_i]
        phaser = phaser[sort_i]

        # Divide into beats
        ts, pls = self._divide_signal(curtime, pl, self.CL)
        ts, vls = self._divide_signal(curtime, vl, self.CL)
        ts, prs = self._divide_signal(curtime, pr, self.CL)
        ts, vrs = self._divide_signal(curtime, vr, self.CL)
        ts, phasels = self._divide_signal(curtime, phasel, self.CL)
        ts, phasers = self._divide_signal(curtime, phaser, self.CL)
        output_dict = {'t':curtime, 'ts':ts, 'pl':pl, 'vl':vl, 'pr':pr, 'vr':vr, 'phasel':phasel, 'phaser':phaser,
                        'pls':pls, 'vls':vls, 'prs':prs, 'vrs':vrs, 'phasels':phasels, 'phasers':phasers,
                        'plabel':'Pressure (kPa)', 'vlabel':'Volume (mL)'}
        return output_dict

    def _divide_signal(self, curtime, signal, CL):
        if (curtime.max() > CL):
            t_offsets = []
            signals = []
            idx_start = []
            idx_end = []
            for i in range(0, int(curtime.max()/CL)+1):
                idx_start.append(numpy.where(curtime>=i*CL)[0][0])
                if curtime.max() > (i+1)*CL:
                	idx_end.append(numpy.where(curtime>=(i+1)*CL)[0][0])
                else:
                	idx_end.append(len(curtime)-1)
                t_offsets.append(curtime[idx_start[i]:idx_end[i]]-curtime[idx_start[i]])
                signals.append(signal[idx_start[i]:idx_end[i]])
        else:
            t_offsets = [curtime]
            signals = [signal]
        return t_offsets, signals

    def plot_ecgpv_live(self, ecgs, pvs, title, show, ecgs2=[], pvs2=[]):
        print('Plotting ECG PV live')
        matplotlib.rcParams.update({'font.size':'11'})
        matplotlib.rcParams.update({'text.color':'black'})
        matplotlib.rcParams.update({'lines.linewidth':'1'})
        fig = plt.figure(tight_layout=True, figsize=[15,7])
        fig.suptitle(title)
        gs = GridSpec(3,6)
        axs = []
        axs.append(fig.add_subplot(gs[:,0]))
        axs.append(fig.add_subplot(gs[0,1]))
        axs.append(fig.add_subplot(gs[1,1]))
        axs.append(fig.add_subplot(gs[2,1]))
        axs.append(fig.add_subplot(gs[0,2]))
        axs.append(fig.add_subplot(gs[1,2]))
        axs.append(fig.add_subplot(gs[2,2]))
        axs.append(fig.add_subplot(gs[0,3]))
        axs.append(fig.add_subplot(gs[1,3]))
        axs.append(fig.add_subplot(gs[2,3]))
        axs.append(fig.add_subplot(gs[0,4]))
        axs.append(fig.add_subplot(gs[1,4]))
        axs.append(fig.add_subplot(gs[2,4]))
        axs.append(fig.add_subplot(gs[0,5]))
        axs.append(fig.add_subplot(gs[1,5]))
        axs.append(fig.add_subplot(gs[2,5]))

        def animate_single(i):
            # Plot PV
            axs[0].clear()
            axs[0].plot(pvs['vl'], pvs['pl'], pvs['vr'], pvs['pr'])
            axs[1].clear()
            axs[1].plot(pvs['t'], pvs['pl'], pvs['t'], pvs['pr'])
            axs[2].clear()
            axs[2].plot(pvs['t'], pvs['vl'], pvs['t'], pvs['vr'])
            axs[4].clear()
            axs[3].plot(pvs['t'], pvs['phasel'], pvs['t'], pvs['phaser'])

            # Plot ECGs:
            axs[4].clear()
            axs[4].plot(ecgs['t'], ecgs['I']/ecgs['max_limb_leads'])
            axs[5].clear()
            axs[5].plot(ecgs['t'], ecgs['II']/ecgs['max_limb_leads'])
            axs[6].clear()
            axs[6].plot(ecgs['t'], ecgs['III']/ecgs['max_limb_leads'])
            axs[7].clear()
            axs[7].plot(ecgs['t'], ecgs['aVR']/ecgs['max_limb_leads'])
            axs[8].clear()
            axs[8].plot(ecgs['t'], ecgs['aVL']/ecgs['max_limb_leads'])
            axs[9].clear()
            axs[9].plot(ecgs['t'], ecgs['aVF']/ecgs['max_limb_leads'])
            axs[10].clear()
            axs[10].plot(ecgs['t'], ecgs['V1']/ecgs['max_precord_leads'])
            axs[11].clear()
            axs[11].plot(ecgs['t'], ecgs['V2']/ecgs['max_precord_leads'])
            axs[12].clear()
            axs[12].plot(ecgs['t'], ecgs['V3']/ecgs['max_precord_leads'])
            axs[13].clear()
            axs[13].plot(ecgs['t'], ecgs['V4']/ecgs['max_precord_leads'])
            axs[14].clear()
            axs[14].plot(ecgs['t'], ecgs['V5']/ecgs['max_precord_leads'])
            axs[15].clear()
            axs[15].plot(ecgs['t'], ecgs['V6']/ecgs['max_precord_leads'])

            axs[0].set_xlabel('Volume (mL)')
            axs[1].set_xlabel('Time (ms)')
            axs[2].set_xlabel('Time (ms)')
            axs[3].set_xlabel('Time (ms)')
            axs[0].set_ylabel('Pressure (kPa)')
            axs[1].set_ylabel('Pressure (kPa)')
            axs[2].set_ylabel('Volume (mL)')
            axs[3].set_ylabel('Phase')
            axs[3].set_ylim([0,5])
            axs[1].set_title('Pressure transient')
            axs[2].set_title('Volume transient')
            axs[3].set_title('Cardiac cycle')
            axs[4].set_xlabel('Time (s)')
            axs[5].set_xlabel('Time (s)')
            axs[6].set_xlabel('Time (s)')
            axs[7].set_xlabel('Time (s)')
            axs[8].set_xlabel('Time (s)')
            axs[9].set_xlabel('Time (s)')
            axs[10].set_xlabel('Time (s)')
            axs[11].set_xlabel('Time (s)')
            axs[12].set_xlabel('Time (s)')
            axs[13].set_xlabel('Time (s)')
            axs[14].set_xlabel('Time (s)')
            axs[15].set_xlabel('Time (s)')
            axs[4].set_ylabel('Normalised ECG')
            axs[5].set_ylabel('Normalised ECG')
            axs[6].set_ylabel('Normalised ECG')
            axs[7].set_ylabel('Normalised ECG')
            axs[8].set_ylabel('Normalised ECG')
            axs[9].set_ylabel('Normalised ECG')
            axs[10].set_ylabel('Normalised ECG')
            axs[11].set_ylabel('Normalised ECG')
            axs[12].set_ylabel('Normalised ECG')
            axs[13].set_ylabel('Normalised ECG')
            axs[14].set_ylabel('Normalised ECG')
            axs[15].set_ylabel('Normalised ECG')
            axs[4].set_ylim(-1,1)
            axs[5].set_ylim(-1,1)
            axs[6].set_ylim(-1,1)
            axs[7].set_ylim(-1,1)
            axs[8].set_ylim(-1,1)
            axs[9].set_ylim(-1,1)
            axs[10].set_ylim(-1,1)
            axs[11].set_ylim(-1,1)
            axs[12].set_ylim(-1,1)
            axs[13].set_ylim(-1,1)
            axs[14].set_ylim(-1,1)
            axs[15].set_ylim(-1,1)
            axs[4].set_title('I')
            axs[5].set_title('II')
            axs[6].set_title('III')
            axs[7].set_title('aVR')
            axs[8].set_title('aVL')
            axs[9].set_title('aVF')
            axs[10].set_title('V1')
            axs[11].set_title('V2')
            axs[12].set_title('V3')
            axs[13].set_title('V4')
            axs[14].set_title('V5')
            axs[15].set_title('V6')

        def animate_double(i):
            # Plot PV
            axs[0].clear()
            axs[0].plot(pvs['vl'], pvs['pl'], pvs['vr'], pvs['pr'], pvs2['vl'],pvs2['pl'],'--', pvs2['vr'], pvs2['pr'], '--')
            axs[1].clear()
            axs[1].plot(pvs['t'], pvs['pl'], pvs['t'], pvs['pr'],pvs2['t'], pvs2['pl'],'--', pvs2['t'], pvs2['pr'], '--')
            axs[2].clear()
            axs[2].plot(pvs['t'], pvs['vl'], pvs['t'], pvs['vr'],pvs2['t'], pvs2['vl'], '--', pvs2['t'],  pvs2['vr'], '--')
            axs[4].clear()
            axs[3].plot(pvs['t'], pvs['phasel'], pvs['t'], pvs['phaser'],pvs2['t'], pvs2['phasel'], '--', pvs2['t'], pvs2['phaser'], '--')
            axs[0].set_xlabel('Volume (mL)')
            axs[1].set_xlabel('Time (ms)')
            axs[2].set_xlabel('Time (ms)')
            axs[3].set_xlabel('Time (ms)')
            axs[0].set_ylabel('Pressure (kPa)')
            axs[1].set_ylabel('Pressure (kPa)')
            axs[2].set_ylabel('Volume (mL)')
            axs[3].set_ylabel('Phase')
            axs[3].set_ylim([0,5])

            axs[1].set_title('Pressure transient')
            axs[2].set_title('Volume transient')
            axs[3].set_title('Cardiac cycle')

            # Plot ECGs:
            max_all_leads = max([ecgs['max_all_leads'], ecgs2['max_all_leads']])
            max_precord_leads = max([ecgs['max_precord_leads'], ecgs2['max_precord_leads']])
            max_limb_leads = max([ecgs['max_limb_leads'], ecgs2['max_limb_leads']])
            axs[4].clear()
            axs[4].plot(ecgs['t'], ecgs['I']/max_limb_leads, ecgs2['t'], ecgs2['I']/max_limb_leads, '--')
            axs[5].clear()
            axs[5].plot(ecgs['t'], ecgs['II']/max_limb_leads, ecgs2['t'],ecgs2['II']/max_limb_leads, '--')
            axs[6].clear()
            axs[6].plot(ecgs['t'], ecgs['III']/max_limb_leads, ecgs2['t'], ecgs2['III']/max_limb_leads, '--')
            axs[7].clear()
            axs[7].plot(ecgs['t'], ecgs['aVR']/max_limb_leads, ecgs2['t'], ecgs2['aVR']/max_limb_leads, '--')
            axs[8].clear()
            axs[8].plot(ecgs['t'], ecgs['aVL']/max_limb_leads, ecgs2['t'], ecgs2['aVL']/max_limb_leads, '--')
            axs[9].clear()
            axs[9].plot(ecgs['t'], ecgs['aVF']/max_limb_leads, ecgs2['t'], ecgs2['aVF']/max_limb_leads, '--')
            axs[10].clear()
            axs[10].plot(ecgs['t'], ecgs['V1']/max_precord_leads, ecgs2['t'], ecgs2['V1']/max_precord_leads, '--')
            axs[11].clear()
            axs[11].plot(ecgs['t'], ecgs['V2']/max_precord_leads, ecgs2['t'], ecgs2['V2']/max_precord_leads, '--')
            axs[12].clear()
            axs[12].plot(ecgs['t'], ecgs['V3']/max_precord_leads, ecgs2['t'], ecgs2['V3']/max_precord_leads, '--')
            axs[13].clear()
            axs[13].plot(ecgs['t'], ecgs['V4']/max_precord_leads, ecgs2['t'], ecgs2['V4']/max_precord_leads, '--')
            axs[14].clear()
            axs[14].plot(ecgs['t'], ecgs['V5']/max_precord_leads, ecgs2['t'], ecgs2['V5']/max_precord_leads, '--')
            axs[15].clear()
            axs[15].plot(ecgs['t'], ecgs['V6']/max_precord_leads, ecgs2['t'], ecgs2['V6']/max_precord_leads, '--')
            axs[4].set_xlabel('Time (s)')
            axs[5].set_xlabel('Time (s)')
            axs[6].set_xlabel('Time (s)')
            axs[7].set_xlabel('Time (s)')
            axs[8].set_xlabel('Time (s)')
            axs[9].set_xlabel('Time (s)')
            axs[10].set_xlabel('Time (s)')
            axs[11].set_xlabel('Time (s)')
            axs[12].set_xlabel('Time (s)')
            axs[13].set_xlabel('Time (s)')
            axs[14].set_xlabel('Time (s)')
            axs[15].set_xlabel('Time (s)')
            axs[4].set_ylabel('Normalised ECG')
            axs[5].set_ylabel('Normalised ECG')
            axs[6].set_ylabel('Normalised ECG')
            axs[7].set_ylabel('Normalised ECG')
            axs[8].set_ylabel('Normalised ECG')
            axs[9].set_ylabel('Normalised ECG')
            axs[10].set_ylabel('Normalised ECG')
            axs[11].set_ylabel('Normalised ECG')
            axs[12].set_ylabel('Normalised ECG')
            axs[13].set_ylabel('Normalised ECG')
            axs[14].set_ylabel('Normalised ECG')
            axs[15].set_ylabel('Normalised ECG')
            axs[4].set_ylim(-1,1)
            axs[5].set_ylim(-1,1)
            axs[6].set_ylim(-1,1)
            axs[7].set_ylim(-1,1)
            axs[8].set_ylim(-1,1)
            axs[9].set_ylim(-1,1)
            axs[10].set_ylim(-1,1)
            axs[11].set_ylim(-1,1)
            axs[12].set_ylim(-1,1)
            axs[13].set_ylim(-1,1)
            axs[14].set_ylim(-1,1)
            axs[15].set_ylim(-1,1)
            axs[4].set_title('I')
            axs[5].set_title('II')
            axs[6].set_title('III')
            axs[7].set_title('aVR')
            axs[8].set_title('aVL')
            axs[9].set_title('aVF')
            axs[10].set_title('V1')
            axs[11].set_title('V2')
            axs[12].set_title('V3')
            axs[13].set_title('V4')
            axs[14].set_title('V5')
            axs[15].set_title('V6')

        # if ecgs2:
        #     ani = animation.FuncAnimation(fig, animate_double, fargs=( axs, ecgs, pvs, ecgs2, pvs2), interval=1000)
        # else:
        ani = animation.FuncAnimation(fig, animate_single, interval=1000)
        if show:
            plt.show(block=True)

    def _set_ecg_ticks(self,ax, t_end, CL):
        t_start = 0
        t_end = numpy.ceil(t_end/CL) * CL
        t_end = int(numpy.ceil(t_end*10.0/2.0)*2)/10.0
        #t_end = numpy.round(t_end*10.0)/10.0
        #t_end = numpy.round(t_end)
        #t_end = self.CL
        minor_ticks = numpy.arange(0, t_end+0.04, 0.04)
        major_ticks = numpy.arange(0, t_end+0.2, 0.2)
        #minor_ticks = numpy.linspace(0, t_end, int((t_end-t_start)/0.04) + 1)
        #major_ticks = numpy.linspace(0, t_end, int((t_end-t_start)/0.2) + 1)
        ax.set_xticks(minor_ticks, minor=True)
        ax.set_xticks(major_ticks)
        minor_ticks = numpy.linspace(-1, 1, 21)
        major_ticks = numpy.linspace(-1, 1, 5)
        ax.set_yticks(minor_ticks, minor=True)
        ax.set_yticks(major_ticks)
        ax.grid(which="minor", color='r', linestyle='-', linewidth=1, alpha=0.5)
        ax.grid(which="major", color='r', linestyle='-', linewidth=2, alpha=0.5)

    def _set_full_ecg_ticks(self,ax, t_end, v_max, v_min, CL):
        t_start = 0
        t_end = numpy.ceil(t_end/CL) * CL
        t_end = int(numpy.ceil(t_end*10.0/2.0)*2)/10.0
        minor_ticks = numpy.arange(0, t_end+0.04, 0.04)
        major_ticks = numpy.arange(0, t_end+0.2, 0.2)
        ax.set_xticks(minor_ticks, minor=True)
        ax.set_xticks(major_ticks)
        v_max = numpy.ceil(v_max)
        minor_ticks = numpy.arange(v_min, v_max+0.1, 0.1)
        major_ticks = numpy.arange(v_min, v_max+0.5, 0.5)
        ax.set_yticks(minor_ticks, minor=True)
        ax.set_yticks(major_ticks)
        ax.grid(which="minor", color='r', linestyle='-', linewidth=1, alpha=0.3)
        ax.grid(which="major", color='r', linestyle='-', linewidth=2, alpha=0.3)
        ax.spines['right'].set_visible(False)
        ax.spines['left'].set_visible(False)
        ax.spines['top'].set_visible(False)
        ax.spines['bottom'].set_visible(False)
        for tic in ax.xaxis.get_major_ticks():
            tic.tick1line.set_visible(False)
            tic.tick2line.set_visible(False)
            tic.label1.set_visible(False)
            tic.label2.set_visible(False)
        for tic in ax.xaxis.get_minor_ticks():
            tic.tick1line.set_visible(False)
            tic.tick2line.set_visible(False)
            tic.label1.set_visible(False)
            tic.label2.set_visible(False)
        for tic in ax.yaxis.get_major_ticks():
            tic.tick1line.set_visible(False)
            tic.tick2line.set_visible(False)
            tic.label1.set_visible(False)
            tic.label2.set_visible(False)
        for tic in ax.yaxis.get_minor_ticks():
            tic.tick1line.set_visible(False)
            tic.tick2line.set_visible(False)
            tic.label1.set_visible(False)
            tic.label2.set_visible(False)

    def _set_pv_ticks(self,ax):
        x_ticks = numpy.arange(50, 250, 50)
        y_ticks = numpy.arange(0, 140, 20)
        ax.set_xticks(x_ticks)
        ax.set_yticks(y_ticks)
        #ax.grid(which="major", color='k', linestyle='--', linewidth=1, alpha=0.5)

    def plot_ecg_lead(self, ecgs, lead_name, filename,show, beat=0, ecg2=[]):
        print('Plotting ECG lead '+str(lead_name))
        matplotlib.rcParams.update({'font.size':'24'})
        matplotlib.rcParams.update({'text.color':'black'})
        matplotlib.rcParams.update({'lines.linewidth':'3'})
        if ecg2:
            if beat > 0:
                max_all_leads = max([ecgs['max_all_leads'], ecgs2['max_all_leads']])
                self._plot_double(self.beat_fig_size,
                            ecgs['ts'][beat-1],ecgs[lead_name+'s'][beat-1]/max_all_leads,'-',
                            ecgs2['ts'][beat-1], ecgs[lead_name+'s'][beat-1]/max_all_leads,'--',
                            'Time (s)', lead_name, filename+'_beat'+str(beat)+'_comparison.png',
                            show,ecg_grid=True)
            else:
                figsize=[self.beat_fig_size[0]*(int(ecgs['t'][-1]/1.0)+1), self.beat_fig_size[1]]
                self._plot_double(figsize, ecgs['t'], ecgs[lead_name]/max_all_leads,'-',
                                ecgs2['t'], ecgs2[lead_name]/max_all_leads,'--'
                                'Time (s)', lead_name, filename+'_full_comparison.png',show, ecg_grid=True)
        else:
            if beat > 0:
                self._plot_single(self.beat_fig_size, ecgs['ts'][beat-1],
                            ecgs[lead_name+'s'][beat-1]/ecgs['max_all_leads'],
                            'Time (s)', lead_name, filename+'_beat'+str(beat)+'.png',
                            show, ecg_grid=True)
            else:
                figsize=[self.beat_fig_size[0]*(int(ecgs['t'][-1]/1.0)+1), self.beat_fig_size[1]]
                self._plot_single(figsize, ecgs['t'], ecgs[lead_name]/ecgs['max_all_leads'],
                                'Time (s)', lead_name, filename+'_full.png', show,ecg_grid=True)

    def plot_ecg_all_leads(self, ecgs, show, beat=0, ecgs2=[]):
        print('Plotting all ECG leads')
        matplotlib.rcParams.update({'font.size':'24'})
        matplotlib.rcParams.update({'text.color':'black'})
        matplotlib.rcParams.update({'lines.linewidth':'1'})

        if ecgs2:
            print('Full ECG comparison not yet implemented...')
        else:
            if beat > 0:
                # t_padding
                t_padding = 0.2
                v_padding = 0.5

                # Generate calibrating step function
                t_res = ecgs['t'][1] - ecgs['t'][0]
                t_calib = numpy.arange(0, 0.2+t_res, t_res)
                v_calib = numpy.zeros(numpy.shape(t_calib))
                for i in range(0, len(t_calib)):
                    if t_calib[i] < 0.04:
                        v_calib[i] = 0.0
                    elif t_calib[i] > t_calib[-1]-0.04:
                        v_calib[i] = 0.0
                    else:
                        v_calib[i] = 1.0

                # Concatenate the leads together to plot in a single figure
                t_end = ecgs['ts'][beat-1][-1]
                full_t = numpy.concatenate([t_calib, ecgs['ts'][beat-1]+t_calib[-1], ecgs['ts'][beat-1]+t_end+t_calib[-1], ecgs['ts'][beat-1]+2*t_end+t_calib[-1], ecgs['ts'][beat-1]+3*t_end+t_calib[-1]]) + t_padding
                scale = 1
                fig_size = [numpy.ceil(full_t[-1]+t_padding)*0.5/0.2*scale,6*scale]
                fig = plt.figure(tight_layout=True, figsize=fig_size)
                gs = GridSpec(1,1)
                ax = fig.add_subplot(gs[0,0])

                # Bottom row: III, aVF, V3, V6
                bottom_V = numpy.concatenate([v_calib, ecgs['IIIs'][beat-1]/ecgs['max_limb_leads'], ecgs['aVFs'][beat-1]/ecgs['max_limb_leads'], ecgs['V3s'][beat-1]/ecgs['max_precord_leads'], ecgs['V6s'][beat-1]/ecgs['max_precord_leads']])
                ax.plot(full_t, bottom_V, 'k')

                # Middle row: II, aVL, V2, V5
                offset = 2
                midrow_V = numpy.concatenate([v_calib+offset, ecgs['IIs'][beat-1]/ecgs['max_limb_leads']+offset, ecgs['aVLs'][beat-1]/ecgs['max_limb_leads']+offset, ecgs['V2s'][beat-1]/ecgs['max_precord_leads']+offset, ecgs['V5s'][beat-1]/ecgs['max_precord_leads']+offset])
                ax.plot(full_t, midrow_V, 'k')

                # Top row: I, aVR, V1, V4
                toprow_V = numpy.concatenate([v_calib+offset*2, ecgs['Is'][beat-1]/ecgs['max_limb_leads']+offset*2, ecgs['aVRs'][beat-1]/ecgs['max_limb_leads']+offset*2, ecgs['V1s'][beat-1]/ecgs['max_precord_leads']+offset*2, ecgs['V4s'][beat-1]/ecgs['max_precord_leads']+offset*2])
                ax.plot(full_t, toprow_V, 'k')

                # ax.set_xlim([0, t_end*4])
                # ax.set_ylim([-1, offset*2+1])
                #
                # self._set_full_ecg_ticks(ax, full_t[-1]+t_padding, offset*2+1, self.CL)

                # Add ECG red grid
                self._set_full_ecg_ticks(ax, full_t[-1]+t_padding*2, offset*2 + 1 + v_padding, -1-v_padding, self.CL)
                ax.set_xlim([0, full_t[-1]+t_padding*2])
                ax.set_ylim([-1-v_padding, offset*2 + 1 +v_padding])

                # Label the leads
                label_fontsize = 'large'
                label_t_offset = t_calib[-1] + t_padding
                label_v_offset = 0.1
                plt.text(0+label_t_offset, -1+label_v_offset, 'III', fontsize=label_fontsize)
                plt.text(t_end+label_t_offset, -1+label_v_offset, 'aVF', fontsize=label_fontsize)
                plt.text(2*t_end+label_t_offset, -1+label_v_offset, 'V3', fontsize=label_fontsize)
                plt.text(3*t_end+label_t_offset, -1+label_v_offset, 'V6', fontsize=label_fontsize)
                plt.text(0+label_t_offset, -1+offset+label_v_offset, 'II', fontsize=label_fontsize)
                plt.text(t_end+label_t_offset, -1+offset+label_v_offset, 'aVL', fontsize=label_fontsize)
                plt.text(2*t_end+label_t_offset, -1+offset+label_v_offset, 'V2', fontsize=label_fontsize)
                plt.text(3*t_end+label_t_offset, -1+offset+label_v_offset, 'V5', fontsize=label_fontsize)
                plt.text(0+label_t_offset, -1+offset*2+label_v_offset, 'I', fontsize=label_fontsize)
                plt.text(t_end+label_t_offset, -1+offset*2+label_v_offset, 'aVR', fontsize=label_fontsize)
                plt.text(2*t_end+label_t_offset, -1+offset*2+label_v_offset, 'V1', fontsize=label_fontsize)
                plt.text(3*t_end+label_t_offset, -1+offset*2+label_v_offset, 'V4', fontsize=label_fontsize)
                plt.savefig('full_ecg_beat_'+str(beat)+'.png')
                if show:
                    plt.show()
            else:
                # Padding
                t_padding = 0.2
                v_padding = 0.5

                # Generate calibrating step function
                t_res = ecgs['t'][1] - ecgs['t'][0]
                t_calib = numpy.arange(0, 0.2+t_res, t_res)
                v_calib = numpy.zeros(numpy.shape(t_calib))
                for i in range(0, len(t_calib)):
                    if t_calib[i] < 0.04:
                        v_calib[i] = 0.0
                    elif t_calib[i] > t_calib[-1]-0.04:
                        v_calib[i] = 0.0
                    else:
                        v_calib[i] = 1.0

                # Concatenate the leads together to plot in a single figure
                t_end = ecgs['t'][-1]
                full_t = numpy.concatenate([t_calib, ecgs['t']+t_calib[-1], ecgs['t']+t_end+t_calib[-1], ecgs['t']+2*t_end+t_calib[-1], ecgs['t']+3*t_end+t_calib[-1]]) + t_padding
                scale = 0.8
                fig_size = [numpy.ceil(full_t[-1]+t_padding)*0.5/0.2*scale,6*scale]
                fig = plt.figure(tight_layout=True, figsize=fig_size)
                gs = GridSpec(1,1)
                ax = fig.add_subplot(gs[0,0])
                # Bottom row: III, aVF, V3, V6
                bottom_V = numpy.concatenate([v_calib, ecgs['III']/ecgs['max_limb_leads'], ecgs['aVF']/ecgs['max_limb_leads'], ecgs['V3']/ecgs['max_precord_leads'], ecgs['V6']/ecgs['max_precord_leads']])
                ax.plot(full_t, bottom_V, 'k')

                # Middle row: II, aVL, V2, V5
                offset = 2
                midrow_V = numpy.concatenate([v_calib+offset, ecgs['II']/ecgs['max_limb_leads']+offset, ecgs['aVL']/ecgs['max_limb_leads']+offset, ecgs['V2']/ecgs['max_precord_leads']+offset, ecgs['V5']/ecgs['max_precord_leads']+offset])
                ax.plot(full_t, midrow_V, 'k')

                # Top row: I, aVR, V1, V4
                toprow_V = numpy.concatenate([v_calib+offset*2, ecgs['I']/ecgs['max_limb_leads']+offset*2, ecgs['aVR']/ecgs['max_limb_leads']+offset*2, ecgs['V1']/ecgs['max_precord_leads']+offset*2, ecgs['V4']/ecgs['max_precord_leads']+offset*2])
                ax.plot(full_t, toprow_V, 'k')

                # Add ECG red grid
                self._set_full_ecg_ticks(ax, full_t[-1]+t_padding*2, offset*2 + 1 + v_padding, -1-v_padding, self.CL)
                ax.set_xlim([0, full_t[-1]+t_padding*2])
                ax.set_ylim([-1-v_padding, offset*2 + 1 +v_padding])

                # Label the leads
                label_fontsize = 'xx-small'
                label_t_offset = t_calib[-1] + t_padding
                label_v_offset = 0.1
                plt.text(0+label_t_offset, -1+label_v_offset, 'III', fontsize=label_fontsize)
                plt.text(t_end+label_t_offset, -1+label_v_offset, 'aVF', fontsize=label_fontsize)
                plt.text(2*t_end+label_t_offset, -1+label_v_offset, 'V3', fontsize=label_fontsize)
                plt.text(3*t_end+label_t_offset, -1+label_v_offset, 'V6', fontsize=label_fontsize)
                plt.text(0+label_t_offset, -1+offset+label_v_offset, 'II', fontsize=label_fontsize)
                plt.text(t_end+label_t_offset, -1+offset+label_v_offset, 'aVL', fontsize=label_fontsize)
                plt.text(2*t_end+label_t_offset, -1+offset+label_v_offset, 'V2', fontsize=label_fontsize)
                plt.text(3*t_end+label_t_offset, -1+offset+label_v_offset, 'V5', fontsize=label_fontsize)
                plt.text(0+label_t_offset, -1+offset*2+label_v_offset, 'I', fontsize=label_fontsize)
                plt.text(t_end+label_t_offset, -1+offset*2+label_v_offset, 'aVR', fontsize=label_fontsize)
                plt.text(2*t_end+label_t_offset, -1+offset*2+label_v_offset, 'V1', fontsize=label_fontsize)
                plt.text(3*t_end+label_t_offset, -1+offset*2+label_v_offset, 'V4', fontsize=label_fontsize)
                plt.savefig('full_ecg_all_beats.png')
                if show:
                    plt.show()



    def plot_pv_signal(self, pvs, signal_name, filename, show, beat=0, pvs2=[]):
        matplotlib.rcParams.update({'font.size':'28'})
        matplotlib.rcParams.update({'text.color':'black'})
        matplotlib.rcParams.update({'lines.linewidth':'3'})
        if pvs2:
            if beat > 0:
                if (signal_name == 'p') | (signal_name == 'v'):
                    self._plot_quadruple(self.pv_fig_size,
                                pvs['ts'][beat-1], pvs[signal_name+'ls'][beat-1],'b--',
                                pvs['ts'][beat-1], pvs[signal_name+'rs'][beat-1],'g--',
                                pvs2['ts'][beat-1], pvs2[signal_name+'ls'][beat-1],'b-',
                                pvs2['ts'][beat-1], pvs2[signal_name+'rs'][beat-1],'g-',
                                'Time (s)',pvs[signal_name+'label'], filename+'_beat'+str(beat)+'_compare.png', show)
                elif (signal_name == 'pv'):
                    self._plot_quadruple(self.pv_fig_size,
                                pvs['vls'][beat-1],pvs['pls'][beat-1]*7.5,'b--',
                                pvs['vrs'][beat-1],pvs['prs'][beat-1]*7.5,'g--',
                                pvs2['vls'][beat-1],pvs2['pls'][beat-1]*7.5,'b-',
                                pvs2['vrs'][beat-1],pvs2['prs'][beat-1]*7.5,'g-',
                                'Volume (mL)', 'Pressure (mmHg)',
                                filename+'_beat'+str(beat)+'_compare.png', show)
            else:
                if (signal_name == 'p') | (signal_name == 'v'):
                    figsize=[self.pv_fig_size[0]*(int(pvs['t'][-1]/1.0)+1), self.pv_fig_size[1]]
                    self._plot_quadruple(figsize,
                                    pvs['t'], pvs[signal_name+'l'],'b--',
                                    pvs['t'], pvs[signal_name+'r'],'g--',
                                    pvs2['t'], pvs2[signal_name+'l'],'b-',
                                    pvs2['t'], pvs2[signal_name+'r'],'g-','Time (s)',
                                    pvs[signal_name+'label'], filename+'_full_compare.png', show)
                elif (signal_name == 'pv'):
                    self._plot_quadruple(self.pv_fig_size,
                                    pvs['vl'], pvs['pl']*7.5,'b--',
                                    pvs['vr'], pvs['pr']*7.5, 'g--',
                                    pvs2['vl'], pvs2['pl']*7.5,'b-',
                                    pvs2['vr'], pvs2['pr']*7.5, 'g-',
                                    'Volume (mL)','Pressure (mmHg)', filename+'_full_compare.png', show)
        else:
            if beat > 0:
                if (signal_name == 'p') | (signal_name == 'v'):
                    self._plot_double(self.pv_fig_size,
                                pvs['ts'][beat-1],pvs[signal_name+'ls'][beat-1],'b-',
                                pvs['ts'][beat-1],pvs[signal_name+'rs'][beat-1],'g-',
                                'Time (s)',pvs[signal_name+'label'], filename+'_beat'+str(beat)+'.png',show)
                elif (signal_name == 'pv'):
                    self._plot_double(self.pv_fig_size,
                                pvs['vls'][beat-1],pvs['pls'][beat-1], 'b-',
                                pvs['vrs'][beat-1],pvs['prs'][beat-1], 'g-',
                                'Volume (mL)', 'Pressure (kPa)',
                                filename+'_beat'+str(beat)+'.png',show)
            else:
                if (signal_name == 'p') | (signal_name == 'v'):
                    figsize=[self.pv_fig_size[0]*(int(pvs['t'][-1]/1.0)+1), self.pv_fig_size[1]]
                    self._plot_double(figsize, pvs['t'], pvs[signal_name+'l'],'b-',
                                    pvs['t'], pvs[signal_name+'r'],'g-',
                                    'Time (s)',pvs[signal_name+'label'], filename+'_full.png',show)
                elif (signal_name == 'pv'):
                    self._plot_double(self.pv_fig_size, pvs['vl'], pvs['pl'],'b-',
                                    pvs['vr'], pvs['pr'], 'r-',
                                    'Volume (mL)','Pressure (kPa)', filename+'_full.png',show)

    def _plot_single(self, fig_size, x, y, xlabel, ylabel, filename, show, ecg_grid=False):
        fig = plt.figure(tight_layout=True, figsize=fig_size)
        gs = GridSpec(1,1)
        ax = fig.add_subplot(gs[0,0])
        ax.plot(x, y)
        ax.set_xlabel(xlabel)
        ax.set_ylabel(ylabel)
        if ecg_grid:
            print('setting ecg grids')
            self._set_ecg_ticks(ax, x[-1], self.CL)
            ax.spines['top'].set_visible(False)
            ax.spines['right'].set_visible(False)
            ax.spines['bottom'].set_visible(False)
            ax.spines['left'].set_visible(False)
            ax.tick_params(left=False, right=False, top=False, bottom=False, labelleft=False, labelbottom=False)
            ax.set_xlim([0, numpy.ceil(x[-1]/self.CL)*CL])
        if show:
            plt.show(block=True)
        plt.savefig(filename)

    def _plot_double(self, fig_size, x, y, linestyle, x1, y1, linestyle1, xlabel, ylabel, filename, show, ecg_grid=False):
        fig = plt.figure(tight_layout=True, figsize=fig_size)
        gs = GridSpec(1,1)
        ax = fig.add_subplot(gs[0,0])
        ax.plot(x, y, linestyle, x1, y1, linestyle1)
        ax.set_xlabel(xlabel)
        ax.set_ylabel(ylabel)
        if ecg_grid:
            self._set_ecg_ticks(ax, x[-1],self.CL)
        if show:
            plt.show(block=True)
        plt.savefig(filename)

    def _plot_quadruple(self, fig_size, x, y, linestyle,  x1, y1,linestyle1, x2, y2, linestyle2, x3, y3,linestyle3, xlabel, ylabel, filename, show, ecg_grid=False):
        fig = plt.figure(tight_layout=True, figsize=fig_size)
        gs = GridSpec(1,1)
        ax = fig.add_subplot(gs[0,0])
        ax.plot(x, y, linestyle,x1, y1,linestyle1, x2, y2, linestyle2, x3, y3,linestyle3)
        ax.set_xlabel(xlabel)
        ax.set_ylabel(ylabel)
        ax.set_ylim([0,120])
        self._set_pv_ticks(ax)
        if ecg_grid:
            self._set_ecg_ticks(ax, x[-1], self.CL)
        if show:
            plt.show(block=True)
        plt.savefig(filename)

    def read_single_cell(self, dir, material):
        # Read membrane potentials
        filename = dir+'torord_ode.m'+str(material)+'c3.csv'
        with open(filename, 'r') as f:
            data = f.readlines()
        data = data[1:]
        epi = numpy.zeros((len(data),5))
        for i in range(0, len(epi)): epi[i,1] = float(data[i].split()[2])*1000000.0 # Calcium transient
        for i in range(0, len(epi)): epi[i,2] = float(data[i].split()[3]) # Membrane potential
        for i in range(0, len(epi)): epi[i,0] = float(data[i].split()[0])/1000.0 # real time
        for i in range(0, len(epi)): epi[i,3] = float(data[i].split()[1])/1000.0 # each beat time
        for i in range(0, len(epi)): epi[i,4] = float(data[i].split()[4]) # Ta

        filename = dir+'torord_ode.m'+str(material)+'c2.csv'
        with open(filename, 'r') as f:
            data = f.readlines()
        data = data[1:]
        mid = numpy.zeros((len(data),5))
        for i in range(0, len(mid)): mid[i,1] = float(data[i].split()[2])*1000000.0 # Calcium transient
        for i in range(0, len(mid)): mid[i,2] = float(data[i].split()[3]) # Membrane potential
        for i in range(0, len(mid)): mid[i,0] = float(data[i].split()[0])/1000.0 # real time
        for i in range(0, len(mid)): mid[i,3] = float(data[i].split()[1])/1000.0 # each beat time
        for i in range(0, len(mid)): mid[i,4] = float(data[i].split()[4]) # Ta

        filename = dir+'torord_ode.m'+str(material)+'c1.csv'
        with open(filename, 'r') as f:
            data = f.readlines()
        data = data[1:]
        endo = numpy.zeros((len(data),5))
        for i in range(0, len(endo)): endo[i,1] = float(data[i].split()[2])*1000000.0 # Calcium transient
        for i in range(0, len(endo)): endo[i,2] = float(data[i].split()[3]) # Membrane potential
        for i in range(0, len(endo)): endo[i,0] = float(data[i].split()[0])/1000.0 # real time
        for i in range(0, len(endo)): endo[i,3] = float(data[i].split()[1])/1000.0 # each beat time
        for i in range(0, len(endo)): endo[i,4] = float(data[i].split()[4]) # Ta
        return epi, mid, endo

    def plot_single_cell(self, epi, mid, endo, material,analysis, show, epi2=[], mid2=[], endo2=[]):
        # Set up subplots
        fig = plt.figure(tight_layout=True, figsize=[15,5])
        gs = GridSpec(3,7)
        ax1 = fig.add_subplot(gs[0,0])
        ax2 = fig.add_subplot(gs[1,0])
        ax3 = fig.add_subplot(gs[2,0])
        ax4 = fig.add_subplot(gs[:,1:3])
        ax5 = fig.add_subplot(gs[:,3:5])
        ax6 = fig.add_subplot(gs[:,5:7])

        if len(epi2)>0:
            ax1.plot(epi[:,0], epi[:,2], mid[:,0], mid[:,2], endo[:,0], endo[:,2],
                    epi2[:,0], epi2[:,2],'--', mid2[:,0], mid2[:,2],'--', endo2[:,0], endo2[:,2], '--')
            ax2.plot(epi[:,0], epi[:,1],mid[:,0],mid[:,1],endo[:,0],endo[:,1],
                    epi2[:,0], epi2[:,1],'--',mid2[:,0],mid2[:,1],'--',endo2[:,0],endo2[:,1],'--')
            ax3.plot(epi[:,0], epi[:,4],mid[:,0],mid[:,4],endo[:,0],endo[:,4],
                    epi2[:,0], epi2[:,4],'--',mid2[:,0],mid2[:,4],'--',endo2[:,0],endo2[:,4],'--')
            dt = epi[1,0] - epi[0,0]
            idx = int(self.CL / dt)
            t_s_epi = epi[-idx,0]
            t_s_mid = mid[-idx,0]
            t_s_endo = endo[-idx,0]
            t_s_epi2 = epi2[-idx,0]
            t_s_mid2 = mid2[-idx,0]
            t_s_endo2 = endo2[-idx,0]
            ax4.plot(epi[-idx:,0]-t_s_epi, epi[-idx:,2], mid[-idx:,0]-t_s_mid, mid[-idx:,2], endo[-idx:,0]-t_s_endo, endo[-idx:,2],
                    epi2[-idx:,0]-t_s_epi2, epi2[-idx:,2],'--', mid2[-idx:,0]-t_s_mid2, mid2[-idx:,2],'--', endo2[-idx:,0]-t_s_endo2, endo2[-idx:,2], '--')
            ax5.plot(epi[-idx:,0]-t_s_epi, epi[-idx:,1], mid[-idx:,0]-t_s_mid, mid[-idx:,1], endo[-idx:,0]-t_s_endo, endo[-idx:,1],
                    epi2[-idx:,0]-t_s_epi2, epi2[-idx:,1],'--',mid2[-idx:,0]-t_s_mid2, mid2[-idx:,1],'--', endo2[-idx:,0]-t_s_endo2, endo2[-idx:,1],'--')
            ax6.plot(epi[-idx:,0]-t_s_epi, epi[-idx:,4], mid[-idx:,0]-t_s_mid, mid[-idx:,4], endo[-idx:,0]-t_s_endo, endo[-idx:,4],
                    epi2[-idx:,0]-t_s_epi2, epi2[-idx:,4],'--',mid2[-idx:,0]-t_s_mid2, mid2[-idx:,4],'--', endo2[-idx:,0]-t_s_endo2, endo2[-idx:,4],'--')
            if analysis:
                print ('1: Diastolic calcium (nM) epi: '+str(epi[-1,1])+', mid: '+str(mid[-1,1])+', endo: '+str(endo[-1,1]))
                print ('1: Calcium amplitude (nM) epi: '+str(max(epi[-idx:,1]))+', mid: '+str(max(mid[-idx:,1]))+', endo: '+str(max(endo[-idx:,1])))
                print ('1: Diastolic active tension (kPa) epi: '+str(epi[-1,4])+', mid: '+str(mid[-1,4])+', endo: '+str(endo[-1,4]))
                print ('1: Active tension amplitude (kPa) epi: '+str(max(epi[-idx:,4]))+', mid: '+str(max(mid[-idx:,4]))+', endo: '+str(max(endo[-idx:,4])))
                print ('2: Diastolic calcium (nM) epi: '+str(epi2[-1,1])+', mid: '+str(mid2[-1,1])+', endo: '+str(endo2[-1,1]))
                print ('2: Calcium amplitude (nM) epi: '+str(max(epi2[-idx:,1]))+', mid: '+str(max(mid2[-idx:,1]))+', endo: '+str(max(endo2[-idx:,1])))
                print ('2: Diastolic active tension (kPa) epi: '+str(epi2[-1,4])+', mid: '+str(mid2[-1,4])+', endo: '+str(endo2[-1,4]))
                print ('2: Active tension amplitude (kPa) epi: '+str(max(epi2[-idx:,4]))+', mid: '+str(max(mid2[-idx:,4]))+', endo: '+str(max(endo2[-idx:,4])))
        else:
            ax1.plot(epi[:,0], epi[:,2], mid[:,0], mid[:,2], endo[:,0], endo[:,2])
            ax2.plot(epi[:,0],epi[:,1],mid[:,0],mid[:,1],endo[:,0],endo[:,1])
            ax3.plot(epi[:,0],epi[:,4],mid[:,0],mid[:,4],endo[:,0],endo[:,4])
            dt = epi[1,0] - epi[0,0]
            idx = int(self.CL / dt)
            t_s_epi = epi[-idx,0]
            t_s_mid = mid[-idx,0]
            t_s_endo = endo[-idx,0]
            ax4.plot(epi[-idx:,0]-t_s_epi, epi[-idx:,2], mid[-idx:,0]-t_s_mid, mid[-idx:,2], endo[-idx:,0]-t_s_endo, endo[-idx:,2])
            ax5.plot(epi[-idx:,0]-t_s_epi, epi[-idx:,1], mid[-idx:,0]-t_s_mid, mid[-idx:,1], endo[-idx:,0]-t_s_endo, endo[-idx:,1])
            ax6.plot(epi[-idx:,0]-t_s_epi, epi[-idx:,4], mid[-idx:,0]-t_s_mid, mid[-idx:,4], endo[-idx:,0]-t_s_endo, endo[-idx:,4])
            if analysis:
                print ('Diastolic calcium (nM) epi: {:.2f}, mid: {:.2f}, endo: {:.2f}'.format(epi[-1,1], mid[-1,1], endo[-1,1]))
                print ('Calcium amplitude (nM) epi: {:.2f}, mid: {:.2f}, endo: {:.2f}'.format(max(epi[-idx:,1]), max(mid[-idx:,1]), max(endo[-idx:,1])))
                print ('Diastolic active tension (kPa) epi: {:.2f}, mid: {:.2f}, endo: {:.2f}'.format(epi[-1,4], mid[-1,4], endo[-1,4]))
                print ('Active tension amplitude (kPa) epi: {:.2f}, mid: {:.2f}, endo: {:.2f}'.format(max(epi[-idx:,4]), max(mid[-idx:,4]), max(endo[-idx:,4])))

        ax1.set_ylabel('(mV)')
        ax1.set_xlabel('Time (s)')
        #ax1.set_title('Action potentials')
        ax1.legend(['Epi', 'Mid', 'Endo'])

        #ax2.set_ylabel('Intracellular Calcium (nM)')
        ax2.set_xlabel('Time (s)')
        ax2.set_ylabel('CaT (nM)')

        ax3.set_xlabel('Time (s)')
        ax3.set_ylabel('Active tension (kPa)')

        dt = epi[1,0] - epi[0,0]
        idx = int(0.8 / dt)

        ax4.set_ylabel('Vm (mV)')
        ax4.set_xlabel('Time (s)')
        ax4.set_ylim([-100, 40])
        ax4.grid()
        ax4.set_xticks(numpy.arange(0, self.CL+0.2, 0.2))

        ax5.set_ylabel('CaT (nM)')
        ax5.set_xlabel('Time (s)')
        ax5.set_ylim([0, 1000])
        ax5.grid()
        ax5.set_xticks(numpy.arange(0, self.CL+0.2, 0.2))

        ax6.set_ylabel('Active tension (kPa)')
        ax6.set_xlabel('Time (s)')
        ax6.set_ylim([0, 70])
        ax6.grid()
        ax6.set_xticks(numpy.arange(0, self.CL+0.2, 0.2))

        if show:
            plt.show(block=True)
        plt.savefig('single_cell_'+str(material)+'.png')


    def analysis_PV(self, pvs, beat=0):
        if beat > 0:
            EDVL = max(pvs['vls'][beat-1])
            EDVR = max(pvs['vrs'][beat-1])
            ESVL = min(pvs['vls'][beat-1])
            ESVR = min(pvs['vrs'][beat-1])
            PmaxL = int(max(pvs['pls'][beat-1]))
            PmaxR = int(max(pvs['prs'][beat-1]))
            LVEF = int((EDVL-ESVL)/EDVL*100)
            RVEF = int((EDVR-ESVR)/EDVR*100)
            SVL = int(EDVL - ESVL)
            SVR = int(EDVR - ESVR)

        else:
            LVEF = []
            RVEF = []
            EDVL = []
            EDVR = []
            ESVL = []
            ESVR = []
            PmaxL = []
            PmaxR = []
            SVL = []
            SVR = []
            for i in range(0, len(pvs['vls'])):
                EDVL.append(max(pvs['vls'][i]))
                ESVL.append(min(pvs['vls'][i]))
                LVEF.append(int((EDVL[i]-ESVL[i])/EDVL[i] * 100))
                PmaxL.append(int(max(pvs['pls'][i])))
                SVL.append(int(EDVL[i]-ESVL[i]))
                EDVR.append(max(pvs['vrs'][i]))
                ESVR.append(min(pvs['vrs'][i]))
                RVEF.append(int((EDVR[i]-ESVR[i])/EDVR[i] * 100))
                PmaxR.append(int(max(pvs['prs'][i])))
                SVR.append(int(EDVR[i]-ESVR[i]))
        print('EDVL:'+str(int(EDVL))+' mL,\tEDVR: '+str(int(EDVR))+' mL')
        print('ESVL: '+str(int(ESVL))+' mL,\tESVR: '+str(int(ESVR))+' mL')
        print('LVEF: '+str(LVEF)+' %,\tRVEF: '+str(RVEF)+' %')
        print('PmaxL: '+str(PmaxL)+' kPa,\tPmaxR: '+str(PmaxR)+' kPa')
        print('SVL: '+str(SVL)+' mL,\tSVR: '+str(SVR)+' mL')

    def analysis_ECG(self,ecgs,beat,show):
        fig = plt.figure(tight_layout=True, figsize=[11,9])
        gs = GridSpec(3,4)
        ax5 = fig.add_subplot(gs[0,0])
        ax6 = fig.add_subplot(gs[1,0])
        ax7 = fig.add_subplot(gs[2,0])
        ax8 = fig.add_subplot(gs[0,1])
        ax9 = fig.add_subplot(gs[1,1])
        ax10 = fig.add_subplot(gs[2,1])
        ax11 = fig.add_subplot(gs[0,2])
        ax12 = fig.add_subplot(gs[1,2])
        ax13 = fig.add_subplot(gs[2,2])
        ax14 = fig.add_subplot(gs[0,3])
        ax15 = fig.add_subplot(gs[1,3])
        ax16 = fig.add_subplot(gs[2,3])

        ecg_biomarkers = numpy.zeros((12,6))
        ecg_biomarkers[0, :] = self._plot_with_landmarks(ax5, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['Is'][beat-1], 'I', CL)
        ecg_biomarkers[1, :] = self._plot_with_landmarks(ax6, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['IIs'][beat-1], 'II', CL)
        ecg_biomarkers[2, :] = self._plot_with_landmarks(ax7, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['IIIs'][beat-1], 'III', CL)
        ecg_biomarkers[3, :] = self._plot_with_landmarks(ax8, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['aVRs'][beat-1], 'aVR', CL)
        ecg_biomarkers[4, :] = self._plot_with_landmarks(ax9, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['aVLs'][beat-1], 'aVL', CL)
        ecg_biomarkers[5, :] = self._plot_with_landmarks(ax10, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['aVFs'][beat-1], 'aVF', CL)
        ecg_biomarkers[6, :] = self._plot_with_landmarks(ax11, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['V1s'][beat-1], 'V1', CL)
        ecg_biomarkers[7, :] = self._plot_with_landmarks(ax12, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['V2s'][beat-1], 'V2', CL)
        ecg_biomarkers[8, :] = self._plot_with_landmarks(ax13, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['V3s'][beat-1], 'V3', CL)
        ecg_biomarkers[9, :] = self._plot_with_landmarks(ax14, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['V4s'][beat-1], 'V4', CL)
        ecg_biomarkers[10, :] = self._plot_with_landmarks(ax15, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['V5s'][beat-1], 'V5', CL)
        ecg_biomarkers[11, :] = self._plot_with_landmarks(ax16, ecgs['ts'][beat-1], ecgs['max_all_leads'], ecgs['V6s'][beat-1], 'V6', CL)

        if show:
            plt.show()
        plt.savefig('ecg_analysis.png')
        print('QRS dur, T dur, T pe, T op, T amp, QT dur')
        print (ecg_biomarkers)

        QRS_mean = numpy.average(ecg_biomarkers[:,0])
        QRS_std = numpy.std(ecg_biomarkers[:,0])
        T_dur_mean = numpy.average(ecg_biomarkers[:,1])
        T_dur_std = numpy.std(ecg_biomarkers[:,1])
        T_pe_mean = numpy.average(ecg_biomarkers[:,2])
        T_pe_std = numpy.std(ecg_biomarkers[:,2])
        T_op_mean = numpy.average(ecg_biomarkers[:,3])
        T_op_std = numpy.std(ecg_biomarkers[:,3])
        QT_dur_mean = numpy.average(ecg_biomarkers[:,5])
        QT_dur_std = numpy.std(ecg_biomarkers[:,5])
        QT_dispersion_6 = ecg_biomarkers[6:12,5].max() - ecg_biomarkers[6:12,5].min()
        QT_dispersion_12 = ecg_biomarkers[:,5].max() - ecg_biomarkers[:,5].min()
        print('QRS: {:.3f} +- {:.3f}'.format(QRS_mean, QRS_std))
        print('T duration: {:.3f} +- {:.3f}'.format(T_dur_mean, T_dur_std))
        print('T pe: {:.3f} +- {:.3f}'.format(T_pe_mean, T_pe_std))
        print('T op: {:.3f} +- {:.3f}'.format(T_op_mean, T_op_std))
        print('QT duration: {:.3f} +- {:.3f}'.format(QT_dur_mean, QT_dur_std))
        print('QT dispersion (precordial): {:.3f}'.format(QT_dispersion_6))
        print('QT dispersion (12 leads): {:.3f}'.format(QT_dispersion_12))

    def _plot_with_landmarks(self, ax, t, max_all_leads, V, name, CL):
    	ax.clear()
    	qrs_dur, t_dur, t_amp, t_ep, t_op, qt_dur, landmarks =self._measurements(V, 0.0, CL, t, 2e-5, 0.02)
    	ax.plot(t, V/max_all_leads, landmarks[:,0], landmarks[:,1]/max_all_leads, '*')
    	ax.set_title(name + ' '+str(int(qrs_dur*1000))+' '+str(int(qt_dur*1000))+'\n'+str(int(t_dur*1000))+' '+str(int(t_amp/max_all_leads)))
    	ax.set_xlim(0.0,CL)
    	ax.set_ylim(-1,1)
    	ax.set_xlabel('Time (s)')
    	ax.set_ylabel('Normalised ECG')
    	return qrs_dur, t_dur, t_amp, t_ep, t_op, qt_dur

    def _measurements(self, V, start_t, end_t, t, t_tol, v_tol):
    	idx_start = numpy.where(abs(numpy.array(t-start_t)) < t_tol)[0][0]
    	try:
    		idx_end = numpy.where(abs(numpy.array(t-end_t)) < t_tol)[0][0]
    	except:
    		idx_end = len(V)-1
    	# Offset voltage using t_end
    	n = 100
    	b = [1.0 / n] * n
    	V = V - V[idx_end]
    	V = lfilter(b,1, V)
    	dV = numpy.gradient(V)
    	dV[0:2] = 0.0 # Remove gradient artefacts

    	n = 500
    	b = [1.0 / n] * n
    	dV = lfilter(b, 1, dV)
    	dV = abs(dV)/abs(dV).max()
    	ddV = numpy.gradient(dV)
    	ddV[0:2] = 0.0 # Remove gradient artefacts
    	ddV = lfilter(b, 1, ddV)
    	ddV = abs(ddV)/abs(ddV).max()

    	TOL = v_tol

    	# Find Q start
    	for i in range(idx_start, len(V)):
    		if (dV[i] > TOL) & (i > 10):
    			break
    	q_start_idx = i
    	q_start_t = t[i]

    	# Find T end
    	for i in range(idx_end-1, idx_start, -1):
    		if (dV[i] > TOL):
    			break
    	t_end_idx = i
    	t_end_t = t[i]

    	# Find QRS end
    	window = 5000
    	dV = abs(dV)
    	max_window = numpy.zeros(t_end_idx-q_start_idx+1)
    	for i in range(q_start_idx, t_end_idx):
    		max_window[i-q_start_idx] = dV[i:(i+window)].max()
    	print('evaluated max window')
    	min = max_window[0]
    	for i in range(0, len(max_window)):
    		if (min >= max_window[i]):
    			min = max_window[i]
    		elif (min < (max_window.min()+0.05)):
    			break
    	qrs_end_idx = i + q_start_idx
    	qrs_end_t = t[qrs_end_idx]

    	qrs_dur = qrs_end_t - q_start_t

    	# Find T peak timing and amplitude
    	segment = V[qrs_end_idx:(t_end_idx+1)]
    	t_amplitude = abs(segment).max()
    	t_peak_idx = numpy.where(abs(segment) == t_amplitude)[0][0] + qrs_end_idx
    	t_sign = numpy.sign(segment[t_peak_idx-qrs_end_idx])
    	t_amplitude = t_sign * t_amplitude
    	t_peak_t = t[t_peak_idx]

    	# Find T start
    	segment = ddV[qrs_end_idx:t_peak_idx]
    	min_dd_idx = numpy.where(segment == segment.min())[0][0] + qrs_end_idx
    	for i in range(min_dd_idx, t_peak_idx):
    		if (abs(ddV[i]) > 0.0005):
    			break
    	t_start_idx = i
    	t_start_t = t[t_start_idx]

    	t_dur = t_end_t - t_start_t

    	qt_dur = t_end_t - q_start_t
    	t_pe = t_end_t - t_peak_t
    	t_op = t_peak_t - t_start_t

    	landmarks = numpy.array([[q_start_t, V[q_start_idx]], [qrs_end_t, V[qrs_end_idx]], [t_start_t, V[t_start_idx]], [t_peak_t, V[t_peak_idx]], [t_end_t, V[t_end_idx]]])
    	return qrs_dur, t_dur, t_pe, t_op, t_amplitude, qt_dur, landmarks


if __name__=='__main__':
    false = ['False', 'false', 'F', 'f']
    true = ['True', 'true', 'T', 't']
    parser = argparse.ArgumentParser()
    parser.add_argument("--type", help='Either ecg or pv, or single cell')
    parser.add_argument("--name", help='Name of simulation', default='heart_remeshed_3D')
    parser.add_argument("--refresh", help='Delete previous reading of ECG and PV', default=False)
    parser.add_argument("--lead", help='Name of the signal lead to plot')
    parser.add_argument("--CL", help='Cycle length of simulation, (s)',default=0.8)
    parser.add_argument("--beat", help='Beat number for single beat plots', default=0)
    parser.add_argument("--figure_title", help='Title of the figure', default = '')
    parser.add_argument("--analysis", help='Title of the figure', default =False)
    parser.add_argument("--compare", help='Directory to compare with', default='')
    parser.add_argument("--material", help='Directory to compare with', default=1)
    parser.add_argument("--show", help='Toggle whether to show plot', default=True)
    args = parser.parse_args()
    plot_type = args.type
    name = args.name
    refresh = bool(args.refresh)
    if refresh in true:
        os.system('rm ecgs.pl pvs.pl')
    lead_name = args.lead
    CL = float(args.CL)
    beat = int(args.beat)
    figure_title = args.figure_title
    analysis = args.analysis
    compare = args.compare
    material = int(args.material)
    show = args.show

    if show in false:
        show = False
    if analysis in true:
        analysis = True

    # Set up visualisation
    a = ECGPV_visualisation(CL)
    if not (plot_type=='cell'):
        ecgs, pvs = a.read_ecg_pv(name, './')
    epi, mid, endo = a.read_single_cell('./', material)
    if compare:
        ecgs2, pvs2 = a.read_ecg_pv(name, compare)
        epi2, mid2, endo2 = a.read_single_cell(compare, material)
        if analysis:
            if (plot_type=='live')|(plot_type=='p')|(plot_type=='v')|(plot_type=='pv'):
                a.analysis_PV(pvs, beat)
                print('second one...')
                a.analysis_PV(pvs2, beat)
            elif (plot_type=='ecg'):
                a.analysis_ECG(ecgs,beat,show)
                a.analysis_ECG(ecgs2,beat,show)
        if plot_type == 'live':
            a.plot_ecgpv_live(ecgs, pvs, figure_title, show, ecgs2, pvs2)
        elif plot_type == 'ecg':
            if lead_name:
                a.plot_ecg_lead(ecgs, lead_name, lead_name, show, beat, ecgs2)
            else:
                a.plot_ecg_all_leads(ecgs, show, beat, ecgs2)
        elif plot_type == 'p':
            a.plot_pv_signal(pvs, 'p', 'Pt', show, beat, pvs2)
        elif plot_type == 'v':
            a.plot_pv_signal(pvs, 'v', 'Vt', show, beat, pvs2)
        elif plot_type == 'pv':
            a.plot_pv_signal(pvs, 'pv', 'PV_loop', show, beat, pvs2)
        elif plot_type == 'cell':
            a.plot_single_cell(epi, mid, endo, material,analysis, show, epi2, mid2, endo2)
    else:
        if analysis:
            if (plot_type=='live')|(plot_type=='p')|(plot_type=='v')|(plot_type=='pv'):
                a.analysis_PV(pvs, beat)
            elif (plot_type=='ecg'):
                a.analysis_ECG(ecgs,beat,show)
        if plot_type == 'live':
            a.plot_ecgpv_live(ecgs, pvs, figure_title, show)
        elif plot_type == 'ecg':
            if lead_name:
                assert lead_name, 'Please enter lead name using option: lead='
                a.plot_ecg_lead(ecgs, lead_name, lead_name, show, beat)
            else:
                a.plot_ecg_all_leads(ecgs, show, beat)
        elif plot_type == 'p':
            a.plot_pv_signal(pvs, 'p', 'Pt', show, beat)
        elif plot_type == 'v':
            a.plot_pv_signal(pvs, 'v', 'Vt', show, beat)
        elif plot_type == 'pv':
            a.plot_pv_signal(pvs,'pv', 'PV_loop', show, beat)
        elif plot_type == 'cell':
            a.plot_single_cell(epi, mid, endo, material, analysis, show)

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API

back to top