Source code for circadapt.model.vanosta2024

"""
VanOsta2024 model.

This model is first used in Van Osta et al. under review
"""

from circadapt.model import Model
from circadapt.adapt import ModelAdapt
from circadapt.plot import triseg2022
import matplotlib.pyplot as plt
import numpy as np

# define colors for the plot function
colors = {
    'Lv': np.array([213/255, 96/255, 98/255]),
    'Sv': np.array([236/255, 195/255, 11/255]),
    'Rv': np.array([6/255, 123/255, 194/255]),
    'Ra': np.array([27/255, 231/255, 255/255]),
    'La': np.array([250/255, 175/255, 150/255]),
    'PuArt': np.array([5/255, 75/255, 125/255]),
    'SyArt': np.array([150/255, 25/255, 25/255]),
    'RaRv': np.array([0, 0, 0]),
    'RvPuArt': np.array([0, 0, 0]),
    'LaLv': np.array([0, 0, 0]),
    'LvSyArt': np.array([0, 0, 0]),
    }
colors['RaRv'] = colors['Ra'] #+ 0.5*colors['Rv']
colors['RvPuArt'] = colors['Rv'] #+ 0.5*colors['PuArt']
colors['LaLv'] = colors['La'] #+ 0.5*colors['Lv']
colors['LvSyArt'] = colors['Lv'] #+ 0.5*colors['SyArt']
colors['Sv'] = (0.5*colors['Lv'] + 0.5*colors['Rv'])**1.3


[docs] class VanOsta2024(Model, ModelAdapt): def __init__(self, solver: str = None, path_to_circadapt: str = None, model_state: dict = None, ): if solver is None: solver = 'adams_moulton' 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['PressureFlowControl'] = 'PFC'
[docs] def build(self): pass # Circulation self.add_smart_component('ArtVen', build='SystemicCirculation') self.add_smart_component('ArtVen', build='PulmonaryCirculation') self.add_smart_component('Heart', patch_type='Patch', valve_type='Valve') self.add_smart_component('Timings') self.add_smart_component('PressureFlowControl') self.set_component('PFC.circulation_volume_object', 'SyArt') self.set_component('PFC.circulation_volume_object', 'SyVen') self.set_component('PFC.circulation_volume_object', 'PuArt') self.set_component('PFC.circulation_volume_object', 'PuVen') self.set_component('PFC.circulation_volume_object', 'Peri.La') self.set_component('PFC.circulation_volume_object', 'Peri.Ra') self.set_component('PFC.circulation_volume_object', 'Peri.TriSeg.cLv') self.set_component('PFC.circulation_volume_object', 'Peri.TriSeg.cRv') # # 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'] = True 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['ArtVen']['p0'] = np.array([6306.25832487, 1000. ]) self['ArtVen']['q0'] = np.array([4.5e-05, 4.5e-05]) self['ArtVen']['k'] = np.array([1., 2.]) self['Tube0D']['l'] = np.array([0.4, 0.4, 0.2, 0.2]) self['Tube0D']['A_wall'] = np.array([1.12362733e-04, 6.57883944e-05, 9.45910889e-05, 8.22655361e-05]) self['Tube0D']['k'] = np.array([1.66666667, 2.33333333, 1.66666667, 2.33333333]) self['Tube0D']['p0'] = np.array([12162.50457811, 287.7083132 , 2132.51755623, 830.54673184]) self['Tube0D']['A0'] = np.array([0.0004983 , 0.00049909, 0.00047138, 0.00050803]) self['Tube0D']['target_wall_stress'] = np.array([500000., 500000., 500000., 500000.]) self['Tube0D']['target_mean_flow'] = np.array([0.17, 0.17, 0.17, 0.17]) self['Bag']['k'] = np.array([10.]) self['Bag']['p_ref'] = np.array([1000.]) self['Bag']['V_ref'] = np.array([0.00054267]) self['Patch']['Am_ref'] = np.array([0.00425687, 0.00401573, 0.00966859, 0.00289936, 0.01084227]) self['Patch']['V_wall'] = np.array([4.46069398e-06, 2.14548521e-06, 7.35720515e-05, 1.88904978e-05, 3.67720116e-05,]) self['Patch']['v_max'] = np.array([14., 14., 7., 7., 7.]) self['Patch']['l_se0'] = np.array([0.04, 0.04, 0.04, 0.04, 0.04]) self['Patch']['l_s0'] = np.array([1.8, 1.8, 1.8, 1.8, 1.8]) self['Patch']['l_s_ref'] = np.array([2., 2., 2., 2., 2.]) self['Patch']['dl_s_pas'] = np.array([0.6, 0.6, 0.6, 0.6, 0.6]) self['Patch']['Sf_pas'] = np.array([2248.53598 , 2684.76100348, 731.24545453, 729.06063771, 749.47694522,]) self['Patch']['tr'] = np.array([0.4 , 0.4 , 0.25, 0.25, 0.25]) self['Patch']['td'] = np.array([0.4 , 0.4 , 0.25, 0.25, 0.25]) self['Patch']['time_act'] = np.array([0.15 , 0.15 , 0.425, 0.425, 0.425]) self['Patch']['Sf_act'] = np.array([ 84000., 84000., 120000., 120000., 120000.]) self['Patch']['fac_Sf_tit'] = np.array([0.01, 0.01, 0.01, 0.01, 0.01]) self['Patch']['k1'] = np.array([10., 10., 10., 10., 10.]) self['Patch']['dt'] = np.array([0., 0., 0., 0., 0.]) self['Patch']['C_rest'] = np.array([0., 0., 0., 0., 0.]) self['Patch']['l_si0'] = np.array([1.51, 1.51, 1.51, 1.51, 1.51]) self['Patch']['LDAD'] = np.array([1.057, 1.057, 1.057, 1.057, 1.057]) self['Patch']['ADO'] = np.array([0.65, 0.65, 0.65, 0.65, 0.65]) self['Patch']['LDCC'] = np.array([4., 4., 4., 4., 4.]) self['Patch']['SfPasMaxT'] = np.array([320000., 320000., 16000., 16000., 16000.]) self['Patch']['SfPasActT'] = np.array([40000., 40000., 20000., 20000., 20000.]) self['Patch']['FacSfActT'] = np.array([0.8, 0.8, 1. , 1. , 1. ]) self['Patch']['LsPasActT'] = np.array([2.42, 2.42, 2.42, 2.42, 2.42]) self['Patch']['adapt_gamma'] = np.array([0.5, 0.5, 0.5, 0.5, 0.5]) self['Patch']['transmat00'] = np.array([-0.5751, -0.5751, -0.5751, -0.5751, -0.5751]) self['Patch']['transmat01'] = np.array([-0.7851, -0.7851, -0.7851, -0.7851, -0.7851]) self['Patch']['transmat02'] = np.array([0.6063, 0.6063, 0.6063, 0.6063, 0.6063]) self['Patch']['transmat03'] = np.array([-0.5565, -0.5565, -0.5565, -0.5565, -0.5565]) self['Patch']['transmat10'] = np.array([-0.1279, -0.1279, -0.1279, -0.1279, -0.1279]) self['Patch']['transmat11'] = np.array([0.0999, 0.0999, 0.0999, 0.0999, 0.0999]) self['Patch']['transmat12'] = np.array([0.2066, 0.2066, 0.2066, 0.2066, 0.2066]) self['Patch']['transmat13'] = np.array([-1.8441, -1.8441, -1.8441, -1.8441, -1.8441]) self['Patch']['transmat20'] = np.array([-0.1865, -0.1865, -0.1865, -0.1865, -0.1865]) self['Patch']['transmat21'] = np.array([-0.201, -0.201, -0.201, -0.201, -0.201]) self['Patch']['transmat22'] = np.array([1.3195, 1.3195, 1.3195, 1.3195, 1.3195]) self['Patch']['transmat23'] = np.array([-11.8745, -11.8745, -11.8745, -11.8745, -11.8745]) self['Valve']['adaptation_A_open_fac'] = np.array([1., 1., 1., 1., 1., 1.]) self['Valve']['A_open'] = np.array([0.00049916, 0.00047155, 0.00047155, 0.00050805, 0.00049835, 0.00049835]) self['Valve']['A_leak'] = np.array([0.00049916, 1.e-09, 1.e-09, 0.00050805, 1.e-09, 1.e-09]) self['Valve']['l'] = np.array([0.01260512, 0.01225144, 0.01225144, 0.01271679, 0.01259479, 0.01259479]) self['Valve']['L_fac_prox'] = np.array([0.75, 0.75, 0.75, 0.75, 0.75, 0.75]) self['Valve']['L_fac_dist'] = np.array([0.75, 0.75, 0.75, 0.75, 0.75, 0.75]) self['Valve']['L_fac_valve'] = np.array([1.5, 1.5, 1.5, 1.5, 1.5, 1.5]) self['Valve']['rho_b'] = np.array([1050., 1050., 1050., 1050., 1050., 1050.]) self['Valve']['papillary_muscles'] = np.array([False, False, False, False, False, False]) self['Valve']['papillary_muscles_slope'] = np.array([100., 100., 100., 100., 100., 100.]) self['Valve']['papillary_muscles_min'] = np.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) self['Valve']['papillary_muscles_A_open_fac'] = np.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) self['Valve']['soft_closure'] = np.array([ True, True, True, True, True, True]) self['Valve']['fraction_A_open_Aext'] = np.array([0.9, 0.9, 0.9, 0.9, 0.9, 0.9]) self['Timings']['time_fac'] = np.array([1.]) self['Timings']['tau_av'] = np.array([0.150025]) self['Timings']['dtau_av'] = np.array([0.]) self['Timings']['law_tau_av'] = np.array([1]) self['Timings']['law_Ra2La'] = np.array([1]) self['Timings']['law_ta'] = np.array([1]) self['Timings']['law_tv'] = np.array([1]) self['Timings']['c_tau_av0'] = np.array([0.]) self['Timings']['c_tau_av1'] = np.array([0.1765]) self['Timings']['c_ta_rest'] = np.array([0.]) self['Timings']['c_ta_tcycle'] = np.array([0.17647059]) self['Timings']['c_tv_rest'] = np.array([0.1]) self['Timings']['c_tv_tcycle'] = np.array([0.4]) self['PFC']['p0'] = np.array([12200.]) self['PFC']['q0'] = np.array([8.5e-05]) self['PFC']['stable_threshold'] = np.array([0.0001]) self['PFC']['is_active'] = np.array([ True]) self['PFC']['fac'] = np.array([1.]) self['PFC']['fac_pfc'] = np.array([1.]) self['PFC']['epsilon'] = np.array([0.4]) self['TriSeg']['tau'] = np.array([2.]) self['TriSeg']['ratio_septal_LV_Am'] = np.array([0.3]) self['TriSeg']['max_number_of_iterations'] = np.array([100.]) self['TriSeg']['thresh_F'] = np.array([0.001]) self['TriSeg']['thresh_dV'] = np.array([1.e-09]) self['TriSeg']['thresh_dY'] = np.array([1.e-06]) self.run(stable=True) # self.adapt() # set target volume self['PFC']['target_volume'] = self['PFC']['circulation_volume'] return
[docs] def get_unittest_targets(self): """Hardcoded results after initializing and running 1 beat.""" return { 'LVEDV': 120.5, 'LVESV': 48.3, }
[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 len(self['Solver']['t'])==0: raise RuntimeError('Can not plot the model, as no data is available. Did you simulate the model?') 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', weight='bold') # 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', weight='bold') # 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, sharex=axVRv) 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, sharex=axVLv) 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, sharex=axVRv) 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, sharex=axVLv) 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, sharex=axVRv) 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, sharex=axVLv) 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, sharex=axVRv) 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, sharex=axVLv) 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'][:, 'pRa0'], self['Patch']['Sf'][:, 'pRa0']*1e-3, c=colors['Ra'], ) ax_right_stress_strain.plot( self['Patch']['Ef'][:, 'pRv0'], self['Patch']['Sf'][:, 'pRv0']*1e-3, c=colors['Rv'], ) ax_left_stress_strain.plot( self['Patch']['Ef'][:, 'pLa0'], self['Patch']['Sf'][:, 'pLa0']*1e-3, c=colors['La'], ) ax_left_stress_strain.plot( self['Patch']['Ef'][:, 'pLv0'], self['Patch']['Sf'][:, 'pLv0']*1e-3, c=colors['Lv'], ) ax_left_stress_strain.plot( self['Patch']['Ef'][:, 'pSv0'], self['Patch']['Sf'][:, 'pSv0']*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 [-]')