# %% Imports etc.
import sys, os, pickle
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
plt.close('all')
# Paths
cwd = os.getcwd()
baseDir = os.path.join(cwd,'..')
dataDir = os.path.join(baseDir,'data')
funcDir = os.path.join(baseDir,'analysis','functions')
sys.path.append(funcDir)
# Custom imports
import cust_fig, hillmodel
# %% Load muscle parameters
mus = 'GMs1'
parFile = os.path.join(dataDir,mus,'parameters',mus+'_OR.pkl')
muspar = pickle.load(open(parFile, 'rb'))
# %% Figure setup
cust_fig.style(plt, fontname='MinionPro', fontsize=11, grid=True)
fig = plt.figure(figsize=(15.92/2.54+50/600, (15.92/3*2-0.5)/2.54), constrained_layout=True) # 2:1 ratio (3761px x 1880px)
gs = fig.add_gridspec(4, 2,height_ratios=[10/1.5, 10, 1/2.9, 10], hspace=0)
axs = np.array([[fig.add_subplot(gs[i, j]) for j in range(gs.ncols)] for i in range(gs.nrows)])
axs[0,0].remove(); axs[0,1].remove();
axs[0,0] = fig.add_subplot(gs[0, :])
axs[2,0].remove();
# %% Panel 0: Schematic MTC model
data = pd.read_csv("m_mtc_data.txt", sep=',')
x = data.to_numpy()[:,0]
y = data.to_numpy()[:,1]
n = 35 # endpoint CE
ax = axs[0,0]
ax.set_axis_off()
ax.plot(x,y,'k',lw=1)
ax.plot(x[n],y[n],'k',marker='.')
ax.plot(np.array([0,x[0]]),np.array([0,y[0]]),'k')
ax.annotate('', xy=(0,-10), xycoords='data', xytext=(x[n], -10), arrowprops=dict(arrowstyle="<->", lw=0.5, shrinkA = 0, shrinkB = 0))
ax.text(x[n]/2,-16, "$L_{CE} = L_{PEE}$", color="k",ha="center", va = 'bottom') # else part of xlabel is cropped..
ax.annotate('', xy=(x[n],-10), xycoords='data', xytext=(x[-1], -10), arrowprops=dict(arrowstyle="<->", lw=0.5, shrinkA = 0, shrinkB = 0))
ax.text((x[-1]-x[n])/2+x[n],-16, "$L_{SEE}$", color="k",ha="center", va = 'bottom') # else part of xlabel is cropped..
ax.plot([0,0],[-16,-16], alpha=0)
ax.axis('equal')
ax.autoscale(enable=True, axis='x', tight=True)
# %% Panel A: q(gamma)
ax = axs[1,0]
n = 101
gamma = np.linspace(muspar['gamma_0'],1,n)
qlijn = -gamma*2.5+1.5;
for iL,lcerel in enumerate([1.45,1.15,0.85,0.55]):
if iL%2 == 0:
clr = "gray"
else:
clr = "k" # gray if we want to switch..
q = hillmodel.ActState(gamma,np.repeat(lcerel,n),muspar)[0]
l, = ax.plot(gamma,q,color=clr)
idx = np.argmin(np.abs(q-qlijn))
angle = np.rad2deg(np.arctan2(np.gradient(q,gamma)[idx], 1))
ax.text(gamma[idx], q[idx], '{:.2f}'.format(lcerel), ha='center', va='center', fontsize = 7, color = clr,
transform_rotates_text=True, rotation=angle, bbox=dict(ec='1', fc='1', pad=0.1))
ax.annotate('$L_{CE}^{rel}$', xy=(gamma[idx]+0.01, q[idx]-0.01), xycoords='data', xytext=(42, -19),
textcoords='offset points', arrowprops=dict(arrowstyle="->",lw=0.5, connectionstyle="arc3,rad=-.4"))
ax.set_xlabel(r"$\gamma$ [ ]")
ax.set_ylabel(r"$q$ [ ]")
ax.set_xlim(0,1)
ax.set_ylim(0,1.125)
ax.set_xticks([0.0,0.25,0.5,0.75,1.0])
ax.set_xticklabels(["0.0","","0.5","","1.0"])
ax.set_yticks([0.0,0.25,0.5,0.75,1.0])
ax.set_yticklabels(["0.0","","0.5","","1.0"])
# %% Panel B: fisomrel(lcerel,q) ---
ax = axs[1,1]
lcerel = np.linspace(0,2,n)
fisomrel = hillmodel.ForceLength(lcerel,muspar)[0]
idx = np.argmin(np.abs(lcerel-1))
for iGam,gamma in enumerate([0.10,0.20,0.30,1.00]):
if iGam%2 == 0:
clr = "gray"
else:
clr = "k" # gray if we want to switch..
q = hillmodel.ActState(gamma,lcerel,muspar)[0]
ax.plot(lcerel,q*fisomrel,color=clr)
angle = np.rad2deg(np.arctan2(np.gradient(q,lcerel)[idx], 1))
ax.text(lcerel[idx],q[idx], '{:.2f}'.format(gamma), ha='center', va='center', fontsize = 7, color=clr,
transform_rotates_text=True, rotation=angle, bbox=dict(ec='1', fc='1', pad=0.1))
ax.annotate(r'$\gamma$', xy=(lcerel[idx],q[idx]+0.07), xycoords='data', xytext=(70, -20),
textcoords='offset points', arrowprops=dict(arrowstyle="->", lw=0.5, connectionstyle="arc3,rad=.35"), annotation_clip=True)
ax.set_xlabel(r"$L_{CE}^{rel}$ [ ]")
ax.set_xticks([muspar['w'],1,1+muspar['w']])
ax.set_xticklabels(["1-$w$","1.0","1+$w$"])
ax.set_xlim(0.3,1.7)
ax.set_ylabel(r"$q \cdot F_{CE}^{isom,rel}$ [ ]")
ax.set_yticks([0.0,0.25,0.5,0.75,1.0])
ax.set_yticklabels(["0.0","","0.5","","1.0"])
ax.set_ylim(0,1.125)
# %% Panel C: fcerel(vcerel)
ax = axs[3,0]
lcerel = np.repeat(1,n)
fisomrel = hillmodel.ForceLength(lcerel,muspar)
# vcerel = np.linspace(-muspar['brel']/muspar['arel'],muspar['brel']/muspar['arel'],n)
# fcerel = np.linspace(0,1.5,n)*idx
fce = np.linspace(0,1.5,n)*muspar['fmax']
vce = np.linspace(-muspar['b']/muspar['a'],muspar['b']/muspar['a'],n)*muspar['fmax']
qs = [0.05,0.25,0.50,0.75,1.00]
idx = n-20
for iFV,q in enumerate(qs):
if iFV%2 == 0:
clr = "k"
else:
clr = "gray" # gray if we want to switch..
fce,fcerel = hillmodel.Vce2Fce(vce,q,1,muspar)[0:2]
ax.plot(vce,fcerel,color=clr)
ax.text(vce[idx],fcerel[idx], '{:.2f}'.format(q), ha='center', va='center', fontsize = 7, color=clr,
transform_rotates_text=True, rotation=angle, bbox=dict(ec='1', fc='1', pad=0.1))
ax.annotate('$q$', xy=(vce[idx],fcerel[idx]+0.1), xycoords='data', xytext=(-80, -18.5),
textcoords='offset points', arrowprops=dict(arrowstyle="->",lw=0.5, connectionstyle="arc3,rad=-.3"),annotation_clip=False)
ax.set_xlabel(r'$v_{CE}$ [mm/s]')
ax.set_xticks([-muspar['b']*muspar['fmax']/muspar['a'],-0.5*muspar['b']*muspar['fmax']/muspar['a'],0,0.5*muspar['b']*muspar['fmax']/muspar['a'],muspar['b']*muspar['fmax']/muspar['a']])
ax.set_xticklabels([r"$\frac{-b \cdot F_{CE}^{max}}{a}$","","0","",r"$\frac{b \cdot F_{CE}^{max}}{a}$"])
ax.set_xticklabels([r"$-b \cdot F_{CE}^{max}/a$","","0","",r"$b \cdot F_{CE}^{max}/a$"])
ax.set_xlim(-muspar['b']*muspar['fmax']/muspar['a'],muspar['b']*muspar['fmax']/muspar['a'])
ax.set_ylabel(r"$F_{CE}^{rel}$ [ ]")
ax.set_yticks([0.0,0.25,0.5,0.75,1.0,1.25,1.5])
ax.set_yticklabels(["0.0","","0.5","","1.0",'','1.5'])
ax.set_ylim(0,1.75)
# %% Panel D: q(t)
ax = axs[3,1]
def Solve(t,gamma,parms,tStim):
stim = ((t>=tStim[0]) & (t<tStim[1]))*1
gamma_0 = parms['gamma_0'];
gammad = (stim>=gamma)*((stim*(1-gamma_0)-gamma + gamma_0)/parms['tact']) + (stim<gamma)*((stim*(1-gamma_0)-gamma + gamma_0)/parms['tdeact']);
return gammad, stim
state0 = [muspar['gamma_0']]
n = 81
tspan = [0, 0.5]
tStim = [0.1,0.2]
fun = lambda t, x: Solve(t,x,muspar,tStim)[0]
sol = solve_ivp(fun,tspan,state0,method='RK45',max_step=1e-3,rtol=1e-4,atol=1e-8)
stim = Solve(sol.t,sol.y,muspar,tStim)[1]
time = sol.t; gamma = sol.y[0];
n = len(gamma)
# q = ActState(gamma,1,muspar)[0]
for iL,lcerel in enumerate([1.4,1.0,0.6]):
if iL%2 == 0:
clr = "k"
else:
clr = "gray" # gray if we want to switch..
q = hillmodel.ActState(gamma,lcerel,muspar)[0]
ax.plot(time,q,color=clr)
idx = np.argmin(np.abs(q[200:]-0.5))+200
angle = np.rad2deg(np.arctan2(np.gradient(q,time)[idx], 1))
ax.text(time[idx],q[idx], '{:.2f}'.format(lcerel), ha='center', va='center', fontsize = 7, color=clr,
transform_rotates_text=True, rotation=angle, bbox=dict(ec='1', fc='1', pad=0.1))
ax.annotate('$L_{CE}^{rel}$', xy=(time[idx]-0.005, q[idx]), xycoords='data', xytext=(-42, -19),
textcoords='offset points', arrowprops=dict(arrowstyle="->",lw=0.5, connectionstyle="arc3,rad=-.3"))
ax.set_xlabel(r"Time [s]")
ax.set_ylabel(r"$q$ [ ]")
ax.set_xlim(0,0.325)
ax.set_ylim(0,1.125)
ax.set_xticks([0,0.05,0.10,0.15,0.20,0.25,0.3]);
ax.set_xticklabels(["0","","0.1","","0.2",'','0.3'])
ax.set_yticks([0.0,0.25,0.5,0.75,1.0])
ax.set_yticklabels(["0.0","","0.5","","1.0"])
# %% STIM bar of Panel D
ax = axs[2,1]
import matplotlib.patches as patches
from matplotlib.path import Path
verts = [
(tStim[0], 0.99), # left, bottom
(tStim[0], 1.01), # left, top
(tStim[1], 1.01), # right, top
(tStim[1], 0.99), # right, bottom
]
codes = [
Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
]
path = Path(verts, codes)
patch = patches.PathPatch(path, facecolor='k', lw=0)
ax.add_patch(patch)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.set_xticks([]);
ax.set_yticks([]);
ax.set_xlim(0,0.325)
ax.set_ylim(0.98,1.02)
ax.autoscale(enable=True, axis='y', tight=True)
cust_fig.add_labels(fig, axs.flatten(), ['','','A','B','','','C','D'])
axs[2,1].label = 'STIM bar'
# %% Save and display
plt.show()
fig.savefig("m_hill_model.svg", bbox_inches="tight", pad_inches=0)
fig.savefig("m_hill_model.pdf", bbox_inches="tight", pad_inches=0)
#%% Checks
if check_size == True or check_size == 'True':
cust_fig.report_axes_size(fig,axs.flatten())
cust_fig.report_fig_size("m_hill_model.svg")
cust_fig.report_fig_size("m_hill_model.pdf")