$#$Adapt0.m#$#function Adapt0 %function Adapt0; % Theo Arts, Maastricht University % April24, 2008, email: t.arts@bf.unimaas.nl global Par save ParTemp Par; %saves last intermediate solution % Assessment of stationarity of flows Str= '[Par.ValveRVen.q,Par.ValveRArt.q,Par.ValveLVen.q,Par.ValveLArt.q]'; Aux= eval(Str); FlowVec= mean(Aux(2:end,:)); % test on presence of FlowVec with right size if isfield(Par.Adapt, 'FlowVec'); FlowVecPrev= Par.Adapt.FlowVec; if length(FlowVecPrev)~=length(FlowVec); FlowVecPrev= 0*FlowVec; end else FlowVecPrev= 0*FlowVec; end ErrFlow= std(FlowVec-FlowVecPrev)/mean(FlowVec); % instationarity Par.Adapt.FlowVec= FlowVec; disp('Flow RVen RArt LVen LArt Err (ml/s), ErrFlow :'); disp(num2str([FlowVec*1e6,ErrFlow],4)); % ---- end flow error calculation % === identify parameter to make stationary ==== Par.Adapt.StrInOut={... 'Par.Lv.V',... 'Par.Rv.V',... 'Par.La.V',... 'Par.Ra.V',... 'Par.TubeLArt.V',... 'Par.TubeRArt.V',... 'Par.TubeRVen.V',... 'Par.TubeLVen.V',... }; Par2InOut; %writes to Par.Adapt.In % Peripheral resistance settings Par.LRp.R = (Par.General.p0-mean(Par.TubeRVen.p))/Par.General.q0; Par.RRp.R = Par.General.pDropPulm/mean(Par.RRp.q); % NotEqTo q0 due to shunt flows % Estimate AV-delay TauAv=0.185*Par.General.tCycle; Par.General.TauAv= TauAv; Par2InOut; %writes to Par.Adapt.Out % === Faster Steady State if Par.Adapt.Fast; % === if Error==small, ending is made faster if size(Par.Adapt.Out,1)>1; % Escape if steady state is reached ErrVec= log( Par.Adapt.Out(end,:)./Par.Adapt.In(end,:) ); if sqrt(mean(ErrVec .^2 ))<0.001 Par.General.tEnd= Par.General.tEnd-0.5*(Par.General.tEnd-Par.t(end)); end end; %=== ERROR criterium on flow stationarity SteadyState(0.2); % sets new initial conditions to StrInOut-variables end disp(' '); % get the initial condition for next beat, Par is most compact information to % start the simulation Par2SVar; % load physiologic data in record of state variables Par.SVar %SVarDot= CircSVarDot(0,Par.SVar(end,:)',[]); % Compact Par-structure+++++++ CircSVarDot(0,Par.SVar(end,:)',[]); % Compact Par-structure return $#$AdaptExc.m#$#function AdaptExc %function Par= AdaptExc(Par); % Simulates adaptation of vessels and chambers to current hemodynamics % Theo Arts, Maastricht University, April 23, 2008 global Par save ParTemp Par; %saves intermediate solution % % Assessment of stationarity of flows Str= '[Par.ValveRVen.q,Par.ValveRArt.q,Par.ValveLVen.q,Par.ValveLArt.q]'; Aux= eval(Str); FlowVec= mean(Aux(2:end,:)); % test on presence of FlowVec with right size if isfield(Par.Adapt, 'FlowVec') FlowVecPrev= Par.Adapt.FlowVec; if length(FlowVecPrev)~=length(FlowVec); FlowVecPrev= 0*FlowVec; end else FlowVecPrev= 0*FlowVec; end ErrFlow= std(FlowVec-FlowVecPrev)/mean(FlowVec); Par.Adapt.FlowVec= FlowVec; disp('Flow RVen RAv RArt LVen LAv LArt Err (ml/s), ErrFlow :'); disp(num2str([FlowVec*1e6,ErrFlow],4)); %=== end flow error calculation %=== Fast adapt convergence to reach steady state Par.Adapt.StrInOut={... 'Par.Lv.Sarc.SfPas',... 'Par.Sv.Sarc.SfPas',... 'Par.Rv.Sarc.SfPas',... 'Par.La.Sarc.SfPas',... 'Par.Ra.Sarc.SfPas',... 'Par.Lv.AmRef',... 'Par.Sv.AmRef',... 'Par.Rv.AmRef',... 'Par.La.AmRef',... 'Par.Ra.AmRef',... 'Par.TubeRVen.AWall',... 'Par.TubeRArt.AWall',... 'Par.TubeLVen.AWall',... 'Par.TubeLArt.AWall',... 'Par.Peri.pAdapt',... 'Par.Lv.VWall',... 'Par.Sv.VWall',... 'Par.Rv.VWall',... 'Par.La.VWall',... 'Par.Ra.VWall',... 'Par.Ra.V',... 'Par.Rv.V',... 'Par.La.V',... 'Par.Lv.V',... 'Par.TubeRVen.V',... 'Par.TubeRArt.V',... 'Par.TubeLVen.V',... 'Par.TubeLArt.V',... }; %==== if ErrFlow<0.10; Par2InOut;% store input signals % Peripheral resistance settings Par.LRp.R = (Par.General.p0-mean(Par.TubeRVen.p))/Par.General.q0; Par.RRp.R = Par.General.pDropPulm/mean(Par.RRp.q); % NotEqTo q0 due to shunt flows % Estimate AV-delay TauAv=0.185*Par.General.tCycle; Par.General.TauAv= TauAv; %=== Adapt tube wall thickness disp('Sequence: LArt,RArt,LVen,RVen') Par.TubeLArt= TubeAdapt(Par.TubeLArt,{'WallVolume'}); Par.TubeRArt= TubeAdapt(Par.TubeRArt,{'WallVolume'}); Par.TubeLVen= TubeAdapt(Par.TubeLVen,{'WallVolume'}); Par.TubeRVen= TubeAdapt(Par.TubeRVen,{'WallVolume'}); %=== Adapt sheet properties JunctionAdapt; %TriSeg correction of AmRef per sheet by EAmRef % anatomical dimensions, i.e. larger septum disp('Sequence: La,Ra,Lv,Sv,Rv') [Par.La,ELa]= SheetAdapt(Par.La,{'WallVolume','WallArea','EcmStress'}); [Par.Ra,ERa]= SheetAdapt(Par.Ra,{'WallVolume','WallArea','EcmStress'}); [Par.Lv,ELv]= SheetAdapt(Par.Lv,{'WallVolume','WallArea','EcmStress'}); [Par.Sv,ESv]= SheetAdapt(Par.Sv,{'WallVolume','WallArea','EcmStress'}); [Par.Rv,ERv]= SheetAdapt(Par.Rv,{'WallVolume','WallArea','EcmStress'}); %=== Adapt pericardium Par.Peri=PeriAdapt(Par.Peri); ErrSheet=sqrt(mean([ELv,ESv,ERv,ELa,ERa].^2));% RMS error disp(['VWall(ml)= ',num2str( 1e6*(Par.Lv.VWall+Par.Sv.VWall) )]); disp(['ErrSheet = ',num2str( ErrSheet )]); Par2InOut;% store output signals end % === if Error==small, ending is made faster if size(Par.Adapt.Out,1)>1; % Escape if steady state is reached ErrVec= log( Par.Adapt.Out(end,:)./Par.Adapt.In(end,:) ); if sqrt(mean(ErrVec .^2 ))<0.001 Par.General.tEnd= Par.General.tEnd-0.5*(Par.General.tEnd-Par.t(end)); end end; % === Faster Steady State if Par.Adapt.Fast; %=== ERROR criterium on flow stationarity SteadyState(0.2); % sets new initial conditions to StrInOut-variables end disp(' '); %=== make ready for next beat Par2SVar; CircSVarDot(0,Par.SVar(end,:)',[]); return %========================================================================== % Auxilary functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< %========================================================================== function Peri=PeriAdapt(Peri) pMax = max(Peri.p); Fac_pMax= Peri.pAdapt/pMax; Peri.VRef= Peri.VRef/(Fac_pMax^(0.3/Peri.k)); disp(['Pericard adaptation: ',num2str(log(Fac_pMax))]); return function JunctionAdapt % Adjustment factor for Adapt.SfPasMax for sheets around junctions % Keeps total heart geometry in shape by variation of passive stiffness % per sheet. Heuristic approach, not much physiological background. % EAmRef= needed increase of area AmRef, (value>0) -> dilatation global Par AL=Par.Lv.AmRef; AS=Par.Sv.AmRef; AR=Par.Rv.AmRef; Par.Lv.EAmRef= 0; % Par.Sv.EAmRef= 5*log( AL*AR/((AL+AR)*AS*1.5) ); Par.Sv.EAmRef= 5*log( AL*AR/((AL+AR)*AS*1.0) );% larger Sv.AmRef Par.Rv.EAmRef= 0; return $#$AdaptRest.m#$#function AdaptRest %function Par= AdaptRest(Par); % Simulates adaptation of vessels and chambers to current hemodynamics global Par save ParTemp Par; %saves intermediate solution %=== ERROR criterium on flow stationarity FlowVec= mean([Par.ValveRVen.q,Par.ValveRArt.q,... Par.ValveLVen.q,Par.ValveLArt.q]); if isfield(Par.Adapt, 'FlowVec') FlowVecPrev= Par.Adapt.FlowVec; if length(FlowVecPrev)~=length(FlowVec); FlowVecPrev= 0*FlowVec; end else FlowVecPrev= 0*FlowVec; end ErrFlow= std(FlowVec-FlowVecPrev)/mean(FlowVec); %... %+ abs(1- mean(Par.ValveRVen.q)/Par.General.q0); Par.Adapt.FlowVec= FlowVec; disp('Flow RVen RArt LVen LArt Err (ml/s), ErrFlow :'); disp(num2str([FlowVec*1e6,ErrFlow],4)); %=== end flow error calculation %=== Fast adapt convergence records Par.Adapt.StrInOut={... 'Par.Ra.V',... 'Par.Rv.V',... 'Par.La.V',... 'Par.Lv.V',... 'Par.TubeRVen.V',... 'Par.TubeRArt.V',... 'Par.TubeLVen.V',... 'Par.TubeLArt.V',... 'Par.TubeRVen.A0',... 'Par.TubeRArt.A0',... 'Par.TubeLVen.A0',... 'Par.TubeLArt.A0',... }; %=== Carrying out adaptation if (ErrFlow<0.05+0.1*Par.Adapt.Fast); % Adaptation only if ErrFlow indicates 'stationarity' Par2InOut;% store input signals % Peripheral resistance settings Par.LRp.R = (Par.General.p0-mean(Par.TubeRVen.p))/Par.General.q0; Par.RRp.R = Par.General.pDropPulm/mean(Par.RRp.q); % NotEqTo q0 due to shunt flows % Estimate AV-delay TauAv=0.185*Par.General.tCycle; Par.General.TauAv= TauAv; %=== Adapt tube diameters Par.TubeLArt= TubeAdapt(Par.TubeLArt,{'Diameter'}); Par.TubeRArt= TubeAdapt(Par.TubeRArt,{'Diameter'}); Par.TubeLVen= TubeAdapt(Par.TubeLVen,{'Diameter'}); Par.TubeRVen= TubeAdapt(Par.TubeRVen,{'Diameter'}); if 1 %Adapt valve diameters to tube diameters Par.ValveLArt.AOpen=mean(Par.TubeLArt.A); Par.ValveRArt.AOpen=mean(Par.TubeRArt.A); Par.ValveLVen.AOpen=mean(Par.TubeLVen.A); Par.ValveRVen.AOpen=mean(Par.TubeRVen.A); Par.ValveLAv.AOpen= 1.5*Par.ValveLArt.AOpen; Par.ValveRAv.AOpen= 1.5*Par.ValveRArt.AOpen; aux=1.0; % factor representing AmDead / Valve Orifice Area Par.La.AmDead= aux*mean(Par.ValveLAv.AOpen+Par.ValveLVen.AOpen... +Par.ValveASD.AOpen); Par.Ra.AmDead= aux*mean(Par.ValveRAv.AOpen+Par.ValveRVen.AOpen... +Par.ValveASD.AOpen); Par.Lv.AmDead= aux*mean(Par.ValveLAv.AOpen+Par.ValveLArt.AOpen); Par.Sv.AmDead= aux*mean(Par.ValveVSD.AOpen); Par.Rv.AmDead= aux*mean(Par.ValveRAv.AOpen+Par.ValveRArt.AOpen); end Par2InOut;% store output signals end %--- % === if Error==small, ending is made faster if size(Par.Adapt.Out,1)>1; % Escape if steady state is reached ErrVec= log( Par.Adapt.Out(end,:)./Par.Adapt.In(end,:) ); if sqrt(mean(ErrVec .^2 ))<0.001 Par.General.tEnd= Par.General.tEnd-0.5*(Par.General.tEnd-Par.t(end)); end end; % === Faster Steady State if Par.Adapt.Fast; %=== ERROR criterium on flow stationarity SteadyState(0.2); % sets new initial conditions to StrInOut-variables end disp(' '); %=== make ready for next beat Par2SVar; CircSVarDot(0,Par.SVar(end,:)',[]); return $#$CavityMech.m#$#function Cavity= CavityMech(Cavity) % function Cavity= CavityMech(Cavity); % Cavity volume -> Cavity pressure % Structure Cavity. % Sarc: [1x1 struct] : Changed Sarcomere, may contain many patches % AmRef: 0.0120 : In Area with Sarc.Ls=LsRef % VWall: 1.0658e-004 : In Wall volume % V: [851x1 double]: In Cavity volume % VDot: [851x1 double] % Vm: [851x1 double] % Xm: [851x1 double] % Ym: [851x1 double] % Am: [851x1 double]: Changed Wall area % Cm: [851x1 double]: Changed Wall curvature % T: [851x1 double]: Changed Wall tension % pTrans: [851x1 double]: Changed Transmural pressure % DTDAm: [851x1 double]: Changed Wall stiffness % p: [851x1 double]: Changed Cavity pressure % A: [851x1 double]: Changed Cavity short axis cross-section % Z: 0 : Changed Impedance between pIn and p % pIn: [851x1 double]: Changed External hemodynamic pressure % qRemod: [851x1 double]: Changed Flow used for diameter adaptation % Y: [] % YDot: [] % Tau: [] % Collecting input rhob = 1050; VWall= Cavity.VWall; % wall volume V = max(Cavity.V,0); % cavity volume % Segment mechanics full sphere Vm= V+0.5*VWall; % mid-wall enclosed volume Cm= (4*pi./(3*Vm)).^(1/3); % curvature=1/radius Cavity.Am= (4*pi)./Cm.^2; % mid-wall surface area Cavity.Cm= Cm; % Cavity as Sheet is encapsulating the cavity Cavity= SheetMech(Cavity); % 2D-sheet Am,Cm,VWall,AmRef-> T, Len= 2* Vm.^(1/3) ; % estimate of long-axis length LV A = (V+0.1*VWall) ./Len ; % crude estimate of cross-section of a (left) ventricle % Estimate of tube properties for wave guidance Cavity.p = Cavity.pTrans ; % cavity pressure, assuming pExternal=0 Cavity.A = A ; % cross-sectional area for valve inflow and outflow pressure Cavity.Z = 0.2*sqrt(rhob*Len.*abs(Cavity.DTDAm))./A; % Compatibitlity with Tube % Cavity.Z = 0; % Compatibitlity with Tube % Initializations Cavity.pIn = Cavity.p; Cavity.VDot = 0; Cavity.qRemod= 0;$#$CircAdapt.m#$#function CircAdapt % function Par= CircAdapt(Par); % Theo Arts, Maastricht University % April 24, 2008. Email: t.arts@bf.unimaas.nl % === Solution of differential equations, defined in global structure 'Par' % Solution of state variables stored in Par.SVar global Par Par.SVar=[]; Par2SVar; Par.SVar=Par.SVar(:,end); % load general parameters Dt = Par.General.Dt; Par.Adapt.In=[]; Par.Adapt.Out=[]; % reset of storage input/output per beat % solving differential equations Par2SVar; % setting initial condition of differential equation in Par.SVar SVar= Par.SVar(end,:); % sets start condition % Right atriual activation used as start of heart cycle % tRa= Par.Ra.Sarc.ActivationDelay; % Rg = find(tRa>Par.t(end)+Dt); % select relevant RA-triggers % tRa=tRa(Rg); iRa=1; nRa=length(tRa); % tRa=end points of beats Par.General.tEnd= Par.t(end)+Par.General.DtSimulation; while Par.t(end): ','s'); a=[c2,'c']; c=a(1); % convert to 'c' switch lower(c) case('l'); % load file with Par-structure [FileName,PathName] = uigetfile('*.mat','Choose file: '); load([PathName,FileName]); c='c';ShowMenu=false; case('r') % load reference file with Par-structure load('ParRef'); c='c';ShowMenu=false; case('n'); % new Par-structure from scratch CircNew; c='c';ShowMenu=false; case('c'); % continuation of simulation load([PathName,FileName]); c='c';ShowMenu=false; otherwise ShowMenu=true; end end else if exist('ParRef.mat','file'); c2=input('[N]ew, [R]eference, [L]oad,: ','s'); a=[c2,'c']; c=a(1); % convert to 'c' switch lower(c) case('l'); % load file with Par-structure [FileName,PathName] = uigetfile('*.mat','Choose file: '); load([PathName,FileName]); c='c';ShowMenu=false; case('r') load('ParRef'); c='c'; otherwise CircNew; c='c'; end else CircNew; % Parameter initialization, some remodeling rules inclusive % Generates parameter structure Par and initial conditions of the variables Par c='c'; end end % Default initialization G=Par.General; G.DtSimulation=1.5*G.tCycle; % standard duration of simulation Par.Adapt.FunctionName='Adapt0'; % No adaptation Par.Adapt.In=[]; Par.Adapt.Out=[]; % reset of storage input/output per beat Par.Adapt.Fast= 0; % regular beat to beat sequence %XXXX Menu for changing hemodynamic variables and adaptation condition OK=1; NY='NY'; while OK; disp(' '); disp(['[P]ressure (kPa): ',num2str(G.p0/1e3)]); disp(['[F]low (ml/s): ',num2str(G.q0*1e6)]); disp(['[T]ime of beat (ms): ',num2str(G.tCycle*1e3)]); disp(['[D]uration simulation (s): ',num2str(G.DtSimulation)]); disp(['Adapt n[O]ne [R]est,[E]xercise : ',Par.Adapt.FunctionName]); disp(['Faster steady state [Y]/[N] : ',NY(Par.Adapt.Fast+1)]); disp( ' = Continue'); c1=input('Choose Letter : ','s'); switch lower(c1); case 'p' G.p0=input('Mean Arterial Pressure (kPa): ')*1e3; case 'f' G.q0=input('Systemic Flow (ml/s): ')/1e6; case 't' G.tCycle=input('Cycle Time (ms): ')*1e-3; case 'd' G.DtSimulation=input('Duration of simulation (s): '); case 'o' Par.Adapt.FunctionName='Adapt0'; G.DtSimulation=1.5*G.tCycle; case 'r' Par.Adapt.FunctionName='AdaptRest'; G.DtSimulation=50*G.tCycle; case 'e' Par.Adapt.FunctionName='AdaptExc'; G.DtSimulation=100*G.tCycle; case 'y' Par.Adapt.Fast=1; G.DtSimulation=max(30*G.tCycle,G.DtSimulation); case 'n' Par.Adapt.Fast=0; otherwise OK=0; end end %WriteSVarFiles; % needed when changing identity of state variables % === Solves SVar for problem defined in parameter structure 'Par' Par.General=G; CircAdapt; %generate solution %=== Saving State Variables and Model Parameters save Par Par; %save compact solution as structure Par in file 'Par' disp('Differential equation has been solved'); CircAdaptDisplay; % graphical display % Structure Par has been extended with solution for all variables as a % function of time $#$CircNew.m#$#function CircNew(varargin) % function Par= CircNew(varargin); % Theo Arts, Maastricht University % May, 2008, email: t.arts@bf.unimaas.nl % Reference: Arts et al., Am J Physiol Heart Circ Physiol. % 2005;288:1943-1954. % Initialization of simulation of hemodynamics and cardiac mechanics % Left atrium, mitral valve, left ventricle, aortic valve, arteries, venes, % Right atrium, tricuspid valve, right ventricle, pulmonary arteries, lung % venes % Par is a global structure, containing parameters and variables % with sufficient information to start the problem % The structure of Par can be viewed by executing % If varargin is something: some properties are copied from Par % needed functions: Timing, CavityMech, TriSeg global Par; %=== Default values qRest = 85e-6 ; % Cardiac Output, mean systemic flow pRest = 12200 ; % mean systemic blood pressure pDropPulm = 1500 ; % pressure drop in pulmonary circulation pResp = 0*3000; % Thoracic pressure variations TimeFac = 1 ; % Time scale for contraction pulse %==== Copies available Par data in new Par-structure if nargin== 1; %if INPUT==Par structure, this is used as initial Par Par= varargin{1}; General= Par.General; % overwriting of defaults if isfield(General,'qRest' ); qRest= General.qRest ; end if isfield(General,'pRest' ); qRest= General.pRest ; end if isfield(General,'pDropPulm'); qRest= General.pDropPulm; end if isfield(General,'TimeFac' ); qRest= General.TimeFac ; end else Par= []; General= []; % default end Par.t=0; Par.tDot=1; %---- %==== Time scaling by Systemic flow for Body size tCycleRest = 0.85*(qRest/85e-6)^(1/3); % HR-scaling: Dawson et al Dt = 0.0005*2^round(log(tCycleRest/0.2)/log(2)); % time discretization ODD-solution (s) DtSimulation = 5.5*tCycleRest ; % minimum duration of simulation (s), will be rounded to a complete cycle %==== Default hemodynamics set to rest tCycle = tCycleRest; p0 = pRest; q0 = qRest; rhob = 1050 ; % blood density %----------------------------- %==== Default Valve properties Valve.rhob= rhob; % blood density Valve.q = 0 ; % initialial condition no flow (SVar) Valve.qDot= 0 ; % SVarDot %==== Default Tube properties Tube.k = 8.0 ; % stiffness exponent vessel wall fibers, determines ZWave+++++ Tube.Adapt.WallStress= 500e3; % Wall stress at peak (systolic) pressure (Pa) Tube.Adapt.vFlowMean = 0.17 ; % Mean flow velocity [m/s], Dawson Tube.Adapt.vImpact = 3.0 ; % Velocity of possible whole body impact, blood shockwave Tube.q0 = qRest; % estimated mean tube flow for adaptation Tube.rhob = rhob ; % blood density (kgm-3) %=== default systemic arterial Tube Tube.p0 = pRest; Tube.Len = 9.1*qRest^(1/3) ; % Dawson scaling for aortic length Tube = TubeInit(Tube) ; % Wall and lumen size adapted %----------------------------------------- %==== Default (LV-) Sarcomere mechanics %==== Adaptation parameters for sarcomere behavior Sarc.Adapt.LsBe = 2.2 ; % end-diastolic sarcomere length with excercise Sarc.Adapt.LsEe = 1.75; % end-systolic sarcomere length with excercise Sarc.Adapt.SfPasMax = 7500;% maximum passive G= stress/Ls %==== Default ventricular sarcomere Sarc.ActivationDelay = 0 ; % time of depolarization set to zero (= p-wave) Sarc.Ef = 0.0 ; Sarc.LsRef = 2.00 ; % [um] reference sarcomere length for AmRef Sarc.Ls0Pas = 1.80 ; % [um] sarcomere length with zero passive stress Sarc.dLsPas = 0.60 ; % [um] LsiStress0Pas+dLsPas equals stress pole Sarc.SfPas = 3500 ; % [Pa] passive stress at Ef=0 Sarc.Lsi0Act = 1.51 ; % Zero force sarcomere length Sarc.LenSeriesElement= 0.04 ; % [um] isometric dLs of series elastic element Sarc.SfAct = 12E4 ; % [Pa] max isometric stress Sarc.vMax = 7.0*0.85/tCycleRest; % [um/s] shortening velocity scaled to HR(Body weight) Sarc.TimeAct = 0.42 * tCycleRest; % [ m/s] duration force pulse % duration at Ls= Lsi0Act (s), (Arts, AJP, 1982) Sarc.TR = 0.25 ; % ratio rise time to TimeAct Sarc.TD = 0.25 ; % ratio decay time to TimeAct Sarc.C = 0.0 ; % [Ca++] concentration factor (SVar) Sarc.CDot = 0.0 ; % SVarDot Sarc.CRest = 0.02 ; % diastolic resting C-value (contractility) Sarc.Lsi = Sarc.Adapt.LsBe; Sarc.LsiDot = 0; %SVarDot %==== Defining Cavity fields Cavity= struct('Sarc',[],'AmRef',[],'AmDead',[],'VWall',[],'V',[],'VDot',[],'Vm',[],... 'Xm',[],'Ym',[],'Am',[],'Cm',[],'T',[],'pTrans',[],'DTDAm',[],... 'p',[],'A',[],'Z',[],'pIn',[],'qRemod',[],'Y',[],'YDot',[],... 'Tau',[],'nSarc',[] ); Cavity.Sarc = Sarc; %---------------------------- %======================= SPECIFIC PROPERTIES ============================= %==== Mean Valve Flows for initial adaptation Par.ValveLVen= Valve; % default Par.ValveLAv = Valve; % default Par.ValveLArt= Valve; % default Par.ValveRVen= Valve; % default Par.ValveRAv = Valve; % default Par.ValveRArt= Valve; % default Par.ValveASD = Valve; % default Par.ValveVSD = Valve; % default Par.ValveDUCT= Valve; % default %----- %=== valve connections to tubes/chambers Par.ValveLVen.Prox= 'TubeLVen'; Par.ValveLVen.Dist= 'La'; Par.ValveLAv.Prox = 'La' ; Par.ValveLAv.Dist = 'Lv'; Par.ValveLArt.Prox= 'Lv' ; Par.ValveLArt.Dist= 'TubeLArt'; Par.ValveRVen.Prox= 'TubeRVen'; Par.ValveRVen.Dist= 'Ra'; Par.ValveRAv.Prox = 'Ra' ; Par.ValveRAv.Dist = 'Rv'; Par.ValveRArt.Prox= 'Rv' ; Par.ValveRArt.Dist= 'TubeRArt'; Par.ValveDUCT.Prox= 'TubeLArt'; Par.ValveDUCT.Dist= 'TubeRArt'; Par.ValveVSD.Prox = 'Lv' ; Par.ValveVSD.Dist = 'Rv'; Par.ValveASD.Prox = 'La' ; Par.ValveASD.Dist = 'Ra'; %----- %==== Setting independent flows through valves Par.ValveRVen.q = qRest; % RA inflow Par.ValveLVen.q = qRest; % LA inflow Par.ValveASD.q = 0; % ASD flow left->right Par.ValveVSD.q = 0; % VSD flow left->right % calculating dependent flows Par.ValveLAv.q = Par.ValveLVen.q - Par.ValveASD.q ; Par.ValveRAv.q = Par.ValveRVen.q + Par.ValveASD.q ; Par.ValveLArt.q= Par.ValveLAv.q - Par.ValveVSD.q ; Par.ValveRArt.q= Par.ValveRAv.q + Par.ValveVSD.q ; Par.ValveDUCT.q= Par.ValveLVen.q - Par.ValveRArt.q; %==== TUBES %==== Left Arteries= Aorta Par.TubeLArt = Tube ; % Aorta as a tube Par.TubeLArt.p0 = pRest ; % estimated mean pressure Par.TubeLArt.q0 = Par.ValveLArt.q ; % flow to adapt to Par.TubeLArt= TubeInit(Par.TubeLArt) ; % Wall and lumen size adapted %==== Left Venes (pulmonary) Par.TubeLVen = Tube ; % Venes as a tube with open end to heart Par.TubeLVen.k = 10.0 ; % stiffer venular walls AuxSf= Sarc.Adapt.SfPasMax/Sarc.SfAct; %ratio passive/active pressure Par.TubeLVen.p0 = 1.2314*AuxSf*p0 ; % estimated mean venous pressure Par.TubeLVen.q0 = Par.ValveLVen.q ; % flow to adapt to Par.TubeLVen.Len = 0.5*Tube.Len ; % pulmonary vessels are shorter Par.TubeLVen= TubeInit(Par.TubeLVen) ; % Wall and lumen size adapted %==== Right Venes (systemic) Par.TubeRVen = Par.TubeLVen ; % Venes as a tube with open end to heart Par.TubeRVen.p0 = 0.4482*AuxSf*p0 ; % estimated mean venous pressure Par.TubeRVen.q0 = Par.ValveRVen.q ; % flow to adapt to Par.TubeRVen.Len = Tube.Len ; Par.TubeRVen = TubeInit(Par.TubeRVen) ; % Wall and lumen size adapted %==== Right Arteries (Pulmonary) Par.TubeRArt = Par.TubeLArt ; % Pulmonary trunc as a tube Par.TubeRArt.p0 = 1.4160*(pDropPulm+Par.TubeRVen.p0); % estimated mean pressure Par.TubeRArt.q0 = Par.ValveRArt.q ; % flow to adapt to Par.TubeRArt.Len = 0.5*Tube.Len ; % pulmonary vessels are shorter Par.TubeRArt = TubeInit(Par.TubeRArt) ; % Wall and lumen size adapted %==== Peripheral resistance Par.LRp.R= (Par.TubeLArt.p0-Par.TubeLVen.p0)/Par.TubeLArt.q0; %set to normal Par.RRp.R= pDropPulm/Par.TubeRArt.q0; %pulmonary resistance %==== VALVES%+++++++++++++++++++++++++++++++++ % setting isometric load to heart cavities Par.ValveLVen.AOpen = Par.TubeLVen.A0; % pulmonary venous orifice area Par.ValveLVen.ALeak = Par.TubeLVen.A0; % pulmonary venous orifice area Par.ValveLArt.AOpen = Par.TubeLArt.A0; % aortic valve area Par.ValveLAv.AOpen = 1.5*Par.ValveLArt.AOpen; % mitral valve area Par.ValveRVen.AOpen = Par.TubeRVen.A0; % systemic venous orifice area Par.ValveRVen.ALeak = Par.TubeRVen.A0; % systemic venous orifice area Par.ValveRArt.AOpen = Par.TubeRArt.A0; % pulmonary valve area Par.ValveRAv.AOpen = 1.5*Par.ValveRArt.AOpen; % tricuspid valve area Par.ValveDUCT.AOpen = Tube.A0; % Ductus area Par.ValveVSD.AOpen = Tube.A0; % VSD area Par.ValveASD.AOpen = Tube.A0; % ASD area Str= {'LAv', 'LArt', 'RAv', 'RArt', 'DUCT', 'VSD', 'ASD'}; for i= 1:length(Str); s1= ['Par.Valve',Str{i}]; P1= eval(s1); P1.ALeak = 1e-6*P1.AOpen; % setting small valve leaks P1.Len = 0.9*sqrt(P1.AOpen); % inertial length of orifice eval([s1,'= P1;']); end Str= {'LVen', 'RVen'}; for i= 1:length(Str); s1= ['Par.Valve',Str{i}]; P1= eval(s1); P1.ALeak = P1.AOpen; % setting valve leak equal to forward area P1.Len = 0.9*sqrt(P1.AOpen); % inertial length of orifice eval([s1,'= P1;']); end %==== Ductus forward (left->right) closure Par.ValveDUCT.AOpen = Par.ValveDUCT.ALeak; % Ductus cross-section Par.ValveVSD.AOpen = Par.ValveVSD.ALeak ; % VSD cross-section Par.ValveASD.AOpen = Par.ValveASD.ALeak ; % ASD cross-section %==== CAVITIES %==== Left Atrial cavity, estimate for excercise Par.La = Cavity; % default VStrokeAdapt= Par.ValveLVen.q*tCycle; pMax = 0.7755*Par.TubeLVen.pMax ; % maximum La pressure for adaptation Par.La = InitCavity(Par.La,0.2600*VStrokeAdapt,pMax) ; % Adaptation of wall and cavity size %==== Atrium specific sarcomere behavior Par.La.Sarc.vMax = 1.5*Sarc.vMax; % faster contraction rate of atria Par.La.Sarc.TR = 0.5 ; % longer rise time relative to pulse duration Par.La.Sarc.TD = 0.5 ; % longer decay time relative to pulse duration Par.La.Sarc.SfAct= 0.7*Sarc.SfAct; Par.La.Sarc.dLsPas=0.8; Par.La.Sarc.SfPas= 7.0*Sarc.SfPas; Par.La.Sarc.Adapt.SfPasMax= Sarc.Adapt.SfPasMax * 7.0; % stiffer passive behavior %==== Right atrium Par.Ra = Par.La; % default Par.Ra.VWall= 0.3637*Par.La.VWall; %==== Left Ventricular cavity, inner shell Par.Lv = Cavity; pMax = (Par.TubeLArt.p0-Par.TubeRArt.p0); % maximum LV pressure for adaptation Par.Lv = InitCavity(Par.Lv,VStrokeAdapt,pMax); % Adaptation of wall and cavity size %==== Right Ventricular cavity, outer shell Par.Rv = Par.Lv; pMax = Par.TubeRArt.p0; % maximum RV pressure for adaptation Par.Rv = InitCavity(Par.Rv,VStrokeAdapt,pMax); % Adaptation of wall and cavity size %============================================== %=== Ventricular Twin-cavity Par.Sv= Par.Lv; %copy Lv to Sv %=== Dead areas in wall segments due to openings for flows aux=2.0; % factor representing AmDead / Valve Orifice Area Par.La.AmDead= aux*mean(Par.ValveLAv.AOpen+Par.ValveLVen.AOpen... +Par.ValveASD.AOpen); Par.Ra.AmDead= aux*mean(Par.ValveRAv.AOpen+Par.ValveRVen.AOpen... +Par.ValveASD.AOpen); Par.Lv.AmDead= aux*mean(Par.ValveLAv.AOpen+Par.ValveLArt.AOpen); Par.Sv.AmDead= aux*mean(Par.ValveVSD.AOpen); Par.Rv.AmDead= aux*mean(Par.ValveRAv.AOpen+Par.ValveRArt.AOpen); % ventricular cavity volumes/ TriSeg VL=Par.Lv.V; VR=Par.Rv.V; Par.Lv.V=1.0773*VL; %volumes Par.Rv.V=0.7832*VR; AL=Par.Lv.AmRef; AR=Par.Rv.AmRef; %reference wall area Par.Lv.AmRef=0.8081*AL; Par.Sv.AmRef=0.2726*AL; Par.Rv.AmRef=0.8325*AR; hL=Par.Lv.VWall/AL; hR= Par.Rv.VWall/AR; % wall thicknesses hL=hL+0.5*hR; hS=hL; Par.Lv.VWall=Par.Lv.AmRef*hL*2.0980; Par.Sv.VWall=Par.Sv.AmRef*hS*1.8679; Par.Rv.VWall=Par.Rv.AmRef*hR*2.6828; Par.Sv.Y = sqrt(1.0397*Par.Sv.AmRef/pi); Par.Sv.YDot=0; Par.Sv.V = 0.0587*(Par.Lv.V+0.5*Par.Lv.VWall); Par.Sv.Tau= 0.005; % setting time constant of Y-and V-correction for septum %=== Setting unloaded sarcomere length Par.La = CavityMech(Par.La) ; % Estimate of initial sarcomere length and cavity volume Par.La.Sarc.Lsi= Par.La.Sarc.Ls-Par.La.Sarc.LenSeriesElement; Par.Ra = CavityMech(Par.Ra) ; % Estimate of initial sarcomere length and cavity volume Par.Ra.Sarc.Lsi= Par.Ra.Sarc.Ls-Par.Ra.Sarc.LenSeriesElement; [Par.Lv,Par.Sv,Par.Rv]=TriSeg(Par.Lv,Par.Sv,Par.Rv); Par.Sv.V=Par.Sv.Vm; Par.Sv.Y=Par.Sv.Ym; % % Initialization unloaded sarcomere length Par.Lv.Sarc.Lsi= Par.Lv.Sarc.Ls-Par.Lv.Sarc.LenSeriesElement; Par.Sv.Sarc.Lsi= Par.Sv.Sarc.Ls-Par.Sv.Sarc.LenSeriesElement; Par.Rv.Sarc.Lsi= Par.Rv.Sarc.Ls-Par.Rv.Sarc.LenSeriesElement; %--- %===Pericardium V=Par.Lv.V+Par.Rv.V+Par.Lv.VWall+Par.Sv.VWall+Par.Rv.VWall... +Par.La.V+Par.La.VWall+Par.Ra.V+Par.Ra.VWall; Peri.VRef = 1.3120*mean(V); Peri.pAdapt= 500; Peri.k = 10; Par.Peri = Peri; %----- %============================================== %=========== SETTING INITIAL STATE % General data in Par.General General.qRest = qRest; General.pRest = pRest; General.pDropPulm = pDropPulm; General.pResp = pResp; General.tCycleRest = tCycleRest; General.Dt = Dt; General.DtSimulation = DtSimulation; General.tCycle = tCycle; General.p0 = p0; General.q0 = q0; General.rhob = rhob; General.TimeFac = TimeFac; General.TauAv = 0.1252/0.85*tCycle; General.PressureControl = 1; % allowing control of arterial pressure % by change of circulating blood volume, value 0 -> closed circulation Par.General=General; %contains general data %sequence of activation Timing; %timing function WriteSVarFiles; % Writes Script Files for conversions save Par Par return % ============== Auxiliary functions ================ function Tube= TubeInit(Tube) Tube.V = 0; % locate volume (SVar) Tube.VDot = 0; % Tube volume Dot (SVarDot) Tube.pMax = Tube.p0 + 0.5*Tube.rhob*Tube.Adapt.vImpact^2; %temporary estimate Tube = TubeGeometry(Tube); Tube.pMax = Tube.p0 + 2.0*abs(Tube.Z.*Tube.A0*Tube.Adapt.vImpact); Tube = TubeGeometry(Tube) ; % Wall and lumen size adapted Tube.V = Tube.A0*Tube.Len; % Tube volume (SVar) return function Cav = InitCavity(Cav,VStroke,pMax) % estimate of begin-ejection geometry SfMax= 0.4*Cav.Sarc.SfAct; % effective systolic stress Ef=log(Cav.Sarc.Adapt.LsBe/Cav.Sarc.Adapt.LsEe); % systolic strain Aux=exp(Ef/2); Vm= VStroke/(Aux^3-Aux^-3); %mid ejection midwall enclosed volume pDSf=pMax/SfMax; a=exp(3*pDSf); z=(a-1)/(a+1); VWall= 2 * z * Vm; % estimate of wall volume Lambda = Cav.Sarc.Adapt.LsBe/Cav.Sarc.LsRef; Cav.AmRef= 4*pi*(Vm*3/(4*pi))^(2/3) * Lambda^2; %estimate of midwall area Cav.VWall= VWall; Cav.V= Vm - VWall/2 + VStroke/2; % estimate of end-diastolic volume Cav.VDot=0; Cav.nSarc=1; return function Tube=TubeGeometry(Tube) % function Tube=TubeGeometry(Tube); % Theo Arts, Maastricht University % July 31, 2006, email: t.arts@bf.unimaas.nl % Vessel diameter adapts to flow, wall thickness to maximum pressure A0 = Tube.q0/Tube.Adapt.vFlowMean; % A~flow-> constant velocity, Dawson et al pMax = abs(Tube.pMax); SfMax= Tube.Adapt.WallStress; p0 = Tube.p0; k = Tube.k; Tube.AWall= 3*A0/((SfMax/pMax)*(p0/pMax)^(3/(k-3))-1); Tube.A0 = A0; Tube.V(end)= A0*Tube.Len; Tube= TubeDynamics(Tube); % add values of A, p and Z return %Tested OK:SfMax= pMax^(k/(k-3))*(1+3*A0/Tube.AWall)/p0^(3/(k-3)) %EQUATIONS likely to be about OK %ex^3= (1+3AMax/AWall)/(1+3A0/AWall); %s0= (1+3A0/AWall)*p0 %sMax= s0*ex^k %sMax= (1+3AMax/AWall).pMax %known: k,A0,pMax,p0,sMax %to be solved: AWall $#$CircSVarDot.m#$#function SVarDot= CircSVarDot(tDummy,SVar,flag) % function SVarDot= CircSVarDot(tDummy,SVar,flag); % Theo Arts, Maastricht University, % April 24, 2008. Email: t.arts@bf.unimaas.nl % Differential equation in the format to be eaten by odeXX-function % INPUT % tDummy = a dummy variable, irrelevant (needed by odeXX, representing time) % SVar= column vector of State variables, % !!! if SVar is a matrix, column direction -> time % flag = irrelevant, needed for odeXX % global Par = Par-structure, containing all relevant information % OUTPUT % SVarDot= column vector (or matrix) of derivatives of State Variables global Par % Par = structure containing dependent variables Par.SVar= SVar'; % convert column vector SVar to row vector Par.SVar SVar2Par; % script to convert Par.SVar to physiologic values in Par % script text has been generated by CircNew function % read out of general parameters p0 = Par.General.p0; q0 = Par.General.q0; % = X= X= Cavity pressures and cross-sections Par.La = CavityMech(Par.La) ; % sarcomere length and pressures Par.Ra = CavityMech(Par.Ra) ; % [Par.Lv, Par.Sv, Par.Rv]= TriSeg(Par.Lv, Par.Sv, Par.Rv);% Coupled Rv-Lv % Pericardium around 4 cavities Par.Peri.V = Par.Lv.V+Par.Rv.V+Par.Lv.VWall+Par.Sv.VWall+Par.Rv.VWall... +Par.La.V+Par.La.VWall+Par.Ra.V+Par.Ra.VWall; Par.Peri = SackMech(Par.Peri); pPeri = Par.Peri.p; Par.La.p = Par.La.p + 0.5*pPeri; % human pericardium half around La Par.Lv.p = Par.Lv.p + pPeri; Par.Ra.p = Par.Ra.p + pPeri; Par.Rv.p = Par.Rv.p + pPeri; % ----- % ===== Tube pressures, wave impedances and cross-sections Par.TubeLArt = TubeDynamics(Par.TubeLArt); % Aorta Par.TubeLVen = TubeDynamics(Par.TubeLVen); % Pulmonary Veins Par.TubeRArt = TubeDynamics(Par.TubeRArt); % Pulmonary Artery Par.TubeRVen = TubeDynamics(Par.TubeRVen); % Systemic Veins % ===== Systemic peripheral resistance (virtually connnected to zero p) Par.LRp.q= (Par.TubeLArt.p-Par.TubeRVen.p)./Par.LRp.R; % closed system, systemic flow Par.TubeLArt.VDot= Par.TubeLArt.VDot-Par.LRp.q; % outflow of arterial compartment % ===== Blood pressure control by blood volume change of veins FacPressureControl= Par.General.PressureControl; % 0:Closed, 1:Volume change Dq= FacPressureControl*(Par.TubeLArt.p-p0)./Par.LRp.R; CL= Par.TubeLVen.V ./(Par.TubeLVen.k*Par.TubeLVen.p0); %compliance LVen CR= Par.TubeRVen.V ./(Par.TubeRVen.k*Par.TubeRVen.p0); %compliance RVen aL= CL./(CL+CR); DqL= aL.*Dq; DqR= Dq-DqL; % leak flow for blood pressure control Par.TubeRVen.VDot= Par.TubeRVen.VDot+Par.LRp.q-DqR; % closed circulation Par.TubeLVen.VDot= Par.TubeLVen.VDot -DqL; % closed circulation % ==== non-linear pressure-flow relation of pulmonary circulation Dp = Par.TubeRArt.p-Par.TubeLVen.p; Par.RRp.q= Dp.^2/(Par.General.pDropPulm*Par.RRp.R);%power^2 flow increase with pressure drop Par.TubeRArt.VDot = Par.TubeRArt.VDot-Par.RRp.q; Par.TubeLVen.VDot = Par.TubeLVen.VDot+Par.RRp.q; % ===== FLOWS AROUND VALVES % Flow continuity % Time derivatives of volumes and boundary pressures pIn and pOut % Time derivatives of flows % Enlarged leak of atrioventricular valves during diastole % Valve leaflets are less obstructive to flow in diastole dL= Par.Lv.T ./ Par.Lv.Am *(5e-4*Par.Lv.AmRef^2/Par.Lv.VWall); % estimate mitral leak signal PassiveLv= 1-tanh(dL.^2); % If passive, mitral valve leaks Par.ValveLAv.ALeak= max(0.2*PassiveLv,1e-6) * Par.ValveLAv.AOpen; % Mitral regurgitation dR= Par.Rv.T ./ Par.Rv.Am *(5e-4*Par.Rv.AmRef^2/Par.Rv.VWall); % estimate tricuspid leak signal PassiveRv= 1-tanh(dR.^2); % If passive, tricuspid valve leaks Par.ValveRAv.ALeak= max(0.2*PassiveRv,1e-6) * Par.ValveRAv.AOpen; % Valve flows determine VDot in compliances and qDot in valves Par.ValveRVen.AOpen=1;%++ safety, may be removed later ++++++++ Par.ValveLVen.AOpen=1;%++ safety, may be removed later ++++++++ Par.ValveRVen.ALeak=1;%++ safety, may be removed later ++++++++ Par.ValveLVen.ALeak=1;%++ safety, may be removed later ++++++++ ValveStr= {'ValveLVen','ValveLAv','ValveLArt','ValveRVen','ValveRAv',... 'ValveRArt','ValveDUCT','ValveVSD','ValveASD'}; VDot(ValveStr); % VolumeDot of compliances = sum(flows) qDot(ValveStr); % FlowDot in Valve = fu(PressureDifference over Valve) % time Par.t is a state variable Par.tDot= 1; Par.SVarDot= zeros(size(Par.SVar)); Par2SVarDot; % script, generated by CircNew, to fill Par.SVarDot SVarDot= real(Par.SVarDot)'; % odeXX requires SVarDot to be a row vector return % ========= Auxilary functions ============================== %==== VALVE RELATED FUNCTIONS function VDot(ValveStr) % Volume derivatives in neighboring chambers/tubes global Par nValve= size(ValveStr,2); for iValve= 1:nValve; Valve = ValveStr{iValve}; % string: name of valve Prox = Par.(Valve).Prox; % string: name of proximal chamber Dist = Par.(Valve).Dist; % string: name of distal chamber q = Par.(Valve).q; Par.(Prox).VDot = Par.(Prox).VDot -q; Par.(Prox).qRemod= Par.(Prox).qRemod-q; Par.(Prox).pIn = Par.(Prox).p+Par.(Prox).VDot.*Par.(Prox).Z; Par.(Dist).VDot = Par.(Dist).VDot +q; Par.(Dist).qRemod= Par.(Dist).qRemod+q; Par.(Dist).pIn = Par.(Dist).p+Par.(Dist).VDot.*Par.(Dist).Z; end return function qDot(ValveStr) % flow derivative from pressure drop, using Bernouilli and inertia % determines opening/closure of the valves % 'soft' closure facilitates ODE-solution global Par nValve= size(ValveStr,2); for iValve= 1:nValve; Valve = ValveStr{iValve} ; % string: name of valve Prox = Par.(Valve).Prox ; % string: name of proximal chamber Dist = Par.(Valve).Dist ; % string: name of distal chamber q = Par.(Valve).q ; % flow AOpenMax = Par.(Valve).AOpen; % open valve cross-section ALeakMax = Par.(Valve).ALeak; % closed valve cross-section Len = Par.(Valve).Len ; % effective length of flow channel rhob = 1050 ; % density of blood Dp = Par.(Prox).pIn-Par.(Dist).pIn; % pressure drop AProx = Par.(Prox).A; % proximal orifice areas ADist = Par.(Dist).A; % distal orifice areas AExt = min(AProx,ADist); AOpen = min(AOpenMax,AExt);% AOpen <= than A connected vessels ALeak = min(ALeakMax,AExt);% AOpen <= than A connected vessels Par.(Valve).AFw=max([AOpen,ALeak],[],2); Reverse = double(AOpen0); % positive forward pressure xPos= double(x>0); % positive forward flow Closed= (1-xPos).*(1-yPos); % Closed for backward flow & pressure Closing= xPos .*(1-yPos); % Closing phase by backward pressure Open = yPos; % Fully open if pressure forward AClosing= sqrt(x./r).*(AFw-ABw)+ABw; % Soft closure described by % a continuous function A= Closed.*ABw + Open.*AFw + Closing.*AClosing; v1= qV./APr; v2= qV./A; v3= qV./ADi; % velocities % Bernouilli pressure drop DpB= (0.5*rhob) * (1-2*Reverse) .* (... double(qV>0).*max(v2.^2-v1.^2,0) - double(qV<0).*max(v2.^2-v3.^2,0) ); L= 1.5*rhob*( Len./A+ 0.5*(1./sqrt(APr)+1./sqrt(ADi))); % inertia Par.(Valve).L= L; Par.(Valve).qDot= (Dp-DpB)./L; % flow derivative end return $#$MakeParRef.m#$#global Par disp('To a first Steady State') Par.General.DtSimulation= 15*Par.General.tCycle; CircAdapt; disp('Adaptation of vessel diameter at rest') Par.Adapt.FunctionName='AdaptRest'; Par.General.DtSimulation=50*Par.General.tCycle; CircAdapt; disp('To exercise: flow x 3, tCycle / 2') Par.General.q0=3*Par.General.q0; Par.General.tCycle=0.5*Par.General.tCycle; Par.Adapt.FunctionName='Adapt0'; Par.General.DtSimulation=15*Par.General.tCycle; CircAdapt; disp('Adapt geometry of heart and vessels to exercise') Par.Adapt.FunctionName='AdaptExc'; Par.Adapt.Fast=1; Par.General.DtSimulation=50*Par.General.tCycle; CircAdapt; disp('Back to normal') Par.General.q0 = (1/3)*Par.General.q0; Par.General.tCycle= 2.0*Par.General.tCycle; Par.Adapt.FunctionName='Adapt0'; Par.Adapt.Fast=0; Par.General.DtSimulation=10*Par.General.tCycle; CircAdapt; disp('Back to normal') Par.Adapt.FunctionName='Adapt0'; Par.Adapt.Fast=1; Par.General.DtSimulation=50*Par.General.tCycle; CircAdapt; disp('Adapt to Rest') Par.Adapt.FunctionName='AdaptRest'; Par.Adapt.Fast=1; Par.General.DtSimulation=50*Par.General.tCycle; CircAdapt; disp('Two beats in reference state') Par.Adapt.FunctionName='Adapt0'; Par.Adapt.Fast=0; Par.General.DtSimulation=1.5*Par.General.tCycle; CircAdapt; CircAdaptDisplay save ParRef Par $#$MapStructure.m#$#function strTot=MapStructure(Struct,varargin) %function MapStructure(Struct,varargin); %Commonly used as function MapStructure(Struct) %Nested search in structure tree to map sizes of branch arrays within structure %Single records (end branches) are not printed %The varargin facility is needed for the specific nesting in this procedure %INPUT % Struct=stucture to map % varargin{1}=nr, current branch number % varargin{2}=depth, depth in structure tree % varargin{3}=string array of output %OUTPUT % cell array of strings, containing output to print %Theo Arts %==== initialization of Root of structure LenMin=0; %determines whether compact (LenMin=1) or extended map (LenMin=0) if nargin<4; % initialization of Root of structure nr=1; depth=0; strTot={}; if isfield(Struct,'Name'); strTot=[strTot;{Struct.Name}]; %disp(Struct.Name); end else nr=varargin{1}; depth=varargin{2}; strTot=varargin{3}; end if ~isstruct(Struct); return; end; % Escape if not a structure Fn1=fieldnames(Struct); n1=length(Fn1); for i1=1:n1, Twig=getfield(Struct,{1},Fn1{i1}); Len=length(Twig); if (~ischar(Twig)) && (Len>LenMin); % str=[repmat(' ',[1,4*depth]), num2str(nr),' ',Fn1{i1},'(',num2str(Len),')']; str=[repmat(' ',[1,4*depth]), num2str(nr),' ',Fn1{i1},'(',num2str(Len),')']; strTot=[strTot;{str}]; %disp(str); for i2=1:Len strTot=MapStructure(Twig(i2),i2,depth+1,strTot); end end end return $#$Par2InOut.m#$#function Par2InOut % function Par2In; % Reads In records as defined in in Par.AdaptStrInOut, writes to % Par.Adapt.In if this record is short, else to Par.Adapt.Out % May be used to display steady-state error information global Par StrInOut= Par.Adapt.StrInOut; n=length(StrInOut); InOut=zeros(1,n); nIn=size(Par.Adapt.In,2); if nIn~=n && nIn~=0 ; return; end %escape for direct use of Adapt0 if size(Par.Adapt.In,1)<= size(Par.Adapt.Out,1); % ==== append 'InOut' values =================== for i=1:n; InOut (i)=eval([StrInOut{i},'(1 )']); end Par.Adapt.In = [Par.Adapt.In ; InOut ]; else for i=1:n; InOut(i)=eval([StrInOut{i},'(end)']); end Par.Adapt.Out= [Par.Adapt.Out; InOut]; % Estimate of residual error ErrVec= log( Par.Adapt.Out(end,:)./Par.Adapt.In(end,:) ); ErrSteady= sqrt(mean(ErrVec .^2 )); disp(['Error Steady State [1e-4]= ',num2str(round(1e4*ErrSteady))]); end % ---- end append 'Out'------------------------- return$#$Par2SVar.m#$#function Par2SVar global Par Par.SVar(:,1)= Par.t/Par.Scale(1); Par.SVar(:,2)= Par.ValveLVen(1).q/Par.Scale(2); Par.SVar(:,3)= Par.ValveLAv(1).q/Par.Scale(3); Par.SVar(:,4)= Par.ValveLArt(1).q/Par.Scale(4); Par.SVar(:,5)= Par.ValveRVen(1).q/Par.Scale(5); Par.SVar(:,6)= Par.ValveRAv(1).q/Par.Scale(6); Par.SVar(:,7)= Par.ValveRArt(1).q/Par.Scale(7); Par.SVar(:,8)= Par.ValveASD(1).q/Par.Scale(8); Par.SVar(:,9)= Par.ValveVSD(1).q/Par.Scale(9); Par.SVar(:,10)= Par.ValveDUCT(1).q/Par.Scale(10); Par.SVar(:,11)= Par.TubeLArt(1).V/Par.Scale(11); Par.SVar(:,12)= Par.TubeLVen(1).V/Par.Scale(12); Par.SVar(:,13)= Par.TubeRVen(1).V/Par.Scale(13); Par.SVar(:,14)= Par.TubeRArt(1).V/Par.Scale(14); Par.SVar(:,15)= Par.La(1).Sarc(1).C/Par.Scale(15); Par.SVar(:,16)= Par.La(1).Sarc(1).Lsi/Par.Scale(16); Par.SVar(:,17)= Par.La(1).V/Par.Scale(17); Par.SVar(:,18)= Par.Ra(1).Sarc(1).C/Par.Scale(18); Par.SVar(:,19)= Par.Ra(1).Sarc(1).Lsi/Par.Scale(19); Par.SVar(:,20)= Par.Ra(1).V/Par.Scale(20); Par.SVar(:,21)= Par.Lv(1).Sarc(1).C/Par.Scale(21); Par.SVar(:,22)= Par.Lv(1).Sarc(1).Lsi/Par.Scale(22); Par.SVar(:,23)= Par.Lv(1).V/Par.Scale(23); Par.SVar(:,24)= Par.Rv(1).Sarc(1).C/Par.Scale(24); Par.SVar(:,25)= Par.Rv(1).Sarc(1).Lsi/Par.Scale(25); Par.SVar(:,26)= Par.Rv(1).V/Par.Scale(26); Par.SVar(:,27)= Par.Sv(1).Sarc(1).C/Par.Scale(27); Par.SVar(:,28)= Par.Sv(1).Sarc(1).Lsi/Par.Scale(28); Par.SVar(:,29)= Par.Sv(1).V/Par.Scale(29); Par.SVar(:,30)= Par.Sv(1).Y/Par.Scale(30); return $#$Par2SVarDot.m#$#function Par2SVarDot global Par Par.SVarDot(:,1)= Par.tDot/Par.Scale(1); Par.SVarDot(:,2)= Par.ValveLVen(1).qDot/Par.Scale(2); Par.SVarDot(:,3)= Par.ValveLAv(1).qDot/Par.Scale(3); Par.SVarDot(:,4)= Par.ValveLArt(1).qDot/Par.Scale(4); Par.SVarDot(:,5)= Par.ValveRVen(1).qDot/Par.Scale(5); Par.SVarDot(:,6)= Par.ValveRAv(1).qDot/Par.Scale(6); Par.SVarDot(:,7)= Par.ValveRArt(1).qDot/Par.Scale(7); Par.SVarDot(:,8)= Par.ValveASD(1).qDot/Par.Scale(8); Par.SVarDot(:,9)= Par.ValveVSD(1).qDot/Par.Scale(9); Par.SVarDot(:,10)= Par.ValveDUCT(1).qDot/Par.Scale(10); Par.SVarDot(:,11)= Par.TubeLArt(1).VDot/Par.Scale(11); Par.SVarDot(:,12)= Par.TubeLVen(1).VDot/Par.Scale(12); Par.SVarDot(:,13)= Par.TubeRVen(1).VDot/Par.Scale(13); Par.SVarDot(:,14)= Par.TubeRArt(1).VDot/Par.Scale(14); Par.SVarDot(:,15)= Par.La(1).Sarc(1).CDot/Par.Scale(15); Par.SVarDot(:,16)= Par.La(1).Sarc(1).LsiDot/Par.Scale(16); Par.SVarDot(:,17)= Par.La(1).VDot/Par.Scale(17); Par.SVarDot(:,18)= Par.Ra(1).Sarc(1).CDot/Par.Scale(18); Par.SVarDot(:,19)= Par.Ra(1).Sarc(1).LsiDot/Par.Scale(19); Par.SVarDot(:,20)= Par.Ra(1).VDot/Par.Scale(20); Par.SVarDot(:,21)= Par.Lv(1).Sarc(1).CDot/Par.Scale(21); Par.SVarDot(:,22)= Par.Lv(1).Sarc(1).LsiDot/Par.Scale(22); Par.SVarDot(:,23)= Par.Lv(1).VDot/Par.Scale(23); Par.SVarDot(:,24)= Par.Rv(1).Sarc(1).CDot/Par.Scale(24); Par.SVarDot(:,25)= Par.Rv(1).Sarc(1).LsiDot/Par.Scale(25); Par.SVarDot(:,26)= Par.Rv(1).VDot/Par.Scale(26); Par.SVarDot(:,27)= Par.Sv(1).Sarc(1).CDot/Par.Scale(27); Par.SVarDot(:,28)= Par.Sv(1).Sarc(1).LsiDot/Par.Scale(28); Par.SVarDot(:,29)= Par.Sv(1).VDot/Par.Scale(29); Par.SVarDot(:,30)= Par.Sv(1).YDot/Par.Scale(30); return $#$SVar2Par.m#$#function SVar2Par global Par Par.t= Par.Scale(1)*Par.SVar(:,1); Par.ValveLVen(1).q= Par.Scale(2)*Par.SVar(:,2); Par.ValveLAv(1).q= Par.Scale(3)*Par.SVar(:,3); Par.ValveLArt(1).q= Par.Scale(4)*Par.SVar(:,4); Par.ValveRVen(1).q= Par.Scale(5)*Par.SVar(:,5); Par.ValveRAv(1).q= Par.Scale(6)*Par.SVar(:,6); Par.ValveRArt(1).q= Par.Scale(7)*Par.SVar(:,7); Par.ValveASD(1).q= Par.Scale(8)*Par.SVar(:,8); Par.ValveVSD(1).q= Par.Scale(9)*Par.SVar(:,9); Par.ValveDUCT(1).q= Par.Scale(10)*Par.SVar(:,10); Par.TubeLArt(1).V= Par.Scale(11)*Par.SVar(:,11); Par.TubeLVen(1).V= Par.Scale(12)*Par.SVar(:,12); Par.TubeRVen(1).V= Par.Scale(13)*Par.SVar(:,13); Par.TubeRArt(1).V= Par.Scale(14)*Par.SVar(:,14); Par.La(1).Sarc(1).C= Par.Scale(15)*Par.SVar(:,15); Par.La(1).Sarc(1).Lsi= Par.Scale(16)*Par.SVar(:,16); Par.La(1).V= Par.Scale(17)*Par.SVar(:,17); Par.Ra(1).Sarc(1).C= Par.Scale(18)*Par.SVar(:,18); Par.Ra(1).Sarc(1).Lsi= Par.Scale(19)*Par.SVar(:,19); Par.Ra(1).V= Par.Scale(20)*Par.SVar(:,20); Par.Lv(1).Sarc(1).C= Par.Scale(21)*Par.SVar(:,21); Par.Lv(1).Sarc(1).Lsi= Par.Scale(22)*Par.SVar(:,22); Par.Lv(1).V= Par.Scale(23)*Par.SVar(:,23); Par.Rv(1).Sarc(1).C= Par.Scale(24)*Par.SVar(:,24); Par.Rv(1).Sarc(1).Lsi= Par.Scale(25)*Par.SVar(:,25); Par.Rv(1).V= Par.Scale(26)*Par.SVar(:,26); Par.Sv(1).Sarc(1).C= Par.Scale(27)*Par.SVar(:,27); Par.Sv(1).Sarc(1).Lsi= Par.Scale(28)*Par.SVar(:,28); Par.Sv(1).V= Par.Scale(29)*Par.SVar(:,29); Par.Sv(1).Y= Par.Scale(30)*Par.SVar(:,30); $#$SackMech.m#$#function Sack= SackMech(Sack) % function Sack= SackMech(Sack); % Volume -> pressure of pericardial sack % k represents stiffness % V = sack volume % VRef = constraint reference volume % p = pressure VNorm = Sack.V/Sack.VRef; Sack.p = Sack.pAdapt * VNorm .^ Sack.k; return $#$SarcMech.m#$#function Sarc= SarcMech(Sarc) %function Sarc= SarcMech(Sarc); %Theo Arts, Maastricht University. July 30, 2006. %Sarcomere mechanics, G= Stress/sarcomere length= function of time and sarcomere length %contractility and other parameters may be varied by varying Sarc-values % matrix proof: vertical->t; horizontal -> different sarcomeres % structure Sarc. for wall with 3 patches % ActivationDelay: [3x3 double] % Ef: [851x3 double] % LsRef: [1.9973 1.9973 1.9973] % Ls0Pas: [1.8000 1.8000 1.8000] % dLsPas: [0.6000 0.6000 0.6000] % SfPas: [3.9913e+003 3.9913e+003 3.9913e+003] % Lsi0Act: [1.5100 1.5100 1.5100] % LenSeriesElement: [0.0400 0.0400 0.0400] % SfAct: [120000 120000 120000] % vMax: [7 7 7] % TimeAct: [0.4250 0.4250 0.4250] % TR: [0.2500 0.2500 0.2500] % TD: [0.2500 0.2500 0.2500] % C: [851x3 double] % CDot: [851x3 double] % CRest: [0.0200 0.0200 0.0200] % Lsi: [851x3 double] % LsiDot: [851x3 double] % Adapt: [1x1 struct] global Par; % general time %==== Input variables t = Par.t; Ef = Sarc.Ef ; nt = size(Ef,1); % nt: number of times; nr: number of sarcomeres tc = Tc(t,Sarc.ActivationDelay); Lsi = Sarc.Lsi; C = Sarc.C; LenSeriesElement= repmat(Sarc.LenSeriesElement,[nt,1]); TR = repmat(Sarc.TR ,[nt,1]); %TR atrial muscle > ventricular muscle TD = repmat(Sarc.TD ,[nt,1]); TimeAct = repmat(Sarc.TimeAct,[nt,1]); %time scale of contraction pulse Ls0 = repmat(Sarc.Lsi0Act,[nt,1]); %zero active stress sarcomere length Ls0Pas = repmat(Sarc.Ls0Pas ,[nt,1]); dLsPas = repmat(Sarc.dLsPas ,[nt,1]); SfPas = repmat(Sarc.SfPas ,[nt,1]); CRest = repmat(Sarc.CRest ,[nt,1]); %Resting C-value (Ca++ contractility) LsRef = repmat(Sarc.LsRef ,[nt,1]); SfAct = repmat(Sarc.SfAct ,[nt,1]); vMax = repmat(Sarc.vMax ,[nt,1]); % series elasticity and sarcomere shortening velocity % Ls = exp(Ef) * sparse(diag(Sarc.LsRef)); Ls = exp(Ef) .* LsRef; Sarc.Ls = Ls; %=== Active sarcomere % constants related to timing are mainly based on experimental findings L = max(Lsi-Ls0,0.0001); % normalized sarc length for active contraction tA = TimeAct.*(0.65+0.7*L); % activation time lengthens with sarcomere length tR = 0.55*TR.*TimeAct ; % rise time tD = 0.33*TD.*TimeAct ; % decay time (default 0.22) T = tc./tR; x=min(8,max(0,T)); % normalized time during rise of activation ft1= x.^3 .* exp(-x) .* (8-x).^2 * 0.020 ./ tR; % rise of contraction, 'amount of Ca++ release' %Integral T^n exp(-T) = Gamma(n+1) = n! x=(tc-tA)./tD; % normalized time during decay of activation tanhx= 0.5+0.5*sin( sign(x).*min(pi/2,abs(x)) ); %always>0 % Time confined approximation of 1/(1-e^x) function FL= tanh(4.0*L.^2); % regulates increase of contractility with Ls Sarc.CDot= FL.*ft1 - C.*tanhx./tD; % 1st order rise and decay of [Ca++] SfIso = C .* L .* SfAct ; SfRest = CRest .* L .* SfAct ; % Passive ECM and sarcomere kS = Ls0Pas./dLsPas; %Sarcomere stiffness (Titin?) xEcm = kS.*log(Ls./Ls0Pas) ; % normalized ECM strain, may be<0 (pushing) ePos = double(Ls>Ls0Pas); % positive ECM stretch xPos = ePos.*xEcm; % ECM strain, buckling for negative strain xSarc= log(Ls./LsRef)+0.1; % sarcomere strain m1=5; m2=0.12; m3=1; % paameters simulating measured passive characteristics y= m2*(cosh(m1*xPos)-1)+m3*xSarc; DyDEf = m2*m1*kS.*sinh(m1*xPos)+m3; SfPasT = SfPas.*y ; DSfPasDEf= SfPas.*DyDEf; %=== Stress/Ls and stiffness, collected for output Sarc.SfPasT = SfPasT;% passive stress LNormSe = max( -0.02,(Ls-Lsi)./LenSeriesElement); % normalized Series Elastic Element length NoBuckle = tanh(10*C + max(0,1e-4*SfPasT.^2));% NoBuckle: Lsi follows Ls Sarc.LsiDot = max(LNormSe-1,NoBuckle.*(LNormSe-1)) .* vMax; Sarc.Sf = max(0.2*SfPasT,SfPasT) + (SfIso+SfRest).*LNormSe - SfRest; % Sarc.Sf = max(-0.2*abs(SfPasT),SfPasT) + (SfIso+SfRest).*LNormSe - SfRest; Sarc.DSfDEf = DSfPasDEf+SfIso.*Ls./LenSeriesElement; % estimate of sarcomere stiffness return function tc=Tc(t,ttrig) % 'matrix proof' % selects last trigger moment % nt=number of samples in time % nc=number of stored trigger moments % nr=number of sarcomeres (or patches) % selcts for each [nt,nr] event best trigger moment out of nc % tc[nt,nr]= time-trigger moment nt=size(t,1); %time length [nc,nr]=size(ttrig); %number of triggers, number of sarc's ttrigT=ttrig'; A=double(repmat(t,[1,nr*nc])>repmat(ttrigT(:)',[nt,1])); DBnr=repmat((nc+1)*(0:nr-1),[nt,1]); %index shift per column nr B=reshape( sum(reshape(A,[nt*nr,nc]),2), [nt,nr])+DBnr; %index trigger time matrix b=[zeros(1,nr)-10;ttrig]; %trigger time matrix tc=repmat(t,[1,nr])-b(B+1); %trigger time subtracted return $#$SheetAdapt.m#$#function [Sheet,Error]= SheetAdapt(Sheet,AdaptType) % function Sheet= SheetAdapt(Sheet,AdaptType); % Simulates adaptation of sheet geometry to load AdaptWallArea=0; AdaptWallVolume=0; AdaptEcmStress =0; for i=1:length(AdaptType); if strcmp(AdaptType{i},'WallArea' ); AdaptWallArea =1; end if strcmp(AdaptType{i},'WallVolume'); AdaptWallVolume=1; end if strcmp(AdaptType{i},'EcmStress' ); AdaptEcmStress =1; end end % Correction AmRef by strain EAmRef for TriSeg junction EAmRef=0; if isfield(Sheet,'EAmRef'); EAmRef=Sheet.EAmRef; end Sarc= Sheet.Sarc; Split=0; if isfield(Sarc,'Sarc') SheetMaster=Sheet; Sheet=Sarc; Sarc=Sheet.Sarc; Split=1; end % Am= mean(Sheet.Am); Cm= mean(Sheet.Cm); VWall= Sheet.VWall; nt= size(Sheet.Am,1); Lsi = Sarc.Lsi; SfPasT = Sarc.SfPasT; SfAct = max(0, Sarc.Sf-SfPasT); % active stress SfPasMax= max(SfPasT); LsAct = mean(SfAct.*Lsi)./mean(SfAct); % active stress-weighted Ls DLs = 3.0*sqrt(mean(SfAct.*(Lsi-repmat(LsAct,[nt,1])).^2) ./ mean(SfAct)); SfPasMaxAdapt= Sarc.Adapt.SfPasMax; % conventional FacStrain = (Sarc.Adapt.LsBe-Sarc.Adapt.LsEe)./DLs; % Correction AmRef by change of Adapt.SfPasMax for TriSeg junction FacSfPasM = SfPasMaxAdapt*exp(-1.0*EAmRef)./SfPasMax; %ECM overstretch FacLsAct = (Sarc.Adapt.LsEe+0.60*(Sarc.Adapt.LsBe-Sarc.Adapt.LsEe))./LsAct; % Clipping of Fac around 1 with range +/-a ClipFac= @(x,a) exp(a*tanh(log(x)/a)); Clip=0.5; FacStrain = ClipFac(FacStrain ,Clip); FacSfPasM = ClipFac(FacSfPasM ,Clip); FacLsAct = ClipFac(FacLsAct ,Clip); % Calculate adaptation factors switch 2 % various trials, case 2 is besfit matrix, % otherwise is physiological interpretation case 1 % === matrix found by BEST FIT inverse, ParsJ204 FacVWall= FacStrain.^(+0.25) .* FacSfPasM.^(-0.19) .* FacLsAct.^(-0.23); FacAm = FacStrain.^(-0.35) .* FacSfPasM.^(-0.14) .* FacLsAct.^(+2.3 ); FacSfPas= FacStrain.^(-0.55) .* FacSfPasM.^(+0.51) .* FacLsAct.^(-4.4 ); % interpretation to structural remodeling: % high sarcomere length -> high ECM force, ECM cross-linking, ECM % shrinkage, area decrease, Ls lengthening relative to ECM, AmRef % decrease, apparent ECM stiffening, AmRef decrease % Hypertrophy by ECM stretch and sarcomere stretch % ECM stiffening by stress-weighted strain, sarcomere stretch and low ECM stretch % Dilatation by stress-weighted strain and low sarcomere stretch case 2 % K930 new sensitivity analysis, works OK dEffectdSens=[... 0.3957 -0.4571 -0.3338 -0.3066 -0.0433 0.2091 -0.2350 1.3028 -1.3101]; Aux= log([FacStrain,FacSfPasM,FacLsAct])*dEffectdSens; FacVWall= exp(Aux(1)); FacAm = exp(Aux(2)); FacSfPas= exp(Aux(3)); otherwise % based on case 2, with some physiological interpretation % matrix based on HEURISTIC physiologic hypothesis % Shifting sarc length over passive matrix for best work range FacAm = FacLsAct.^(+2); % keep Am attached to sarc length 2 um FacSfPas = FacLsAct.^(-Sarc.Ls0Pas./Sarc.dLsPas); % increase of sarc % length implies apparent change of ECM stiffness % Hypertrophy mainly by overstretch of sarcomere FacVWall= FacStrain.^(+0.18) .* FacSfPasM.^(-0.19) .* FacLsAct.^(-0.23); % Dilatation mainly by strain softening FacAm = FacAm .* FacStrain.^(-0.35) .* FacSfPasM.^(-0.14) .* FacLsAct.^(+0.3); % Tissue softening mainly by (over)stretch of passive matrix FacSfPas= FacSfPas .* FacStrain.^(-0.55) .* FacSfPasM.^(+0.51) .* FacLsAct.^(-1.6); end a= 0.5; % gain of adaptation feedback. 1.0 represents best fit matrix%+++++++++++++++ FacVWall = FacVWall.^a; FacAm = FacAm .^a; FacSfPas = FacSfPas.^a; %=== Carrying out adaptation Error=[]; %hypertrophy by stretch of passive matrix, preservation of fiber length if AdaptWallVolume Vw0= Sheet.VWall; Vw1= Vw0 .* FacVWall; Sheet.VWall=Vw1; Error= [Error,abs(log(FacVWall))]; end if AdaptWallArea %Strain softening of passive matrix by creep % Sheet.AmRef= Sheet.AmRef*FacAm; Sheet.AmRef= Sheet.AmRef.*FacAm; Error= [Error,abs(log(FacAm))]; end if AdaptEcmStress % Shifting active sarcomere range over passive matrix % Passive stress increase with same sarc length is equivalent with % decrease of sarcomere length with same passive stress Sarc.SfPas= Sarc.SfPas.*FacSfPas; Error= [Error, abs(log(FacSfPas))]; % Softening by changing dLsPas seems very OK % Sarc.dLsPas= Sarc.dLsPas * FacSfPas.^-0.3; % Error= [Error, abs(log(FacSfPas))]; end ErrorT= sqrt(mean(Error.^2)); disp(['Strain LsAct FacSfPasM Error*1e3: ',... num2str(round(1000*[log([FacStrain,FacLsAct,FacSfPasM]),ErrorT]),'%+5.3d') ]); Sheet.Sarc= Sarc; if Split==1; %if sheet has been split in patches VWall=sum(Sheet.VWall); AmRef=sum(Sheet.AmRef); SheetMaster.Sarc=Sheet; SheetMaster.VWall= VWall; SheetMaster.AmRef= AmRef; Sheet=SheetMaster; end return $#$SheetMech.m#$#function Sheet= SheetMech(Sheet) % function Sheet= SheetMech(Sheet); % mechanics of a patch of curved thick-walled muscular wall % matrix proof % global Par nSarc= Sheet.nSarc; if nSarc>1 Sheet= MultiPatch(Sheet); else Sarc= Sheet.Sarc; Sarc.Am = Sheet.Am - Sheet.AmDead; %midwall area of myo-wall Sarc.Cm = Sheet.Cm ; %curvature=1/r midwall % nt=size(Sarc.Am,1); Sarc.VWall= Sheet.VWall; %wall volume Sarc.AmRef= Sheet.AmRef; %wall area in reference state % Col1 = ones(nt,1); Sarc=PatchMech(Sarc); Sheet.Sarc = Sarc; Sheet.T = Sarc.T; Sheet.pTrans = Sarc.pTrans; Sheet.DTDAm = Sarc.DTDAm; end return %===================================================================== %=====================Auxilary functions============================== function Sarc =PatchMech(Sarc) % Patch data are stored in structure Sarc % [nt,ns]= number of [time points, patches] % INPUT: midwall area: Am(nt,ns), midwall curvature:Cm(nt,ns), % patch wall volume: VWall(ns), reference midwall area: AmRef(ns) % Output: [wall tension, transmural pressure, wall area stiffness]= % [T,pTrans,DTDAm](nt,ns) Am = Sarc.Am; Cm = Sarc.Cm; VWall= Sarc.VWall; AmRef= Sarc.AmRef; nt= size(Am,1); % [nt,ns]=size(Am); % dimensionless z for wall thickness to cavity radius z = 1.5*repmat(VWall,[nt,1]).*(Cm./Am); z2=z.^2; z4=z2.^2; % for thick-walled sphere: z=VWall/2Vm % TDSf= 0.5*VWall*(1+z2/3+z4/5+....)./Am; TDSf= ( 0.5*(1+0.27*z2)./Am ) .* repmat(VWall,[nt,1]); % best work balance TdA/SfdEf % Polynomial approximate of ratio wall tension/fiber stress. % Sheet fiber strain Sarc.Ef= 0.5*log(Am./repmat(AmRef,[nt,1])) -z2/12 -0.019*z4; Sarc = SarcMech(Sarc); Sf = Sarc.Sf; % stress % Output variables of SarcMech Sarc.T = TDSf .* Sf; % wall tension Sarc.pTrans = 2 * Sarc.T .* Cm; % transmural pressure Sarc.DTDAm = 0.5 * TDSf .* Sarc.DSfDEf ./ Am; % sheet stiffness return function Sheet= MultiPatch(Sheet) CmSh = Sheet.Cm; %curvature fu(t) AmSh = Sheet.Am - Sheet.AmDead; % area fu(t) nt = size(AmSh,1); % number of time points nSarc = Sheet.nSarc; % number of patches (nSarc) Sarc = Sheet.Sarc ; % represents array of patches/sarcomeres VWall = Sarc.VWall; % wall volume row (nSarc) AmRef = Sarc.AmRef; % row (nSarc) Sheet.VWall= sum(VWall,2); % scalar Lsi = Sarc.Lsi; % matrix(nt,nSarc) LsRef = Sarc.LsRef; % row (nSarc) % filling Patches with first estimate of Am with Ls=Lsi AmLsi = Lsi.^2 .* repmat(AmRef./LsRef.^2,[nt,1]); %Am for Ls=Lsi Sarc.Am = AmLsi; %initial estimate Sarc.Cm = repmat(CmSh,[1,nSarc]); %initial estimate %==== Patch wall tension Sarc=PatchMech(Sarc); % solve areas of Patches on balance of Tension % Equations: sum(Patch.Am)==(AmSh=Sheet.Am-Sheet.AmDead) % All Patch.T are equal niT=1; %number of iterations, a single iteration is already quite good for i=1:niT dAdT = 1./Sarc.DTDAm; % compliance per patch DADT = sum(dAdT,2); % total compliance TSh = sum(Sarc.T .* Sarc.Am,2)./sum(Sarc.Am,2);%+++ ATSh = Sarc.Am - (Sarc.T-repmat(TSh,[1,nSarc])) .* dAdT; % TSh stress Am per patch DAmSh = AmSh - sum(ATSh,2); % series elastic dAm, column vector DTSh = DAmSh ./ DADT; % Sheet tension Sarc.Am = ATSh + repmat(DTSh,[1,nSarc]).*dAdT; Sarc = PatchMech(Sarc); % maybe iterations are needed end; Sheet.Sarc = Sarc; % collection of patches Sheet.T = sum(Sarc.T.*Sarc.Am,2)./sum(Sarc.Am,2); Sheet.pTrans= 2 * Sheet.T .* (Sarc.Cm*VWall') /Sheet.VWall ; % transmural pressure Sheet.DTDAm = 1./DADT; return $#$SplitSheet.m#$#function SplitSheet(SheetName,nSarc) % function SplitSheet(SheetName,nSarc) % Splits Sheet in nSarc equally sized Subsheets % if nSarc<=1, inverse action: % then sheets merge into single average sheet % Because of getting new state variables, % >> WriteSVarFiles >> is executed global Par Sheet = getfield(Par,{1},SheetName);% get sheet from Par structure Sheet.nSarc= nSarc;%++++++ Sarc = Sheet.Sarc; FldSarc= {'ActivationDelay','Ef','LsRef','Ls0Pas','dLsPas','SfPas',... 'Lsi0Act','LenSeriesElement','SfAct','vMax','TimeAct','TR',... 'TD','C','CDot','CRest','Lsi','LsiDot'}; nSarc0=size(Sarc.Ef,2); if nSarc==nSarc0; %no change Par=setfield(Par,{1},SheetName,Sheet); return; end; %escape for doing nothing if nSarc>1; % splits one Sheet into n Patches %Parent properties Sarc.VWall= repmat( Sheet.VWall/nSarc,[1,nSarc]); Sarc.AmRef= repmat( Sheet.AmRef/nSarc,[1,nSarc]); Sarc.Am = repmat( Sheet.Am /nSarc,[1,nSarc]); Sarc.Cm = repmat( Sheet.Cm ,[1,nSarc]); for iField=1:length(FldSarc) Sarc.(FldSarc{iField}) = ... repmat(Sheet.Sarc.(FldSarc{iField}),[1,nSarc]); end fn= fieldnames(Sheet.Sarc.Adapt); for iField=1:length(fn) Sarc.Adapt.(fn{iField}) = ... repmat(Sheet.Sarc.Adapt.(fn{iField}),[1,nSarc]); end Sheet.Sarc= Sarc; else % merge sub-sheets to a single sheet with average properties % if ~isfield(Sheet.Sarc,'Sarc'); return; end %if already 1 sheet Sarc=Sheet.Sarc; SarcAdapt=Sarc.Adapt; Sheet.VWall= sum(Sarc.VWall,2); Sheet.AmRef= sum(Sarc.AmRef,2); Sheet.Cm = (Sarc.Cm*Sarc.VWall') ./ sum(Sarc.VWall,2); Sheet.Am = sum(Sarc.Am,2); for iField=1:length(FldSarc) Sheet.Sarc.(FldSarc{iField}) = sum(Sarc.(FldSarc{iField})... * sparse(diag(Sarc.VWall)),2)/Sheet.VWall; end fn= fieldnames(SarcAdapt); for iField=1:length(fn) Sheet.Sarc.Adapt.(fn{iField}) = ... sum(Sarc.Adapt.(fn{iField})... * sparse(diag(Sarc.VWall)),2)/Sheet.VWall; end end Par=setfield(Par,{1},SheetName,Sheet); % Initialization of Par.SVar and Par.SVarDot WriteSVarFiles; Par.SVar=[]; save Par Par; % Save file and SVar Files for new number of state variables clear return $#$SteadyState.m#$#function SteadyState(ErrY0) global Par % read input-output record, take logarithm for relative change In = log(Par.Adapt.In ); Out = log(Par.Adapt.Out); [nx,np]=size(In); % [number of samples, number of parameters] yRef= Out(end,:); y0=zeros(1,np); nxn= ceil(0.5*np); nMin= max(2,nx-nxn); % number of samples used for prediction Rgn=(nMin:nx)'; %indices used for prediction nxn=length(Rgn); nx1=nxn; if nxn>0 X=In(Rgn,:); Y=Out(Rgn,:); U=Out(Rgn-1,:); YRef= repmat(yRef,[nxn,1]); Fac=10; w0=1; while nx1>1 && Fac>1.0 Rg=(nxn-nx1+1:nxn)'; X1 = X(Rg,:)-YRef(Rg,:); Y1 = Y(Rg,:)-YRef(Rg,:); U1 = U(Rg,:)-YRef(Rg,:); w= w0*sqrt(mean((Y1(:)-X1(:)).^2)); Col1= w*ones(nx1,1); Method=1; if Method==1 % no history dependency YmX =Y1-X1; M1 = [YmX ,Col1]; N1 = pinv(M1)*Y1; y0 = w*N1(end,:); end; if Method==2 % history dependency YmXU= [Y1-X1,Y1-U1]; M1 = [YmXU,Col1]; N1 = pinv(M1)*Y1; y0 = w*N1(end,:); end; Fac=norm(y0)/(ErrY0*sqrt(np)); nx1=nx1-1; end % disp(num2str([nxn,nx1+1])); % disp(round(1000*[y0;Y(end,:)-X(end,:)])); end % disp(['Number of used samples for SteadyState: ',num2str(nx1)]); % disp(num2str(y0)); y1= yRef+y0; OutNew=exp(y1); % prediction after exponential %=== substitution of predicted parameter values for i=1:length(OutNew); eval([Par.Adapt.StrInOut{i},'(end)= OutNew(i);']); end return $#$Timing.m#$#function Timing % function Timing; % Theo Arts, Maastricht University, May 26, 2008 % Renders arrays of activation times (Sarc.ActivationDelay) per sarcomere % Start of simulation: Par.t(end) % Duration of simulation: Par.General.DtSimulation. % To be executed at the beginning of a simulation CircAdapt(Par) % Allows variation of spread of activation and irregular heart beat %SHOULD BE IMPROVED!!!! global Par DtLa = 0.00; % dispersion activation time DtRa = 0.00; % dispersion activation time DtLv = 0.00; % dispersion activation time DtSv = 0.00; % dispersion activation time DtRv = 0.05; % dispersion activation time % Reading timing parameters G = Par.General; tCycleRef= G.tCycleRest ; % Reference tCycle for body size scaling tCycle = G.tCycle ; % current cycle time TimeFac = G.TimeFac ; % scaling of contraction time TauAv = G.TauAv ; % AV-delay, controlled in Adapt0 % TimeAct [s] duration of contraction for Ls==LsStress0Act (s) ta=0.15*(tCycle/0.85)*TimeFac; tv=(0.10*tCycleRef +0.40*tCycle)*TimeFac; Par.La= TimeAct(Par.La, ta ); Par.Ra= TimeAct(Par.Ra, ta ); Par.Lv= TimeAct(Par.Lv, tv ); Par.Sv= TimeAct(Par.Sv, tv ); Par.Rv= TimeAct(Par.Rv, tv ); % Delay times electrical activation tRa2La= 0.02 * (tCycleRef/0.85) * TimeFac; tRa2Rv= TauAv; % AV-Delay tRv2Lv= -0.00; % pacing delay LBBB, negative: prepacing tRv2Sv= 0.5*tRv2Lv; % pacing delay LBBB, negative: prepacing tRa2Ra= tCycle; % cycle time % tRa2Ra= tRa2Ra*(1+0.3*(rand-0.5)); % irregular HR %TRIAL, CAN BE IMPROVED+++++++++++++++++++++++++++++++ % Time interval of simulation tStart= Par.t(end); tEnd = tStart+tCycle; % assume Ra as main trigger for activation tRa=tStart; tLa=[]; tLv=[]; tSv=[]; tRv=[]; t=tStart; while t Lv.Vm and Rv.Vm Lv.Vm= Sv.Vm-max(0,Lv.V)-0.5*(Lv.VWall+Sv.VWall); Rv.Vm= Sv.Vm+max(0,Rv.V)+0.5*(Sv.VWall+Rv.VWall); Lv=VYm2ACXm(Lv) ; Sv=VYm2ACXm(Sv) ; Rv=VYm2ACXm(Rv); Lv=SheetMech(Lv); Sv=SheetMech(Sv); Rv=SheetMech(Rv); %determine stiffness matrix with numerical differentiation DV, DY LRef= mean(Y); %averaged over time, scalar DV= 0.04*LRef.^3; DY=0.02*LRef; % differentials StrSheet= {'AmRef','VWall','Vm','Ym','DTDAm','Am','AmDead','T'}; LSRv=StructMerge(repmat([Lv,Sv,Rv],[1,3]),StrSheet); A0 = LSRv.Am; T0 = LSRv.T; LSRv.Vm(:,4:6)= LSRv.Vm(:,1:3)+DV; LSRv.Ym(:,7:9)= LSRv.Ym(:,1:3)+DY; % structureLSRv: % LSRv.Sarc(1).Var(9) % LSRv.Var(9) LSRv = VYm2ACXm(LSRv) ; % 3D geometry to Sheet % Fast linear version of SheetMech: LSRv.T = T0+(LSRv.Am-A0).*LSRv.DTDAm; % Lineair estimate of Tension only LSRv = T2TxTy(LSRv); %=== Equilibrium of tension at node Tx_Twd=LSRv.Tx; Ty_Twd=LSRv.Ty; % summing Tx and Ty over Lv,Sv and Rv wall [nt,nx3]=size(Tx_Twd); Tx_Td= ( reshape( sum( reshape(Tx_Twd',[nx3/3,3*nt]) ),[3,nt] ) )'; Ty_Td= ( reshape( sum( reshape(Ty_Twd',[nx3/3,3*nt]) ),[3,nt] ) )'; Tx0= Tx_Td(:,1); Ty0= Ty_Td(:,1); D11= (Tx_Td(:,2)-Tx0); D21= (Ty_Td(:,2)-Ty0); D12= (Tx_Td(:,3)-Tx0); D22= (Ty_Td(:,3)-Ty0); % Jacobian matrix and matrix inversion Det= max(D11.*D22-D12.*D21,0.1*(abs(D11.*D22)+abs(D12.*D21))); FacV= (-D22.*Tx0+D12.*Ty0)./Det; FacY= (+D21.*Tx0-D11.*Ty0)./Det; % Substitute calculated TriSeg geometry DVm=repmat(FacV*DV,[1,3]); DYm=repmat(FacY*DY,[1,3]); LSRv.Vm(:,1:3)= LSRv.Vm(:,1:3)+DVm; LSRv.Ym(:,1:3)= LSRv.Ym(:,1:3)+DYm; LSRv= VYm2ACXm(LSRv) ; % Copy geometry to wall segments FieldsOut={'Vm','Ym','Am','Cm'}; nField= length(FieldsOut); for iField=1:nField; Field= FieldsOut{iField}; MTwd= LSRv.(Field); Lv.(Field)=MTwd(:,1); Sv.(Field)=MTwd(:,2); Rv.(Field)=MTwd(:,3); end % Get stress, tension and pressures and Sarc.CDot/LsiDot Lv=SheetMech(Lv); Sv=SheetMech(Sv); Rv=SheetMech(Rv); % transmural pressures -> cavity pressures Lv.p= -Lv.pTrans; Rv.p= +Rv.pTrans; Sv.VDot= FacV*DV/Sv.Tau; Sv.YDot= FacY*DY/Sv.Tau; % Calculate hemodynamics of cavities Lv=CavityHemodynamics(Lv); Rv=CavityHemodynamics(Rv); return %---- end of main function ----------------------- %==== Auxilary functions ================================ function Seg=VYm2ACXm(Seg) % input: Vm, Ym, % output: Am, Cm, Xm Vm=Seg.Vm; Ym=Seg.Ym; nt= size(Vm,1); % Solving 3rd order polynomial % Mathematica: Solve[x^3 + 3y^2x - 2V == 0, x] % Q= (V + Sqrt(V^2 + y^6))^(1/3); x= Q - y^2/Q; SignVm= sign(Vm); Vm=abs(Vm); V = (3/pi)*Vm; Q = (V + sqrt(V.^2 + Ym.^6)).^(1/3); Xm = SignVm .* ( Q - Ym.^2 ./ Q ); %calculate midwall area Am and curvature Cm=1/rm x2 = Xm.^2; y2= Ym.^2; x2py2 = x2+y2; Am = pi*x2py2; % midwall cap area Cm = 2*Xm./x2py2; % midwall cap curvature Seg.Xm = Xm ; Seg.Am = Am ; Seg.Cm = Cm ; return function Seg=T2TxTy(Seg) Ym= Seg.Ym; Xm= Seg.Xm; Cm= Seg.Cm; %calculate midwall area Am and curvature Cm=1/rm x2=Xm.^2;y2=Ym.^2; x2py2 = x2+y2; % Sheet mechanics output T = Seg.T; SinA = Ym.*Cm; CosA = (y2-x2)./x2py2; Ty = T.*CosA; Tx = T.*SinA; Seg.Tx = Tx; Seg.Ty = Ty; return %==================================================== function Cavity= CavityHemodynamics(Cavity) %rho= 1050; %Cm = Cavity.Cm; VWall= Cavity.VWall ; % wall volume V = max(Cavity.V,0) ; % cavity volume Len = 2*(V+VWall).^(1/3) ; % estimate of long-axis length LV A = (V+0.1*VWall)./Len ; % crude estimate of cross-section of a (left) ventricle % Estimate of tube properties for wave transmission and valve connections Cavity.A = A ; % cross-sectional area for valve inflow and outflow pressure Cavity.Z = 0 ; % Initializations Cavity.pIn = Cavity.p; Cavity.VDot = 0; Cavity.qRemod= 0; return %==================================================== function SFn= StructMerge(SnF,StrField) % SFn= SnF(1); SFn=[]; for ifield= 1:length(StrField); SFn.(StrField{ifield})= [SnF.(StrField{ifield})]; end return $#$TubeAdapt.m#$#function Tube= TubeAdapt(Tube,AdaptType) % function Tube= TubeAdapt(Tube,AdaptType); % AdaptType= {'Diameter', 'WallVolume'} indicates type of adaptation AdaptDiameter=0; AdaptWallVolume=0; for i=1:length(AdaptType); if strcmp(AdaptType{i},'Diameter') ; AdaptDiameter =1; end if strcmp(AdaptType{i},'WallVolume'); AdaptWallVolume=1; end end % AuxDisplay = [Tube.p0*1e-3,Tube.q0*1e3,Tube.pMax*1e-3,... % Tube.A0*1e+4,Tube.AWall*1e+4]; %=== Sensor signal calculation qRemod = Tube.qRemod; qPos = mean(max(0,qRemod)); qNeg=mean(max(0,-qRemod)); qMean = abs(mean(qRemod)); qAux = median([qPos;qNeg;qMean]); % determines size of entering blood vessel vFlow = qAux/mean(Tube.A); pMax = Tube.pIn + Tube.Z.*Tube.A*Tube.Adapt.vImpact; WallStress= max(pMax.*(1+3*Tube.A/Tube.AWall)); Fac_vFlow = Tube.Adapt.vFlowMean / vFlow; FacWallStress= Tube.Adapt.WallStress / WallStress; Facp0 = Tube.p0 / sqrt( mean(Tube.p.^2) ); %--- %=== Carrying out adaptation if AdaptDiameter; a=0.5; Tube.A0 = Tube.A0 * Fac_vFlow.^(-a) * Facp0^(-a/(Tube.k/3-1)); Tube.p0 = Facp0.^a * sqrt( mean(Tube.p.^2) ); end if AdaptWallVolume a=0.5; Tube.AWall= Tube.AWall * FacWallStress^(-a); Tube.pMax = Tube.pMax * (max(pMax)/Tube.pMax)^a; end %--- % disp(' p0(kPa), qRemod*(L/s), pMax(kPa), A0(cm2), AWall(cm2)'); % disp([AuxDisplay;[Tube.p0*1e-3,Tube.q0*1e3,Tube.pMax*1e-3,... % Tube.A0*1e+4,Tube.AWall*1e+4]]); % disp(['[Fac_vFlow,FacWallStress,Facp0]=',num2str([Fac_vFlow,FacWallStress,Facp0])]); disp(['vFlow,WallStress,p0: ',... num2str(round(1000*log([Fac_vFlow,FacWallStress,Facp0])),'%+5.3d') ]); return $#$TubeDynamics.m#$#function Tube= TubeDynamics(Tube) %function Tube= TubeDynamics(Tube); %Theo Arts, University of Maastricht, July 30, 2006. %Elastic Tube hemodynamics, related to the pressure/flow wave (e.g. aorta) % %INPUT/OUTPUT i/o %Tube. % k: 8 i % p0: 12200 i % Len: 0.4001 i % AWall: 1.2866e-004 i % A0: 5.0000e-004 i % V: [852x1 double] i % A: [852x1 double] changed % p: [852x1 double] changed % Z: [852x1 double] changed % pIn: [852x1 double] changed % VDot: [852x1 double] changed % qRemod: [852x1 double] changed rhob = 1050; AWalld3= Tube.AWall/3; Tube.A = max(0.001*Tube.A0,Tube.V/Tube.Len); %mean cross-section of tube, safe for A0<0 Aux = ((Tube.A+AWalld3)./(Tube.A0+AWalld3)).^(Tube.k/3-1); p = Tube.p0* ( Aux - 0.01*Tube.A0./Tube.A ); %safety for collapse Tube.p = p; Tube.Z = sqrt( rhob*(Tube.k-3) * max(1.0,p)/3) ./ (Tube.A+Tube.AWall/3); % Avoids (too) high Z for low pressure %=== Initializations for CircSVarDot Tube.pIn = p; z0=zeros(size(p)); Tube.VDot = z0; %inflow Tube.qRemod= z0; %through flow for diameter adaptation $#$WorkBalance.m#$#function WorkBalance; % function WorkBalance; % Comparisons of % Work as generated by pumpung of two cavities Lv and Rv % Work as generated by tension and area of the sheets L, S and R % Work as generated by stress and strain in the sarcomeres global Par Dt=Par.General.Dt; FDot= @(a) conv2([a(end);a;a(1)],[+0.5;0;-0.5]/Dt,'valid'); PHem = @(a) a.p.*FDot(a.V); PSheet= @(a) a.T.*FDot(a.Am); PSarc = @(a) a.Sarc.Sf.*FDot(a.Sarc.Ef)*a.VWall; PHemL= PHem(Par.Lv); PHemR= PHem(Par.Rv); PHemTot= PHemL+PHemR; PHemLa = PHem(Par.La); PHemRa = PHem(Par.Ra); PSheetL=PSheet(Par.Lv); PSheetS=PSheet(Par.Sv); PSheetR=PSheet(Par.Rv); PSheetTot= PSheetL+PSheetS+PSheetR; PSheetLa=PSheet(Par.La); PSheetRa=PSheet(Par.Ra); PSarcL=PSarc(Par.Lv); PSarcS=PSarc(Par.Sv); PSarcR=PSarc(Par.Rv); PSarcTot= PSarcL+PSarcS+PSarcR; PSarcLa=PSarc(Par.La); PSarcRa=PSarc(Par.Ra); figure(2); plot([PHemTot,PSheetTot,PSarcTot]) figure(3); plot([PSheetL,PSheetS,PSheetR,PSarcL,PSarcS,PSarcR]) figure(4); plot([PHemLa,PHemRa,PSarcLa,PSarcRa]); disp(['Sum pDV,TdA,SfDEf,Diff: ',num2str(sum([PHemTot,... PSheetTot,PSarcTot,PSheetTot-PSarcTot]))]); return$#$WriteSVarFiles.m#$#function WriteSVarFiles % function WriteSVarFiles; % To be applied on structure 'global Par' % Should be executed with change of number or names of state variables % Writes script files SVar2Par.m, Par2SVarDot.m and Par2SVar.m % Adds Par.Scale to Par-structure for scaling of state variables in ODE % Clears Par.SVar % StrDot is a cell-list of strings, showing the state variables-Dot % String part '~Dot' has been used as identification label for SVar's %============================================================== % Write and save program scripts for reading/writing State Variables %============================================================== global Par; G= Par.General; % Find field strings in structure Par containing 'Dot' StrDot= FindDot(Par); n= length(StrDot); % find SVarDot-variables %==== Make String arrays of SVar and SVarDot StrSVar= {}; StrSVarDot= {}; StrScale= {}; for i= 1:n SDot= StrDot{i}; % string containing xxxDot SvDot= strrep(SDot,'Dot(','Dot(:,'); % String XXXDot, represents SVarDot Sv= strrep(SvDot,'Dot',''); % String with Dot removed: State Variable SScale= Sv(end-2); % Letter used for scaling in ODE nr=str2num(Sv(end-1:end)); % number of SVar's in array, may be >1 StrSVar = [StrSVar ;{Sv }]; % state variables SVar StrSVarDot= [StrSVarDot;{SvDot}]; % SVarDot's StrScale = [StrScale ;{repmat([SScale,','],[1,nr])}]; % letter % last letter -> type of SVar end %=== Scaling of state variables to facilitate numerical solution % characteristic letters become scaling factor V= 10^round(log10(0.1*G.tCycle*G.qRest)); % scaling factors of volume, i= 1; % scaling of sarcomere length q= 10^round(log10(G.qRest)); % scaling of flow C= 1; % scaling of contractility factor t= 1; % scaling of time Y= 10^round(log10(G.tCycle*G.qRest)/3); % scaling of length eval(['Par.Scale= [', [StrScale{:}],']'';']); %column vector for scaling of State Variables SVar stored in Par.Scale % Writing of script files for extracting state variables %=== Creating SVar2Par.m % ... Par.ValveLVen(1).q= Par.Scale(2)*Par.SVar(:,2); ... fid = fopen('SVar2Par.m','w'); fprintf(fid,['function SVar2Par','\n']); fprintf(fid,['global Par','\n']); j=1; % counter for state variables for i= 1:length(StrSVar); StrV= StrSVar{i}(1:end-2); nr= str2num(StrSVar{i}(end-1:end)); if nr==1 strnr1=[ '(:,' , num2str(j) , ')' ]; strnr2=[ '(' , num2str(j) , ')' ]; else strnr1=[ '(:,' , num2str(j) , ':' , num2str(j+nr-1) , ')' ]; strnr2=[ '(' , num2str(j) , ')' ]; end Str= ['Par.',StrV,'= Par.Scale' , strnr2, '*Par.SVar' ,strnr1,';']; fprintf(fid,[Str,'\n']); j=j+nr; end fclose(fid); %=== Creating Par2SVarDot.m % ... Par.SVarDot(:,2)= Par.ValveLVen(1).qDot/Par.Scale(2); ... fid = fopen('Par2SVarDot.m','w'); fprintf(fid,['function Par2SVarDot','\n']); fprintf(fid,['global Par','\n']); j=1; % counter for state variables for i= 1:length(StrSVar); StrV= StrSVarDot{i}(1:end-2); nr= str2num(StrSVarDot{i}(end-1:end)); if nr==1 strnr1=[ '(:,' , num2str(j) , ')' ]; strnr2=[ '(' , num2str(j) , ')' ]; else strnr1=[ '(:,' , num2str(j) , ':' , num2str(j+nr-1) , ')' ]; strnr2=[ '(' , num2str(j) , ')' ]; end Str=['Par.SVarDot',strnr1,'= Par.',StrV,'/Par.Scale',strnr2,';']; fprintf(fid,[Str,'\n']); j=j+nr; end fprintf(fid,['return','\n']); fclose(fid); %=== Creating Par2SVar.m % ... Par.SVar(:,2)= Par.ValveLVen(1).q/Par.Scale(2); ... fid = fopen('Par2SVar.m','w'); fprintf(fid,['function Par2SVar','\n']); fprintf(fid,['global Par','\n']); j=1; for i= 1:length(StrSVar); StrV= StrSVar{i}(1:end-2); nr= str2num(StrSVar{i}(end-1:end)); if nr==1 strnr1=[ '(:,' , num2str(j) , ')' ]; strnr2=[ '(' , num2str(j) , ')' ]; else strnr1=[ '(:,' , num2str(j) , ':' , num2str(j+nr-1) , ')' ]; strnr2=[ '(' , num2str(j) , ')' ]; end Str=['Par.SVar',strnr1,'= Par.',StrV,'/Par.Scale',strnr2,';']; fprintf(fid,[Str,'\n']); j=j+nr; end fprintf(fid,['return','\n']); fclose(fid); % Clear state variables Par.SVar=[]; Par.SVarDot=[]; %----- end script for program file writing return % ============== Auxiliary functions ================ function Str= FindDot(Struct); % Returns array of strings pointing to State VariablesDot in tree structure global Str j; Str= {}; j= 0; FindStr(Struct); disp(['Number of state variables: ',num2str(j)]); % disp(Str'); % display state variables return %= used recursively inside FindDot function StrTot= FindStr(Struct,varargin); % function StrTot= FindStr(Struct,varargin); % Finds Field names with 'Dot' in a tree structure % Algorithm based on function MapStructure(Struct) % Nested search in structure tree % The varargin facility is needed for nesting in this procedure %INPUT % Struct= stucture to map % varargin{1}= nr, current branch number % varargin{2}= depth, depth in structure tree % varargin{3}= string array of output %OUTPUT % cell array of strings, containing output to print %Theo Arts, May 25, 2008 %==== initialization of Root of structure global Str j; if nargin<4; % initialization of Root of structure nr= 1; depth= 0; StrTot= {}; else nr= varargin{1}; depth= varargin{2}; StrTot= varargin{3}; end if ~isstruct(Struct); return; end; % Escape if not a structure Fn1= fieldnames(Struct); n1 = length(Fn1); for i1= 1:n1, %search fields Twig= getfield(Struct,{1},Fn1{i1}); % Twig structure, suited as root % Equivalent with Twig= Struct(1)."Fn1{i1}" if isstruct(Twig); Len= length(Twig); % Array length of Twig else Len= min(1,length(Twig)); end % if (~ischar(Twig)); StrTot{depth+1}= Fn1{i1}; for i2= 1:Len; % for all array elements of Twig StrTot{depth+1}= [Fn1{i1},'(',num2str(i2),').']; if strfind(Fn1{i1},'Dot') & length(strfind(Fn1{i1},'SVarDot'))== 0; % exclude Par.SVarDot from search nDot=size(Struct.(Fn1{i1}),2); %number of SVar in array S1= [StrTot{1:depth+1}]; S= [S1(1:end-4),num2str(nDot,'%02d')]; % storing nDot in string Str= [Str;{S}]; j= j+nDot; % cumulation of state variables % disp(S); end if isstruct(Twig(i2)) StrTot= FindStr(Twig(i2),i2,depth+1,StrTot); end end end return $#$