"""
VanOsta2023 model.
"""
from circadapt.model import Model
from circadapt.adapt import ModelAdapt
from circadapt.plot import triseg2022
import matplotlib.pyplot as plt
import numpy as np
import warnings
colors = {
'Lv': None, #[213/255, 96/255, 98/255],
'Sv': None, #[236/255, 195/255, 11/255],
'Rv': None, #[6/255, 123/255, 194/255],
'Ra': None, #[27/255, 231/255, 255/255],
'La': None, #[243/255, 119/255, 72/255],
'PuArt': None, #'k',
'SyArt': None, #'k',
'RaRv': None, #'k',
'RvPuArt': None, #'k',
'LaLv': None, #'k',
'LvSyArt': None, #'k',
}
# colors = {
# 'Lv': [213/255, 96/255, 98/255],
# 'Sv': [236/255, 195/255, 11/255],
# 'Rv': [6/255, 123/255, 194/255],
# 'Ra': [27/255, 231/255, 255/255],
# 'La': [243/255, 119/255, 72/255],
# 'PuArt': 'k',
# 'SyArt': 'k',
# 'RaRv': 'k',
# 'RvPuArt': 'k',
# 'LaLv': 'k',
# 'LvSyArt': 'k',
# }
[docs]
class VanOsta2023(Model, ModelAdapt):
def __init__(self,
solver: str = None,
path_to_circadapt: str = None,
model_state: dict = None,
):
# warnings.warn('VanOsta2023 uses old structure. Use VanOsta2024.')
if solver is None:
solver = 'backward_differential'
self._local_save_reference = True
ModelAdapt.__init__(self)
Model.__init__(self,
solver,
path_to_circadapt=path_to_circadapt,
model_state=model_state,
)
def _update_settings(self):
# rename model components for easy input/output
self._module_rename['Wall2022'] = 'Wall'
self._module_rename['Patch2022'] = 'Patch'
self._module_rename['Chamber2022'] = 'Chamber'
self._module_rename['Valve2022'] = 'Valve'
self._module_rename['TriSeg2022'] = 'TriSeg'
self._module_rename['PressureFlowControl'] = 'PFC'
[docs]
def build(self):
# Circulation
self.add_smart_component('ArtVen', build='SystemicCirculation')
self.add_smart_component('ArtVen', build='PulmonaryCirculation')
self.add_smart_component('Heart', patch_type='Patch2022')
self.add_smart_component('Timings')
self.add_smart_component('PressureFlowControl')
# manually set papillary muscles
self.set_component("Peri.RaRv.wPapMus", "Peri.TriSeg.wRv")
self.set_component("Peri.LaLv.wPapMus", "Peri.TriSeg.wLv")
[docs]
def set_reference(self):
self['Chamber']['buckling'] = False
self['Valve']['soft_closure'] = True
self['Valve']['papillary_muscles'] = True
self['Solver']['dt'] = 0.001
self['Solver']['dt_export'] = 0.002
self.set('Solver.order', 2)
self.set('Model.t_cycle', 0.85)
self.set('Model.PFC.fac', 0.5)
self.set('Model.PFC.epsilon', 0.1)
self.set('Model.PFC.fac_pfc', 1)
self.set('Model.PFC.stable_threshold', 0.001)
# Set parameters Circulation
self['Tube0D']['A0'] = [0.00049828, 0.00049899, 0.00046929, 0.00050574]
self['Tube0D']['A_wall'] = [1.13187792e-04, 3.72289316e-05, 7.27489286e-05, 4.31617949e-05]
self['Tube0D']['k'] = [1.66666667, 2.33333333, 1.66666667, 2.33333333]
self['Tube0D']['l'] = [0.4, 0.4, 0.19, 0.19]
self['Tube0D']['p0'] = [12155.25602777, 297.44066943, 1912.6684696 , 580.51425824]
self['ArtVen']['p0'] = [6299.86481149, 1000. ]
self['ArtVen']['q0'] = [4.5e-05, 4.5e-05]
self['ArtVen']['k'] = [1. , 1.88]
# Set volume state variables
# not needed to reset, only if initial model crashes
if True:
self.set('Model.SyArt.V', 2e-4)
self.set('Model.PuArt.V', 1e-4)
self.set('Model.SyVen.V', 4e-4)
self.set('Model.PuVen.V', 2e-4)
self.set('Model.Peri.TriSeg.V', 56e-6)
self.set('Model.Peri.TriSeg.Y', 36e-3)
self.set('Model.Peri.TriSeg.cLv.V', 150e-6)
self.set('Model.Peri.TriSeg.cRv.V', 110e-6)
self.set('Model.Peri.La.V', 25e-6)
self.set('Model.Peri.Ra.V', 150e-6)
# all wall parameters
self['Patch']['l_se0'] = 0.04
self['Patch']['l_s_ref'] = 2.0
self['Patch']['l_s0'] = [1.79, 1.79, 1.8 , 1.8 , 1.8 ]
self['Patch']['dl_s_pas'] = 0.6
self['Patch']['k1'] = 10
self['Patch']['dt'] = 0
self['Patch']['C_rest'] = 0
self['Patch']['l_si0'] = 1.51
self['Patch']['LDAD'] = [1.057, 1.057, 0.74 , 0.74 , 0.74 ]
self['Patch']['ADO'] = [0.081, 0.081, 0.75 , 0.75 , 0.75 ]
self['Patch']['LDCC'] = [4. , 4. , 3., 3., 3.]
self['Patch']['v_max'] = 7.
self['Patch']['v_max'][:2] = 14.
self['Patch']['tr'] = [0.4 , 0.4 , 0.21, 0.21, 0.21]
self['Patch']['td'] = [0.4 , 0.4 , 0.24, 0.24, 0.24]
# wall specific
self['Patch']['Sf_act'] = [100000., 100000., 120000., 120000., 120000.]
self['Patch']['Am_ref'] = [0.00400721, 0.00311263, 0.00918281, 0.00518683, 0.01321999]
self['Patch']['V_wall'] = [1.59935455e-05, 7.72140495e-06, 1.16441768e-04, 4.51241560e-05,
6.88196851e-05]
self['Patch']['Sf_pas'] = [ 9.51064014, 10.3647024 , 424.3537027 , 417.74300178,
439.21574206]
# Valves
self['Valve']['adaptation_A_open_fac'] = [1. , 1.12, 1. , 1. , 1.12, 1. ]
self['Valve']['A_open'] = [0.00050044, 0.00052598, 0.00046962, 0.00050745, 0.0005583 ,
0.00049849]
self['Valve']['A_leak'] = [2.64705882e-04, 2.64705882e-10, 2.64705882e-10, 2.64705882e-04,
2.64705882e-10, 2.64705882e-10]
self['Valve']['l'] = 0.01626978
self['Valve']['rho_b'] = 1050
self['Valve']['papillary_muscles'] = True
self['Valve']['papillary_muscles_slope'] = 100
self['Valve']['papillary_muscles_min'] = 0.1
self['Valve']['papillary_muscles_A_open_fac'] = 0.1
self['Valve']['soft_closure'] = True
self['Bag']['k'] = 10
self['Bag']['V_ref'] = [0.00057937]
self['Bag']['p_ref'] = 100
self.set('Model.Peri.TriSeg.Y', 0.035)
self['Timings']['law_tau_av'] = 2
self['Timings']['c_tau_av0'] = 0.189
self['Timings']['c_tau_av1'] = -0.029
# Adaptation
self['Patch']['adapt_gamma'] = 0.5
self.set('Solver.store_beats', 1)
self['PFC']['stable_threshold'] = 1e-3
self['Patch']['SfPasMaxT'] = [3600., 3600., 4200., 4200., 4200.]
self['Patch']['FacSfActT'] = [0.28, 0.28, 0.69, 0.69, 0.69]
self['Patch']['SfPasActT'] = [2800., 2800., 6600., 6600., 6600.]
self['Patch']['LsPasActT'] = [3. , 3. , 2.31, 2.31, 2.31]
self.run(stable=True)
return
[docs]
def get_unittest_targets(self):
"""Hardcoded results after initializing and running 1 beat."""
return {
'LVEDV': 109.1,
'LVESV': 36.6,
}
[docs]
def get_unittest_results(self, model):
"""Real-time results after initializing and running 1 beat."""
LVEDV = np.max(model['Cavity']['V'][:, 'cLv'])*1e6
LVESV = np.min(model['Cavity']['V'][:, 'cLv'])*1e6
return {
'LVEDV': LVEDV,
'LVESV': LVESV,
}
[docs]
def plot(self, fig=None):
# TODO
self.plot_extended(fig)
[docs]
def plot_extended(self, fig=None):
if fig is None:
fig = 1
if isinstance(fig, int):
fig = plt.figure(fig, clear=True, figsize=(12, 8))
# Settings
grid_size = [32, 32]
def get_lim(module, signal, locs=slice(None, None, None)):
signal = self[module][signal][:, locs]
lim = np.array([np.min(signal), np.max(signal)])
lim += np.array([-1, 1]) * 0.1*np.diff(lim)
return lim
lim_V = get_lim('Cavity', 'V', ['cRv', 'Ra', 'La', 'cLv']) * 1e6
lim_V[0] = 0
lim_p = get_lim('Cavity', 'p', ['cRv', 'Ra', 'La', 'cLv']) / 133
lim_p[0] = np.min([lim_p[0], 0])
lim_Ls = get_lim('Patch', 'l_s')
lim_Sf = get_lim('Patch', 'Sf') * 1e-3
lim_q = get_lim('Valve', 'q',
['LaLv', 'RaRv', 'LvSyArt', 'RaPuArt']) * 1e6
all_lim = [lim_V, lim_p, lim_Ls, lim_Sf, lim_q]
if (np.any(np.isnan(all_lim)) or np.any(np.isinf(all_lim))):
lim_V = [0, 200]
lim_p = [0, 150]
lim_Ls = [1.5, 2.0]
lim_Sf = [0, 100]
lim_q = [-1e-3, 1e-3]
# Pressure Volume plot
axPV = plt.subplot2grid(grid_size, (0, 17), rowspan=15, colspan=15, fig=fig)
axPV.plot(self['Cavity']['V'][:, 'cLv']*1e6,
self['Cavity']['p'][:, 'cLv']/133,
c=colors['Lv'],
)
axPV.plot(self['Cavity']['V'][:, 'cRv']*1e6,
self['Cavity']['p'][:, 'cRv']/133,
c=colors['Rv'],
)
axPV.plot(self['Cavity']['V'][:, 'La']*1e6,
self['Cavity']['p'][:, 'La']/133,
c=colors['La'],
)
axPV.plot(self['Cavity']['V'][:, 'Ra']*1e6,
self['Cavity']['p'][:, 'Ra']/133,
c=colors['Ra'],
)
axPV.spines[['top', 'right']].set_visible(False)
axPV.set_title('Pressure-Volume loop', weight='bold')
axPV.set_xlabel('Volume [mL]')
axPV.set_ylabel('Pressure [mmHg]')
axPV.spines[['bottom', 'left']].set_position(('outward', 5))
ylabel_x_left = -0.25
ylabel_x_right = 1.25
# Volumes
t = self['Solver']['t']*1e3
axVRv = plt.subplot2grid(grid_size, (0, 0), rowspan=8, colspan=6, fig=fig)
axVRv.plot(t, self['Cavity']['V'][:, 'cRv']*1e6,
c=colors['Rv'],
)
axVRv.plot(t, self['Cavity']['V'][:, 'Ra']*1e6,
c=colors['Ra'],
)
axVRv.set_ylim(lim_V)
axVRv.set_ylabel('Volume\n[mL]')
axVRv.spines[['top', 'right']].set_visible(False)
axVRv.set_title('Right Heart')
# axVRv.set_xticks([])
axVRv.tick_params(axis='both', direction='in')
axVRv.yaxis.set_label_coords(ylabel_x_left, 0.5)
axVLv = plt.subplot2grid(grid_size, (0, 6), rowspan=8, colspan=6, fig=fig)
axVLv.plot(t, self['Cavity']['V'][:, 'cLv']*1e6,
c=colors['Lv'],
)
axVLv.plot(t, self['Cavity']['V'][:, 'La']*1e6,
c=colors['La'],
)
axVLv.set_ylabel('Volume\n[mL]')
axVLv.set_ylim(lim_V)
axVLv.yaxis.set_ticks_position('right')
axVLv.yaxis.set_label_position('right')
axVLv.spines['right'].set_position(('outward', 0))
axVLv.spines[['top', 'left']].set_visible(False)
axVLv.set_title('Left Heart')
# axVLv.set_xticks([])
axVLv.tick_params(axis='both', direction='in')
axVLv.yaxis.set_label_coords(ylabel_x_right, 0.5)
# Pressures
axpRv = plt.subplot2grid(grid_size, (8, 0), rowspan=8, colspan=6, fig=fig)
axpRv.plot(t, self['Cavity']['p'][:, 'cRv']/133,
c=colors['Rv'],
)
axpRv.plot(t, self['Cavity']['p'][:, 'Ra']/133,
c=colors['Ra'],
)
axpRv.plot(t, self['Cavity']['p'][:, 'PuArt']/133,
c=colors['PuArt'],
)
axpRv.spines[['top', 'right']].set_visible(False)
# axpRv.set_xticks([])
axpRv.tick_params(axis='both', direction='in')
axpRv.set_ylim(lim_p)
axpRv.set_ylabel('Pressure\n[mmHg]')
axpRv.yaxis.set_label_coords(ylabel_x_left, 0.5)
axpLv = plt.subplot2grid(grid_size, (8, 6), rowspan=8, colspan=6, fig=fig)
axpLv.plot(t, self['Cavity']['p'][:, 'cLv']/133,
c=colors['Lv'],
)
axpLv.plot(t, self['Cavity']['p'][:, 'La']/133,
c=colors['La'],
)
axpLv.plot(t, self['Cavity']['p'][:, 'SyArt']/133,
c=colors['SyArt'],
)
axpLv.yaxis.set_ticks_position('right')
axpLv.yaxis.set_label_position('right')
axpLv.spines['right'].set_position(('outward', 0))
axpLv.spines[['top', 'left']].set_visible(False)
# axpLv.set_xticks([])
axpLv.tick_params(axis='both', direction='in')
axpLv.set_ylim(lim_p)
axpLv.set_ylabel('Pressure\n[mmHg]')
axpLv.yaxis.set_label_coords(ylabel_x_right, 0.5)
# Valves
ax = plt.subplot2grid(grid_size, (16, 0), rowspan=6, colspan=6, fig=fig)
ax.plot(t, self['Valve']['q'][:, 'RaRv']*1e6,
c=colors['RaRv'],
)
ax.plot(t, self['Valve']['q'][:, 'RvPuArt']*1e6,
c=colors['RvPuArt'],
)
ax.spines[['top', 'right']].set_visible(False)
ax.set_ylim(lim_q)
# ax.set_xticks([])
ax.set_ylabel('Flow\n[mL/s]')
ax.yaxis.set_label_coords(ylabel_x_left, 0.5)
ax = plt.subplot2grid(grid_size, (16, 6), rowspan=6, colspan=6, fig=fig)
ax.plot(t, self['Valve']['q'][:, 'LaLv']*1e6,
c=colors['LaLv'],
)
ax.plot(t, self['Valve']['q'][:, 'LvSyArt']*1e6,
c=colors['LvSyArt'],
)
ax.spines[['top', 'left']].set_visible(False)
ax.set_ylim(lim_q)
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
# ax.set_xticks([])
ax.set_ylabel('Flow\n[mL/s]')
ax.yaxis.set_label_coords(ylabel_x_right, 0.5)
# Stress
ax = plt.subplot2grid(grid_size, (22, 0), rowspan=4, colspan=6, fig=fig)
ax.plot(t, self['Patch']['Sf'][:, 'pRv0']*1e-3,
c=colors['Rv'],
)
ax.plot(t, self['Patch']['Sf'][:, 'pRa0']*1e-3,
c=colors['Ra'],
)
ax.spines[['top', 'right']].set_visible(False)
# ax.set_xticks([])
ax.set_ylim(lim_Sf)
ax.set_ylabel('Total\nstress [kPa]')
ax.yaxis.set_label_coords(ylabel_x_left, 0.5)
ax = plt.subplot2grid(grid_size, (22, 6), rowspan=4, colspan=6, fig=fig)
ax.plot(t, self['Patch']['Sf'][:, 'pLv0']*1e-3,
c=colors['Lv'],
)
ax.plot(t, self['Patch']['Sf'][:, 'pSv0']*1e-3,
c=colors['Sv'],
)
ax.plot(t, self['Patch']['Sf'][:, 'pLa0']*1e-3,
c=colors['La'],
)
ax.spines[['top', 'left']].set_visible(False)
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
# ax.set_xticks([])
ax.set_ylim(lim_Sf)
ax.set_ylabel('Total\nstress [kPa]')
ax.yaxis.set_label_coords(ylabel_x_right, 0.5)
# Sarcomere Length
ax = plt.subplot2grid(grid_size, (26, 0), rowspan=6, colspan=6, fig=fig)
ax.plot(t, self['Patch']['l_s'][:, 'pRv0'],
c=colors['Rv'],
)
ax.plot(t, self['Patch']['l_s'][:, 'pRa0'],
c=colors['Ra'],
)
ax.spines[['top', 'right']].set_visible(False)
ax.spines['bottom'].set_position(('outward', 5))
ax.set_ylim(lim_Ls)
ax.set_ylabel('Sarcomere\nlength [$\mu$m]')
ax.yaxis.set_label_coords(ylabel_x_left, 0.5)
ax = plt.subplot2grid(grid_size, (26, 6), rowspan=6, colspan=6, fig=fig)
ax.plot(t, self['Patch']['l_s'][:, 'pLv0'],
c=colors['Lv'],
)
ax.plot(t, self['Patch']['l_s'][:, 'pSv0'],
c=colors['Sv'],
)
ax.plot(t, self['Patch']['l_s'][:, 'pLa0'],
c=colors['La'],
)
ax.spines[['top', 'left']].set_visible(False)
ax.spines['bottom'].set_position(('outward', 5))
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
ax.set_ylim(lim_Ls)
ax.set_ylabel('Sarcomere\nlength [$\mu$m]')
ax.yaxis.set_label_coords(ylabel_x_right, 0.5)
# ax.set_xlabel('Time [ms]')
# ax.xaxis.set_label_coords(0, -0.3)
# Plot TriSeg
titles = ['Pre-A', 'Onset QRS', 'Peak LV \n pressure', 'AV close']
idx = [0,
np.argmax(np.diff(self['Patch']['C'][:, 'pLv0'])>0),
np.argmax(self['Cavity']['p'][:, 'cLv']),
len(t) - 1 - np.argmax(
np.diff(self['Valve']['q'][:, 'LvSyArt'][::-1])>0)
]
for i in range(4):
ax = plt.subplot2grid(grid_size, (26, 16+4*i),
rowspan=5, colspan=4, fig=fig)
triseg2022(self, ax, idx[i],
colors=[colors['Lv'], colors['Sv'], colors['Rv']])
ax.spines[['top', 'right', 'bottom', 'left']].set_visible(False)
ax.set_xticks([])
ax.set_yticks([])
plt.xlabel(titles[i], fontsize=12)
# Plot settings
plt.subplots_adjust(
top=0.96,
bottom=0.05,
left=0.075,
right=0.98,
hspace=5,
wspace=0.5)
plt.draw()
# Stress strain
ax_right_stress_strain = plt.subplot2grid(grid_size, (18, 17),
rowspan=7, colspan=7, fig=fig)
ax_left_stress_strain = plt.subplot2grid(grid_size, (18, 24),
rowspan=7, colspan=7, fig=fig)
ax_right_stress_strain.plot(
self['Patch']['Ef'][:, 1],
self['Patch']['Sf'][:, 1]*1e-3,
c=colors['Ra'],
)
ax_right_stress_strain.plot(
self['Patch']['Ef'][:, 4],
self['Patch']['Sf'][:, 4]*1e-3,
c=colors['Rv'],
)
ax_left_stress_strain.plot(
self['Patch']['Ef'][:, 0],
self['Patch']['Sf'][:, 0]*1e-3,
c=colors['La'],
)
ax_left_stress_strain.plot(
self['Patch']['Ef'][:, 2],
self['Patch']['Sf'][:, 2]*1e-3,
c=colors['Lv'],
)
ax_left_stress_strain.plot(
self['Patch']['Ef'][:, 3],
self['Patch']['Sf'][:, 3]*1e-3,
c=colors['Sv'],
)
ylim = [np.min(self['Patch']['Sf'])*1e-3,
np.max(self['Patch']['Sf'])*1e-3]
ylim_ptp = np.ptp(ylim)*0.025
ylim[0] -= ylim_ptp
ylim[1] += ylim_ptp
ax_right_stress_strain.set_ylim(ylim)
ax_left_stress_strain.set_ylim(ylim)
xlim = [np.min(self['Patch']['Ef']),
np.max(self['Patch']['Ef'])]
xlim_ptp = np.ptp(xlim)*0.025
xlim[0] -= xlim_ptp
xlim[1] += xlim_ptp
ax_right_stress_strain.set_xlim(xlim)
ax_left_stress_strain.set_xlim(xlim)
ax_right_stress_strain.spines[['left', 'bottom']].set_position(('outward', 5))
ax_left_stress_strain.spines[['right', 'bottom']].set_position(('outward', 5))
ax_right_stress_strain.spines[['top', 'right']].set_visible(False)
ax_right_stress_strain.set_ylabel('[kPa]', rotation=0, va='bottom', ha='right')
ax_right_stress_strain.yaxis.set_label_coords(0., 1.05)
ax_right_stress_strain.set_xlabel('Strain [-]')
ax_left_stress_strain.spines[['top', 'left']].set_visible(False)
ax_left_stress_strain.yaxis.set_ticks_position('right')
ax_left_stress_strain.yaxis.set_label_position('right')
ax_left_stress_strain.set_ylabel('Stress\n[kPa]', rotation=0, va='bottom', ha='left')
ax_left_stress_strain.yaxis.set_label_coords(1., 1.05)
ax_left_stress_strain.set_xlabel('Strain [-]')
[docs]
class VanOsta2023_2310(VanOsta2023):
[docs]
def set_reference(self):
self['Chamber']['buckling'] = False
self['Valve']['soft_closure'] = True
self['Valve']['papillary_muscles'] = True
self['Solver']['dt'] = 0.001
self['Solver']['dt_export'] = 0.002
self.set('Solver.order', 2)
self.set('Model.t_cycle', 0.85)
self.set('Model.PFC.fac', 0.5)
self.set('Model.PFC.epsilon', 0.1)
self.set('Model.PFC.fac_pfc', 1)
self.set('Model.PFC.stable_threshold', 0.001)
# Set parameters Circulation
self['Tube0D']['A0'] = [0.00049828, 0.00049899, 0.00046929, 0.00050574]
self['Tube0D']['A_wall'] = [1.13187792e-04, 3.72289316e-05, 7.27489286e-05, 4.31617949e-05]
self['Tube0D']['k'] = [1.66666667, 2.33333333, 1.66666667, 2.33333333]
self['Tube0D']['l'] = [0.4, 0.4, 0.19, 0.19]
self['Tube0D']['p0'] = [12155.25602777, 297.44066943, 1912.6684696 , 580.51425824]
self['ArtVen']['p0'] = [6299.86481149, 1000. ]
self['ArtVen']['q0'] = [4.5e-05, 4.5e-05]
self['ArtVen']['k'] = [1. , 1.88]
# Set volume state variables
# not needed to reset, only if initial model crashes
if True:
self.set('Model.SyArt.V', 2e-4)
self.set('Model.PuArt.V', 1e-4)
self.set('Model.SyVen.V', 4e-4)
self.set('Model.PuVen.V', 2e-4)
self.set('Model.Peri.TriSeg.V', 56e-6)
self.set('Model.Peri.TriSeg.Y', 36e-3)
self.set('Model.Peri.TriSeg.cLv.V', 150e-6)
self.set('Model.Peri.TriSeg.cRv.V', 110e-6)
self.set('Model.Peri.La.V', 25e-6)
self.set('Model.Peri.Ra.V', 150e-6)
# all wall parameters
self['Patch']['l_se0'] = 0.04
self['Patch']['l_s_ref'] = 2.0
self['Patch']['l_s0'] = [1.79, 1.79, 1.8 , 1.8 , 1.8 ]
self['Patch']['dl_s_pas'] = 0.6
self['Patch']['k1'] = 10
self['Patch']['dt'] = 0
self['Patch']['C_rest'] = 0
self['Patch']['l_si0'] = 1.51
self['Patch']['LDAD'] = [1.057, 1.057, 0.74 , 0.74 , 0.74 ]
self['Patch']['ADO'] = [0.081, 0.081, 0.75 , 0.75 , 0.75 ]
self['Patch']['LDCC'] = [4. , 4. , 3., 3., 3.]
self['Patch']['v_max'] = 7.
self['Patch']['v_max'][:2] = 14.
self['Patch']['tr'] = [0.4 , 0.4 , 0.21, 0.21, 0.21]
self['Patch']['td'] = [0.4 , 0.4 , 0.24, 0.24, 0.24]
# wall specific
self['Patch']['Sf_act'] = [100000., 100000., 120000., 120000., 120000.]
self['Patch']['Am_ref'] = [0.00400721, 0.00311263, 0.00918281, 0.00518683, 0.01321999]
self['Patch']['V_wall'] = [1.59935455e-05, 7.72140495e-06, 1.16441768e-04, 4.51241560e-05,
6.88196851e-05]
self['Patch']['Sf_pas'] = [ 9.51064014, 10.3647024 , 424.3537027 , 417.74300178,
439.21574206]
# Valves
self['Valve']['adaptation_A_open_fac'] = [1. , 1.12, 1. , 1. , 1.12, 1. ]
self['Valve']['A_open'] = [0.00050044, 0.00052598, 0.00046962, 0.00050745, 0.0005583 ,
0.00049849]
self['Valve']['A_leak'] = [2.64705882e-04, 2.64705882e-10, 2.64705882e-10, 2.64705882e-04,
2.64705882e-10, 2.64705882e-10]
self['Valve']['l'] = 0.01626978
self['Valve']['rho_b'] = 1050
self['Valve']['papillary_muscles'] = True
self['Valve']['papillary_muscles_slope'] = 100
self['Valve']['papillary_muscles_min'] = 0.1
self['Valve']['papillary_muscles_A_open_fac'] = 0.1
self['Valve']['soft_closure'] = True
self['Bag']['k'] = 10
self['Bag']['V_ref'] = [0.00057937]
self['Bag']['p_ref'] = 100
self.set('Model.Peri.TriSeg.Y', 0.035)
self['Timings']['law_tau_av'] = 2
self['Timings']['c_tau_av0'] = 0.22
self['Timings']['c_tau_av1'] = -0.03
# Adaptation
self['Patch']['adapt_gamma'] = 0.5
self.set('Solver.store_beats', 1)
self['PFC']['stable_threshold'] = 1e-3
self['Patch']['SfPasMaxT'] = [3600., 3600., 4200., 4200., 4200.]
self['Patch']['FacSfActT'] = [0.28, 0.28, 0.69, 0.69, 0.69]
self['Patch']['SfPasActT'] = [2800., 2800., 6600., 6600., 6600.]
self['Patch']['LsPasActT'] = [3. , 3. , 2.31, 2.31, 2.31]
self.run(stable=True)
return
[docs]
def get_unittest_targets(self):
"""Hardcoded results after initializing and running 1 beat."""
return {
'LVEDV': 121.75,
'LVESV': 48.0,
}
[docs]
class VanOsta2023_2306(VanOsta2023):
[docs]
def set_reference(self):
self['Chamber']['buckling'] = False
self['Valve']['soft_closure'] = True
self['Solver']['dt'] = 0.0005
self['Solver']['dt_export'] = self['Solver']['dt']
self.set('Model.t_cycle', 0.85)
self.set('Model.PFC.fac', 0.5)
self.set('Model.PFC.epsilon', 0.1)
self.set('Model.PFC.fac_pfc', 1)
self.set('Model.PFC.stable_threshold', 0.001)
# Set parameters Circulation
self['Tube0D']['A0'] = [
0.00049833,
0.00050034,
0.0004684 ,
0.00051849,
]
self['Tube0D']['A_wall'] = [
1.14408188e-04,
4.81082964e-05,
5.19472921e-05,
5.79933632e-05,
]
self['Tube0D']['k'] = np.array([ 8., 10., 8., 10.]) / 3 - 1
self['Tube0D']['l'] = [0.4, 0.4, 0.2, 0.2]
self['Tube0D']['p0'] = [
12161.21763497,
112.09788908,
1899.95009955,
572.39790382,
]
self['ArtVen']['p0'] = [6395.43973044, 500.]
self['ArtVen']['q0'] = [4.5e-05, 4.5e-05]
self['ArtVen']['k'] = [1, 2]
# Set volume state variables
# not needed to reset, only if initial model crashes
if True:
self.set('Model.SyArt.V', 2e-4)
self.set('Model.PuArt.V', 1e-4)
self.set('Model.SyVen.V', 4e-4)
self.set('Model.PuVen.V', 2e-4)
self.set('Model.Peri.TriSeg.V', 44e-6)
self.set('Model.Peri.TriSeg.Y', 34.6e-3)
self.set('Model.Peri.TriSeg.cLv.V', 150e-6)
self.set('Model.Peri.TriSeg.cRv.V', 100e-6)
self.set('Model.Peri.La.V', 100e-6)
self.set('Model.Peri.Ra.V', 50e-6)
# all wall parameters
self['Patch']['l_se'] = 0.04
self['Patch']['l_s_ref'] = 2.0
self['Patch']['l_s0'] = 1.8
self['Patch']['dl_s_pas'] = 0.6
self['Patch']['k1'] = 10
self['Patch']['dt'] = 0
self['Patch']['C_rest'] = 0
self['Patch']['l_si0'] = 1.51
self['Patch']['LDAD'] = 1.057
self['Patch']['ADO'] = 0.65
self['Patch']['LDCC'] = 4.
self['Patch']['v_max'] = 7.
self['Patch']['v_max'][:2] = 14.
self['Patch']['tr'] = 0.25
self['Patch']['td'] = 0.25
self['Patch']['tr'][:2] = 0.4
self['Patch']['td'][:2] = 0.4
# wall specific
self['Patch']['Am_ref'] = [
0.00706159,
0.00603949,
0.0093191 ,
0.00507053,
0.01313792,
]
self['Patch']['V_wall'] = [
1.86518494e-05,
6.12511827e-06,
9.51121820e-05,
3.74835076e-05,
4.97478018e-05,
]
self['Patch']['Sf_pas'] = np.array([
51591.64007141,
52277.48834349,
22370.46712926,
22051.80366169,
23278.22158555,
]) * 0.0349
self['Patch']['Sf_act'] = [84e3, 84e3, 120e3, 120e3, 120e3]
# Valves
self['Valve']['adaptation_A_open_fac'] = [
1.,
1.5,
1.,
1.,
1.5,
1.]
self['Valve']['A_open'] = [
0.00050078,
0.0007021,
0.00046806,
0.00051776,
0.00074755,
0.00049837]
self['Valve']['A_leak'] = [
2.64705882e-04,
2.64705882e-10,
2.64705882e-10,
2.64705882e-04,
2.64705882e-10,
2.64705882e-10]
self['Valve']['l'] = 0.01626978
self['Valve']['rho_b'] = 1050
self['Valve']['papillary_muscles'] = False
self['Valve']['papillary_muscles_slope'] = 100
self['Valve']['papillary_muscles_min'] = 0.1
self['Valve']['papillary_muscles_A_open_fac'] = 0.1
self['Valve']['soft_closure'] = True
self['Bag']['k'] = 10
self['Bag']['V_ref'] = 0.0005
self['Bag']['p_ref'] = 100
self.set('Model.Peri.TriSeg.Y', 0.035)
self['Timings']['law_tau_av'] = 2
self['Timings']['c_tau_av0'] = 0.2
self['Timings']['c_tau_av1'] = -0.485*60e-3
# Adaptation
self['Patch']['adapt_gamma'] = 0.75
self.run(stable=True)
[docs]
def get_unittest_targets(self):
"""Hardcoded results after initializing and running 1 beat."""
return {
'LVEDV': 141.6,
'LVESV': 69.4,
}
[docs]
class VanOsta2023_2308(VanOsta2023):
[docs]
def set_reference(self):
self['Chamber']['buckling'] = False
self['Valve']['soft_closure'] = True
self['Valve']['papillary_muscles'] = True
self['Solver']['dt'] = 0.001
self['Solver']['dt_export'] = 0.002
self.set('Solver.order', 2)
self.set('Model.t_cycle', 0.85)
self.set('Model.PFC.fac', 0.5)
self.set('Model.PFC.epsilon', 0.1)
self.set('Model.PFC.fac_pfc', 1)
self.set('Model.PFC.stable_threshold', 0.001)
# Set parameters Circulation
self['Tube0D']['A0'] = [0.0004982 , 0.00049959, 0.00045184, 0.00051768]
self['Tube0D']['A_wall'] = [1.13597208e-04, 3.79106863e-05, 8.91394588e-05, 4.26698697e-05]
self['Tube0D']['k'] = [1.66666667, 2.33333333, 1. , 2.33333333]
self['Tube0D']['l'] = [0.4, 0.4, 0.1, 0.1]
self['Tube0D']['p0'] = [12154.79898845, 213.26733196, 1913.05480512, 600.72294959]
self['ArtVen']['p0'] = [6345.26731406, 950. ]
self['ArtVen']['q0'] = [4.5e-05, 4.5e-05]
self['ArtVen']['k'] = [1. , 1.72]
# Set volume state variables
# not needed to reset, only if initial model crashes
if True:
self.set('Model.SyArt.V', 2e-4)
self.set('Model.PuArt.V', 1e-4)
self.set('Model.SyVen.V', 4e-4)
self.set('Model.PuVen.V', 2e-4)
self.set('Model.Peri.TriSeg.V', 56e-6)
self.set('Model.Peri.TriSeg.Y', 36e-3)
self.set('Model.Peri.TriSeg.cLv.V', 150e-6)
self.set('Model.Peri.TriSeg.cRv.V', 110e-6)
self.set('Model.Peri.La.V', 25e-6)
self.set('Model.Peri.Ra.V', 150e-6)
# all wall parameters
self['Patch']['l_se'] = 0.04
self['Patch']['l_s_ref'] = 2.0
self['Patch']['l_s0'] = 1.8
self['Patch']['dl_s_pas'] = 0.6
self['Patch']['k1'] = 10
self['Patch']['dt'] = 0
self['Patch']['C_rest'] = 0
self['Patch']['l_si0'] = 1.51
self['Patch']['LDAD'] = [1.057, 1.057, 0.64 , 0.64 , 0.64 ]
self['Patch']['ADO'] = [0.65, 0.65, 0.75, 0.75, 0.75]
self['Patch']['LDCC'] = [4. , 4. , 3.2, 3.2, 3.2]
self['Patch']['v_max'] = 7.
self['Patch']['v_max'][:2] = 14.
self['Patch']['tr'] = [0.4 , 0.4 , 0.24, 0.24, 0.24]
self['Patch']['td'] = [0.4 , 0.4 , 0.23, 0.23, 0.23]
# wall specific
self['Patch']['Sf_act'] = [80e3, 80e3, 120e3, 120e3, 120e3]
self['Patch']['Am_ref'] = [0.00458522, 0.00381765, 0.00806997, 0.00454307, 0.01176514]
self['Patch']['V_wall'] = [2.75050615e-05, 1.25959326e-05, 8.80910385e-05, 3.46281364e-05,
5.16086110e-05]
self['Patch']['Sf_pas'] = [ 16.51117993, 17.0639141 , 580.39235927, 569.67392472,
606.50514861]
# Valves
self['Valve']['adaptation_A_open_fac'] = [1. , 1.11, 1. , 1. , 1.11, 1. ]
self['Valve']['A_open'] = [0.00050042, 0.00050203, 0.00045228, 0.00051764, 0.00055321,
0.00049838]
self['Valve']['A_leak'] = [2.64705882e-04, 2.64705882e-10, 2.64705882e-10, 2.64705882e-04,
2.64705882e-10, 2.64705882e-10]
self['Valve']['l'] = 0.01626978
self['Valve']['rho_b'] = 1050
self['Valve']['papillary_muscles'] = True
self['Valve']['papillary_muscles_slope'] = 100
self['Valve']['papillary_muscles_min'] = 0.1
self['Valve']['papillary_muscles_A_open_fac'] = 0.1
self['Valve']['soft_closure'] = True
self['Bag']['k'] = 10
self['Bag']['V_ref'] = [0.00051968]
self['Bag']['p_ref'] = 100
self.set('Model.Peri.TriSeg.Y', 0.035)
self['Timings']['law_tau_av'] = 2
self['Timings']['c_tau_av0'] = 0.172
self['Timings']['c_tau_av1'] = -0.485*60e-3
# Adaptation
self['Patch']['adapt_gamma'] = 0.5
self.set('Solver.store_beats', 1)
self['PressureFlowControl']['stable_threshold'] = 1e-3
self['Patch']['SfPasMaxT'] = [6400., 6400., 6600., 6600., 6600.]
self['Patch']['FacSfActT'] = [0.44, 0.44, 0.61, 0.61, 0.61]
self['Patch']['SfPasActT'] = [4800., 4800., 6600., 6600., 6600.]
self['Patch']['LsPasActT'] = [3. , 3. , 2.23, 2.23, 2.23]
self.run(stable=True)
return
[docs]
def get_unittest_targets(self):
"""Hardcoded results after initializing and running 1 beat."""
return {
'LVEDV': 119.7,
'LVESV': 47.2,
}
[docs]
class Walmsley2015(VanOsta2023):
[docs]
def set_reference(self):
# change model parameters here
self['Chamber']['buckling'] = False
self['Valve']['soft_closure'] = True
self['Valve']['papillary_muscles'] = False
self['Solver']['dt'] = 0.001
self['Solver']['dt_export'] = 0.002
self.set('Solver.order', 2)
self.set('Model.t_cycle', 0.85)
self.set('Model.PFC.fac', 0.5)
self.set('Model.PFC.epsilon', 0.1)
self.set('Model.PFC.fac_pfc', 1)
self.set('Model.PFC.stable_threshold', 0.001)
# Set parameters Circulation
self['Tube0D']['A0'] = [4.9720e-04, 4.9830e-04, 4.6850e-04, 5.0910e-04]
self['Tube0D']['A_wall'] = [1.1370e-04, 6.410e-05, 9.670e-05, 5.950e-05]
self['Tube0D']['k'] = [1.66666667, 2.33333333, 1.66666667, 2.33333333]
# self['Tube0D']['k'] = [8, 10, 8, 10]
self['Tube0D']['l'] = [0.4, 0.4, 0.2, 0.2]
self['Tube0D']['p0'] = [12164, 228, 2630 , 678]
self['ArtVen']['p0'] = [6396, 1500. ]
self['ArtVen']['q0'] = [4.5e-05, 4.5e-05]
self['ArtVen']['k'] = [1. , 2]
# Set volume state variables
# not needed to reset, only if initial model crashes
if True:
self.set('Model.SyArt.V', 2e-4)
self.set('Model.PuArt.V', 1e-4)
self.set('Model.SyVen.V', 4e-4)
self.set('Model.PuVen.V', 2e-4)
self.set('Model.Peri.TriSeg.V', 56e-6)
self.set('Model.Peri.TriSeg.Y', 36e-3)
self.set('Model.Peri.TriSeg.cLv.V', 150e-6)
self.set('Model.Peri.TriSeg.cRv.V', 110e-6)
self.set('Model.Peri.La.V', 25e-6)
self.set('Model.Peri.Ra.V', 150e-6)
# all wall parameters
self['Patch']['l_se0'] = 0.04
self['Patch']['l_s_ref'] = 2.0
self['Patch']['l_s0'] = [1.8, 1.8, 1.8 , 1.8 , 1.8 ]
self['Patch']['dl_s_pas'] = 0.6
self['Patch']['k1'] = 10
self['Patch']['dt'] = 0
self['Patch']['C_rest'] = 0
self['Patch']['l_si0'] = 1.51
self['Patch']['LDAD'] = [1.057, 1.057, 0.74 , 0.74 , 0.74 ]
self['Patch']['ADO'] = [0.081, 0.081, 0.75 , 0.75 , 0.75 ]
self['Patch']['LDCC'] = [4. , 4. , 3., 3., 3.]
self['Patch']['v_max'] = 7.
self['Patch']['v_max'][:2] = 14.
self['Patch']['tr'] = [0.4 , 0.4 , 0.25, 0.25, 0.25]
self['Patch']['td'] = [0.4 , 0.4 , 0.25, 0.25, 0.25]
# wall specific
self['Patch']['Sf_act'] = [60000., 60000., 100000., 100000., 100000.]
self['Patch']['Am_ref'] = [0.0071, 0.0058, 0.0099, 0.0052, 0.0135]
self['Patch']['V_wall'] = [1.3377e-05, 5.4588e-06, 1.1382e-04, 3.8320e-05,
8.0093e-05]
self['Patch']['Sf_pas'] = [ 2.8942e3, 3.6579e3 , 794.1495 , 790.6246,
801.1993]
# Valves
self['Valve']['adaptation_A_open_fac'] = [1. , 1.12, 1. , 1. , 1.12, 1. ]
self['Valve']['A_open'] = [0.00049811, 0.00070281, 0.00046854, 0.00050903, 0.00074578 ,
0.00049719]
self['Valve']['A_leak'] = [2.6471e-04, 2.6471e-10, 2.6471e-10, 2.6471e-04,
2.6471e-10, 2.6471e-10]
self['Valve']['l'] = 0.0163
self['Valve']['rho_b'] = 1050
self['Valve']['papillary_muscles'] = False
self['Valve']['papillary_muscles_slope'] = 100
self['Valve']['papillary_muscles_min'] = 0.1
self['Valve']['papillary_muscles_A_open_fac'] = 0.1
self['Valve']['soft_closure'] = True
self['Bag']['k'] = 10
self['Bag']['V_ref'] = [0.00072379]
self['Bag']['p_ref'] = 100
self.set('Model.Peri.TriSeg.Y', 0.035)
# self['Timings']['law_tauAv'] = 1
# self['Timings']['c_tauAv0'] = 0.22
# self['Timings']['c_tauAv1'] = -0.03
# Adaptation
# self['Patch2022']['adapt_gamma'] = 0.5
self.set('Solver.store_beats', 1)
self['PFC']['stable_threshold'] = 1e-3
# self['Patch']['SfPasMaxT'] = [3600., 3600., 4200., 4200., 4200.]
# self['Patch']['FacSfActT'] = [0.28, 0.28, 0.69, 0.69, 0.69]
# self['Patch']['SfPasActT'] = [2800., 2800., 6600., 6600., 6600.]
# self['Patch']['LsPasActT'] = [3. , 3. , 2.31, 2.31, 2.31]
self.run(stable=True)