$#$Adapt0P.m#$#function Adapt0P %function Adapt0P; global P save PTemp P; %saves last intermediate solution % 1st part like Adapt0P controls systemic blood pressure and flow % by adjustment of circulatory blood volume and peripheral resistance % % Assessment of stationarity of flows FlowVec=mean(P.Valve.q); % test on presence of FlowVec with right size if isfield(P.Adapt, 'FlowVec'); FlowVecPrev= P.Adapt.FlowVec; if length(FlowVecPrev)~=length(FlowVec); FlowVecPrev= 0*FlowVec; end else FlowVecPrev= 0*FlowVec; end % flow instationarity error Errq= std(FlowVec-FlowVecPrev)/P.General.q0; % instationarity iqSy= strmatch('SyVenRa',P.Valve.Name); % index systemic venous flow ErrqSy= log(FlowVec(iqSy)/P.General.q0); %relative flow error ErrFlow=sqrt(Errq.^2+ErrqSy.^2); % display of normalized flow P.Adapt.FlowVec= FlowVec; str=[]; for i=1:6 str= [str,' ',P.Valve.Name{i}]; end disp(['Flow/q0: ',str]); disp(num2str([FlowVec(1:6)/P.General.q0],'%11.4f')); disp(['Relative FlowError 1000x= ',num2str(round(1000*ErrFlow))]); % ---- End flow error calculation if (Errq<0.4); % Adaptation only with sufficient stationarity % === recording stationarity variables before and after heart beat ==== P.Adapt.In=[P.Adapt.In; P2StatVec('In')]; %record 1st sample of beat % ===== Pressure and flow control pMean= mean(GetFt('Cavity','p','SyArt')); qMean= mean(GetFt('Valve','q','SyVenRa')); Fac=0.5; %Feedback factor blood pressure control FacpControl= (pMean/P.General.p0)^Fac; % used for volume depletion p0AV= GetFt('ArtVen','p0AV','Sy'); % Peripheral resistance control if P.General.PressFlowContr;%++++ yes/no pressure and flow control +++++++++++++++ PutFt('ArtVen','p0AV','Sy',(p0AV/FacpControl)*(qMean/P.General.q0)^Fac); P.General.FacpControl=FacpControl; end % Estimate AV-delay P.General.TauAv=0.1765*P.General.tCycle; P.Adapt.Out=[P.Adapt.Out; P2StatVec('Out') ]; %record last sample % for fast steady state procedure end if P.Adapt.Fast; if size(P.Adapt.Out,1)>1; % Escape if steady state is reached ErrVec= log( P.Adapt.Out(end,:)./P.Adapt.In(end,:) ); disp(['Stationarity error= ',... num2str(round(1000*sqrt(mean(ErrVec .^2 ))))] ); if sqrt(mean(ErrVec .^ 2 ))<0.001 P.General.tEnd= P.General.tEnd-0.5*(P.General.tEnd-P.t(end)); end %=== ERROR criterium on flow stationarity % === Faster Steady State if P.Adapt.Fast; Vec= SteadyStateP; StatVec2P(Vec); % sets new initial conditions to StrInOut-variables end end end; % get the initial condition for next beat, P is most compact information to % start the simulation P2SVar; % load physiologic data in record of state variables P.SVar %SVarDot(0,P.SVar(end,:)',[]); % Compact P-structure end %===== Stationarity functions for Adapt0P ===== function Vec= P2StatVec(InOut) % vector of variables used for reaching stationarity global P; % VecV=GetFt('Cavity','V','All'); VecV=GetFt('Cavity','V',{'SyArt','SyVen','PuArt','PuVen','La','Ra'}); Facp=P.General.FacpControl; p0AVSy=GetFt('ArtVen','p0AV','Sy'); if strmatch(InOut,'In') Vec=[VecV(1,:),Facp,p0AVSy]; else Vec=[VecV(end,:),Facp,p0AVSy]; end end function StatVec2P(Vec) % Inverse of P2StatPar % fills values at the end global P; PutFt('Cavity','V',{'SyArt','SyVen','PuArt','PuVen','La','Ra'},Vec(1:6)); P.General.FacpControl=Vec(end-1); PutFt('ArtVen','p0AV','Sy',Vec(end)); end $#$AdaptExcP.m#$#function AdaptExcP global P save PTemp P; %saves last intermediate solution % Assessment of stationarity of flows FlowVec=mean(P.Valve.q); % test on presence of FlowVec with right size if isfield(P.Adapt, 'FlowVec'); FlowVecPrev= P.Adapt.FlowVec; if length(FlowVecPrev)~=length(FlowVec); FlowVecPrev= 0*FlowVec; end else FlowVecPrev= 0*FlowVec; end % flow instationarity error Errq= std(FlowVec-FlowVecPrev)/P.General.q0; % instationarity iqSy= strmatch('SyVenRa',P.Valve.Name); % index systemic venous flow ErrqSy= log(FlowVec(iqSy)/P.General.q0); %relative flow error ErrFlow=sqrt(Errq.^2+ErrqSy.^2); % display of normalized flow P.Adapt.FlowVec= FlowVec; str=[]; for i=1:6 str= [str,' ',P.Valve.Name{i}]; end disp(['Flow/q0: ',str]); disp(FlowVec(1:6)/P.General.q0); disp(['Relative FlowError 1000x= ',num2str(round(1000*ErrFlow))]); % ---- End flow error calculation if (Errq<0.1); % Adaptation only with sufficient stationarity % === recording stationarity variables before and after heart beat ==== StatVecIn=P2StatVec('In'); % ===== Pressure and flow control pMean= mean(GetFt('Cavity','p','SyArt')); qMean= mean(GetFt('Valve','q','SyVenRa')); Fac=0.5; %Feedback factor blood pressure control FacpControl= (pMean/P.General.p0)^Fac; % used for volume depletion P.General.FacpControl=FacpControl; p0AV= GetFt('ArtVen','p0AV','Sy'); % Peripheral resistance control PutFt('ArtVen','p0AV','Sy',(p0AV/FacpControl)*(qMean/P.General.q0)^Fac); % Estimate AV-delay P.General.TauAv=0.1765*P.General.tCycleRest; if ErrFlow<0.1 % === recording stationarity variables before and after heart beat ==== P.Adapt.In=[P.Adapt.In; StatVecIn]; %record 1st sample of beat %=== Adapt ArtVen wall thickness and Patches ArtVenAdapt('All','WallVolume'); %PatchAdapt('All',{'WallArea','EcmStress'}); PatchAdapt('All','All'); BagAdapt; P.Adapt.Out=[P.Adapt.Out; P2StatVec('Out') ]; %record last sample end end % ---- end adaptation % === if Error==small, ending is made faster if size(P.Adapt.Out,1)>1; % Escape if steady state is reached ErrVec= log( P.Adapt.Out(end,:)./P.Adapt.In(end,:) ); disp(['Stationarity error= ',... num2str(round(1000*sqrt(mean(ErrVec .^2 ))))] ); if sqrt(mean(ErrVec .^ 2 ))<0.001 P.General.tEnd= P.General.tEnd-0.5*(P.General.tEnd-P.t(end)); end %=== ERROR criterium on flow stationarity % === Faster Steady State if P.Adapt.Fast; Vec= SteadyStateP; StatVec2P(Vec); % sets new initial conditions to StrInOut-variables end end; % get the initial condition for next beat, P is most compact information to % start the simulation P2SVar; % load physiologic data in record of state variables P.SVar %SVarDot(0,P.SVar(end,:)',[]); % Compact P-structure end %===== Stationarity functions for AdaptRest ===== function Vec= P2StatVec(InOut) % vector of variables used for reaching stationarity global P; i= strmatch(InOut,{'In','Out'}); A=[1,size(P.t,1)]; it=A(i); VecA=GetFt('ArtVen','AWall','All'); VecA=VecA(:)'; VecSfPas=GetFt('Patch','SfPas','All'); VecAmRef=GetFt('Patch','AmRef','All'); VecVWall=GetFt('Patch','VWall','All'); % Adapt0P variables: Aux=GetFt('Cavity','V',{'SyArt','SyVen','PuArt','PuVen','La','Ra'}); VecV=Aux(it,:); Facp=P.General.FacpControl; p0AVSy=GetFt('ArtVen','p0AV','Sy'); Vec=[VecA,VecSfPas,VecAmRef,VecVWall,VecV,Facp,p0AVSy]; end function StatVec2P(Vec) % Inverse of P2StatPar % fills values at the end global P; nP=P.Patch.n; nCV=6; nAV=P.ArtVen.n; i=cumsum([0,2*nAV,nP,nP,nP,nCV,1,1]); j= [i(1:end-1)+1; i(2:end)]; VRg=@(i) Vec(j(1,i):j(2,i)); %successive index ranges PutFt( 'ArtVen','AWall','All',reshape(VRg(1),[2,nAV])); PutFt( 'Patch','SfPas','All',VRg(2) ); PutFt( 'Patch','AmRef','All',VRg(3) ); PutFt( 'Patch','VWall','All',VRg(4) ); % Adapt0P variables: PutFt( 'Cavity','V',{'SyArt','SyVen','PuArt','PuVen','La','Ra'},VRg(5) ); P.General.FacpControl=VRg(6); PutFt('ArtVen','p0AV','Sy',VRg(7)); end function BagAdapt % pericardial Bag adaptation to pAdapt reference global P pMax = max(P.Bag.p); Fac_pMax= P.Bag.pAdapt./pMax; P.Bag.VRef= P.Bag.VRef/(Fac_pMax.^(0.3/P.Bag.k)); disp(['Pericard adaptation: ',num2str(log(Fac_pMax))]); end $#$AdaptRestP.m#$#function AdaptRestP % function AdaptRestP % Simulates adaptation of vessels to hemodynamics at rest % Adaptation of vessel cross-section % Theo Arts, Maastricht University, Oct 30, 2011 global P save PTemp P; %saves last intermediate solution % 1st part like Adapt0P controls systemic blood pressure and flow % by adjustment of circulatory blood volume and peripheral resistance % % Assessment of stationarity of flows FlowVec=mean(P.Valve.q); % test on presence of FlowVec with right size if isfield(P.Adapt, 'FlowVec'); FlowVecPrev= P.Adapt.FlowVec; if length(FlowVecPrev)~=length(FlowVec); FlowVecPrev= 0*FlowVec; end else FlowVecPrev= 0*FlowVec; end % flow instationarity error Errq= std(FlowVec-FlowVecPrev)/P.General.q0; % instationarity iqSy= strmatch('SyVenRa',P.Valve.Name); % index systemic venous flow ErrqSy= log(FlowVec(iqSy)/P.General.q0); %relative flow error ErrFlow=sqrt(Errq.^2+ErrqSy.^2); % display of normalized flow P.Adapt.FlowVec= FlowVec; str=[]; for i=1:6 str= [str,' ',P.Valve.Name{i}]; end disp(['Flow/q0: ',str]); disp(FlowVec(1:6)/P.General.q0); disp(['Relative FlowError 1000x= ',num2str(round(1000*ErrFlow))]); % ---- End flow error calculation if (Errq<0.1); % Adaptation only with sufficient stationarity % === recording stationarity variables before and after heart beat ==== P.Adapt.In=[P.Adapt.In; P2StatVec('In')]; %record 1st sample of beat % ===== Pressure and flow control pMean= mean(GetFt('Cavity','p','SyArt')); qMean= mean(GetFt('Valve','q','SyVenRa')); Fac=0.5; %Feedback factor blood pressure control FacpControl= (pMean/P.General.p0)^Fac; % used for volume depletion P.General.FacpControl=FacpControl; p0AV= GetFt('ArtVen','p0AV','Sy'); % Peripheral resistance control PutFt('ArtVen','p0AV','Sy',(p0AV/FacpControl)*(qMean/P.General.q0)^Fac); % Estimate AV-delay P.General.TauAv=0.1765*P.General.tCycleRest; %=== Adapt ArtVen diameters ArtVenAdapt('All','Diameter'); if 1;% Adapt diameter of valves to the connected blood vessel ValveNames={'SyVenRa','RvPuArt','PuVenLa','LvSyArt'}; CavityNames={'SyVen','PuArt','PuVen','SyArt'}; S=mean(GetFt('Cavity','A',CavityNames)); PutFt('Valve','AOpen',ValveNames,S); % mitral and tricuspid valve are larger PutFt('Valve','AOpen',{'RaRv','LaLv'},1.5*S([2,4])); end P.Adapt.Out=[P.Adapt.Out; P2StatVec('Out') ]; %record last sample % for fast steady state procedure end % ===== end recording stationarity variables % === if Error==small, ending is made faster if size(P.Adapt.Out,1)>1; % Escape if steady state is reached ErrVec= log( P.Adapt.Out(end,:)./P.Adapt.In(end,:) ); disp(['Stationarity error= ',... num2str(round(1000*sqrt(mean(ErrVec .^2 ))))] ); if sqrt(mean(ErrVec .^ 2 ))<0.001 P.General.tEnd= P.General.tEnd-0.5*(P.General.tEnd-P.t(end)); end %=== ERROR criterium on flow stationarity % === Faster Steady State if P.Adapt.Fast; Vec= SteadyStateP; StatVec2P(Vec); % sets new initial conditions to StrInOut-variables end end; % After adaptation the initial condition is set for next beat. P2SVar; % load physiologic data in record of state variables P.SVar %SVarDot(0,P.SVar(end,:)',[]); % Most compact P-structure, contains starting %conditions only. end %===== Stationarity functions for AdaptRest ===== function Vec= P2StatVec(InOut) % vector of variables used for reaching stationarity global P; % VecV=GetFt('Cavity','V','All'); VecV=GetFt('Cavity','V',{'SyArt','SyVen','PuArt','PuVen','La','Ra'}); VecA=GetFt('ArtVen','A0',{'Sy','Pu'}); VecA=VecA(:)'; Facp=P.General.FacpControl; p0AVSy=GetFt('ArtVen','p0AV','Sy'); if strmatch(InOut,'In') Vec=[VecV( 1,:),VecA,Facp,p0AVSy]; else Vec=[VecV(end,:),VecA,Facp,p0AVSy]; end end function StatVec2P(Vec) % Inverse of P2StatPar % fills values at the end of the arrays of state variables global P; PutFt('Cavity','V',{'SyArt','SyVen','PuArt','PuVen','La','Ra'},... Vec(1:6)); % Volumes used getting stationarity A=Vec(6+[1,3;2,4]); PutFt('ArtVen','A0',{'Sy','Pu'},A); P.General.FacpControl=Vec(end-1); PutFt('ArtVen','p0AV','Sy',Vec(end)); end $#$ArtVenAdapt.m#$#function ArtVenAdapt(StrAV,AdaptType) % function ArtVenAdapt(StrAV,AdaptType); % Adaptation of Diameter and Wall thickness of Art and Ven to % pressure and flow. % StrAV= array of ArtVen names, e.g. {'Sy','Pu'} % AdaptType= {'Diameter', 'WallVolume'} indicates type of adaptation % Theo Arts, Maastricht University, Oct 30, 2011 global P % Determine ArtVen indices iAV = Str2Index(StrAV ,P.ArtVen.Name); % read string -> index nAV = size(iAV,2); % read type(s) of adaptation iAdapt= Str2Index(AdaptType,{'Diameter','WallVolume'}); Adapt=[0,0]; Adapt(iAdapt)=1; AdaptDiameter=Adapt(1); AdaptWallVolume=Adapt(2); % active if Adapt==1 szAV=[2,nAV]; szRow=[1,2*nAV]; % shape of arry in P.ArtVen and P.Cavity iCavArt = P.ArtVen.iCavity(:,iAV); iCavVen=iCavArt+1; iCav = reshape([iCavArt;iCavVen],szRow); % corresponding Cavity indices in row q = P.ArtVen.q(:,reshape([iAV;iAV],szRow)); % Microcirculatory flow A = P.Cavity.A(:,iCav); % cavity cross-section p = P.Cavity.p(:,iCav); % cavity pressure Z = P.Cavity.Z(:,iCav); % cavity wave impedance vM = reshape(mean(q./A),szAV); % mean flow velocity AMean= reshape(mean(A),szAV); pMean= reshape( ZerosFind(p,log(A/SparseDiag(AMean(:)))),szAV ) ; % pMean=p(AMean) AWall = P.ArtVen.AWall(:,iAV); % wall cross-section p0 = P.ArtVen.p0(:,iAV); % working pressure A0 = P.ArtVen.A0(:,iAV); % working cress-section vImpact = P.ArtVen.Adapt.vImpact(:,iAV); % assumed body impact velocity vFlowMean = P.ArtVen.Adapt.vFlowMean(:,iAV); % target value flow velocity pMax= max(p) + max((Z.*A)*SparseDiag(vImpact)); % maximum pressure AMax= max(A); pMax =reshape(pMax,szAV); AMax =reshape(AMax,szAV); WallStress= 3*pMax.*(0.5+AMax./AWall); % maximum wall stress Fac_vFlow = vM./vFlowMean; % mean velocity/target value FacWallStress= WallStress./P.ArtVen.Adapt.WallStress(:,iAV); % max wall stress/target value Facp0 = pMean./p0; % mean pressure/target value FacA0 = AMean./A0; % mean cross-section/target value %--- %=== Carrying out adaptation FacV=ones(size(A0)); if AdaptDiameter; % adapts diameter to flow a= 0.25; % feedback factor, low value-> slow but stabile FacV = reshape(Fac_vFlow.^a,szRow); % volume stabilization during adaptation A0 = A0 .* (FacA0.*Fac_vFlow).^a; % set working cross-section p0 = p0 .* Facp0.^a ; % set working pressure end if AdaptWallVolume % adapts wall thickness to pressure a=0.5; % feedback factor, low value-> slow but stabile AWall= AWall .* FacWallStress.^a; end %--- P.Cavity.V(end,iCav)= P.Cavity.V(end,iCav).*FacV(:)'; % SVar stabilization P.ArtVen.A0(:,iAV) =A0; % working cross-section P.ArtVen.p0(:,iAV) =p0; % working pressure P.ArtVen.AWall(:,iAV)=AWall; % wall cross-section disp('relative deviation of:') disp(['vFlow : ', num2str(round(1000*log(Fac_vFlow(:)' )),'%+6.3d')]); disp(['WallStress: ', num2str(round(1000*log(FacWallStress(:)')),'%+6.3d')]); end function Index=Str2Index(Str,Names) % conversion of Name(s) to indices in structure n=size(Names,2); Index=[]; if ischar(Str) %if single string if strcmp(Str,'All') Index=1:n; else Index=strmatch(Str,Names); end else % if array of strings for i=1:length(Str) Index= [Index,strmatch(Str{i},Names)]; end end end function pZero= ZerosFind(Y,X) % X and Y(X) are samples points, ordered in columns % find Y(X) for X=0 for each column by polinomal interpolation sz=size(X); pZero=zeros(1,sz(2)); Col1= ones(sz(1),1); for i=1:sz(2) x= X(:,i); y= Y(:,i); Coef=pinv([Col1,x,x.^2,x.^3])*y; % 3rd order polinomal pZero(i)=Coef(1); end end $#$ArtVenV2p.m#$#function ArtVenV2p %function ArtVenV2p % Elastic Art/Ven hemodynamics % Flow waves enter Art and Ven. Art and Ven are connected by peripheral % resistance, representing the microcirculation. % volume V-> transmural pressure pTrans(V) and wave impedance Z(V) % Theo Arts, Maastricht University, Oct 30, 2011 global P; ArtVen = P.ArtVen; % ArtVen structure rhob = P.General.rhob; % indices referring to realted cavities and walls iC= [P.ArtVen.iCavity; P.ArtVen.iCavity+1]; iCavity=iC(:); iW= [P.ArtVen.iWall ; P.ArtVen.iWall+1] ; iWall =iW(:); V = P.Cavity.V(:,iCavity); % cavity volumes (state variable) % SparseDiag function is used for efficient column-wise multiplication Len = SparseDiag(ArtVen.Len ); % repesentative length of blood vessels AWall= SparseDiag(ArtVen.AWall); % wall cross-section of blood vessel kd3m1= SparseDiag(ArtVen.k/3-1); % stiffness exponential p0 = SparseDiag(ArtVen.p0 ); % working pressure A0 = SparseDiag(ArtVen.A0 ); % working cross-section Half = SparseDiag([.5,.5,.5,.5]); % 0.5 diagonal matrix A = max(1e-20,V/Len); % vessel cross-section, buckling included ANorm = A/AWall; % cross-section normalized to wall volume p = exp( log( max(1e-20, ANorm+0.5)/(A0/AWall+Half ) ) * kd3m1) * p0; %p(A) Z = sqrt( (p./(ANorm.*(ANorm+0.5 )) * (rhob*kd3m1/AWall.^2) ) ) ; %Z(A) P.Wall.pTrans(:,iWall)=p; % transmural pressure P.Cavity.Z(:,iCavity) =Z; % wave impedance P.Cavity.A(:,iCavity) =A; % cross-section end % function X=SparseDiag(x) % X=sparse(diag(x(:))); % end $#$BagV2p.m#$#function BagV2p % function BagV2p % A passive elastic bag like the pricardiam. % Pericard transmural pressure P.Wall.pTrans = fu(enclosed volume) % No cavity, only a wall % k= non-linearity exponential % Theo Arts, Maastricht University, Oct 30, 2011 global P; % works for a single Bag % pericardium enclosed cardiac wall and cavities iCav =[P.TriSeg.iCavity+(0:1),P.Chamber.iCavity]; % enclosed cavities iWall=[P.TriSeg.iWall+(0:2),P.Chamber.iWall]; % enclosed walls V= sum(P.Cavity.V(:,iCav),2)+sum(P.Wall.VWall(iWall)); % total enclosed volume VNorm = V/P.Bag.VRef; % normalized volume P.Wall.pTrans(:,P.Bag.iWall)= P.Bag.pAdapt * VNorm .^ P.Bag.k; end $#$ChamberV2p.m#$#function ChamberV2p % function ChamberV2p % A chamber is a cavity encapsulated in a single myocardial wall. % Calculates: Cavity volume V -> % myofiber stress Sf, wall tension T and cavity pressure p % using the locally linearized T(Am) relation % Theo Arts, Maastricht University, Oct 30, 2011 global P; Chamber=P.Chamber; % Chamber structure rhob = 1050; iCavity = Chamber.iCavity; % index of related cavity iWall = Chamber.iWall ; % index of related wall V = max(0,P.Cavity.V(:,iCavity)); %cavity volumes VWall= P.Wall.VWall(iWall); % wall volumes nt = size(V,1); % nt=number of time samples, Vm = max(0,V) + repmat(VWall/2,[nt,1]);% numerical safety with V<0 Cm = (4/3*pi./Vm).^(1/3); % mid wall curvature Am = (4*pi)./Cm.^2; % midwall area Am0 = P.Wall.Am0(:,iWall); % zero tension midwall area DADT = P.Wall.DADT(:,iWall); % wall compliance Am=max(Am,Am0);% buckling with T<0 T = (Am-Am0)./DADT; % wall tension pTrans= 2*Cm.*T; % transmural pressure % wall properties P.Wall.T(:,iWall) = T; % wall tension P.Wall.Cm(:,iWall) = Cm; % curvature=1/radius P.Wall.Am(:,iWall) = Am; % wall area P.Wall.pTrans(:,iWall)= pTrans; % transmural pressure % Cavity impedance properties, needed to make node connection Len= 2*Vm.^(1/3); % cavity length A = ( V + 0.1*repmat(VWall,[nt,1]) ) ./Len; % cross-sectional area P.Cavity.A(:,iCavity) = A; % cross-sectional area for valve P.Cavity.Z(:,iCavity) = 0.2*sqrt(rhob*Len./abs(DADT))./A; % wave resistance to node end $#$CircAdaptMainP.m#$# % CircAdaptMain % Theo Arts, Tammo Delhaas, Joost Lumens % Version CircAdapt.I5 May, 2008 % email: t.arts@bf.unimaas.nl % % CircAdapt is a lumped model of the dynamic circulation. Size of heart % and bloodvessels adapt to load, as generated by the model itself. % All variables and parameter are stored in the structure P with file % name 'P'. Hemodynamic data are displayed graphically % Map of structure 'P' obtained by execution of 'mapstructure(P)' % Used functions: PNew, CircAdapt, CircAdaptDisplay clear global P; addpath(pwd);% add current directory to path if exist('P.mat','file'); % if P.mat file is available FileName='P'; PathName=[pwd,'\']; ShowMenu=1; while ShowMenu; c2=input('[N]ew, [R]eference, [L]oad, [C]ontinue : ','s'); a=[c2,'c']; c=a(1); % convert to 'c' switch lower(c) case('l'); % load file with P-structure [FileName,PathName] = uigetfile('*.mat','Choose file: '); load([PathName,FileName]); c='c';ShowMenu=false; case('r') % load reference file with P-structure load('PRef'); c='c';ShowMenu=false; case('n'); % new P-structure from scratch PNew; c='c';ShowMenu=false; case('c'); % continuation of simulation load([PathName,FileName]); c='c';ShowMenu=false; otherwise ShowMenu=true; end end else if exist('PRef.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 P-structure [FileName,PathName] = uigetfile('*.mat','Choose file: '); load([PathName,FileName]); c='c';ShowMenu=false; case('r') load('PRef'); c='c'; otherwise PNew; c='c'; end else PNew; % Parameter initialization, some remodeling rules inclusive % Generates parameter structure Par and initial conditions of the variables P c='c'; end end % Default initialization G=P.General; G.DtSimulation=1.5*G.tCycle; % standard duration of simulation P.Adapt.FunctionName='Adapt0P'; % No adaptation P.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 : ',P.Adapt.FunctionName]); disp(['Faster steady state [Y]/[N] : ',NY(P.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' P.Adapt.FunctionName='Adapt0P'; G.DtSimulation=1.5*G.tCycle; case 'r' P.Adapt.FunctionName='AdaptRestP'; G.DtSimulation=50*G.tCycle; case 'e' P.Adapt.FunctionName='AdaptExcP'; G.DtSimulation=100*G.tCycle; case 'y' P.Adapt.Fast=1; G.DtSimulation=max(30*G.tCycle,G.DtSimulation); case 'n' P.Adapt.Fast=0; otherwise OK=0; end end % === Solves SVar for problem defined in parameter structure 'P' G.tEnd=P.t(end)+G.DtSimulation; P.General=G; CircAdaptP; %generate solution %=== Saving State Variables and Model Parameters save P P; %save compact solution as structure P in file 'P' disp('Differential equation has been solved'); CircDisplayP; % graphical display % Structure P has been extended with solution for all variables as a % function of time $#$CircAdaptP.m#$#function CircAdaptP global P P.SVar=[]; P2SVar; SVar=P.SVar(end,:); P.SVar=SVar; % start condition % SVar will collect state variables of all beats in the series Dt= P.General.Dt; P.Adapt.In=[]; P.Adapt.Out=[]; % reset of storage input/output per beat P.Adapt.FlowVec=[]; % reset flow instationarity detection P.General.FacpControl=1; % start value p-control factor while P.t(end)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 $#$MemAlloc.m#$#function MemAlloc % function MemAlloc % Allocates memory for matrices with column length= number of time points % These allocations are necessary because they get a value by gradual % substitutions, using indices for locating them in the matrix. % Theo Arts, Maastricht University, Oct 30, 2011 global P; nt=size(P.t,1); % number of time points % test on change of column length, only then MemAlloc is active nt1= size(P.Cavity.V,1); nt2=0; if isfield(P.Cavity,'VDot') nt2= size(P.Cavity.VDot,1); end % Allocating matrices for the walls if nt1~=nt2; % If size of matrices changes, MemAlloc is active nWall =P.Wall.n; % number of walls nCavity=P.Cavity.n; % number of cavities nPatch =P.Patch.n; % number of patches nNode =P.Node.n; % number of nodes nTriSeg=P.TriSeg.n; % number of TriSeg objects % Matrix constructions MatWall = zeros(nt,nWall); MatCavity= zeros(nt,nCavity); MatPatch = zeros(nt,nPatch); MatNode = zeros(nt,nNode); MatTriSeg= zeros(nt,nTriSeg); % Allocating matrices for the walls P.Wall.Am0 =MatWall; %unstressed mid-wall area P.Wall.DADT =MatWall; %area compliance P.Wall.T =MatWall; %tension P.Wall.Cm =MatWall; %curvature P.Wall.Am =MatWall; %actual mid-wall area P.Wall.pTrans=MatWall; %transmural pressure % Allocating matrices for the cavities P.Cavity.A=MatCavity; %cross-sectional area P.Cavity.Z=MatCavity; %resistive impedance P.Cavity.p=MatCavity; %pressure % Allocating matrices for the patches P.Patch.T=MatPatch; %Tension % Allocating matrices for the nodes P.Node.q=MatNode; %node flow source P.Node.Y=MatNode; %node internal flow conductance % Allocating matrices for the TriSeg objects P.TriSeg.VS =MatTriSeg; % volume displacement septal wall P.TriSeg.YS =MatTriSeg; % radius junction ring P.TriSeg.VDot=MatTriSeg; % SVar-Dot P.TriSeg.YDot=MatTriSeg; % SVar-Dot end end $#$P2SVar.m#$#function P2SVar global P ScaleVqY=P.General.ScaleVqY; ScV=ScaleVqY(1); Scq=ScaleVqY(2); ScY=ScaleVqY(3); P.SVar=[P.t,P.Cavity.V/ScV,P.Valve.q/Scq,P.Patch.C,P.Patch.Lsi,... P.TriSeg.V/ScV,P.TriSeg.Y/ScY]; end $#$P2SVarDot.m#$#function P2SVarDot % function P2SVarDot % Physiological P -> scaling -> Time derivative of SVar % stored in P.SVarDot % time in column direction, SVar in row direction % Theo Arts, Maastricht University, Oct 30, 2011 global P ScaleVqY=P.General.ScaleVqY; ScV=ScaleVqY(1); Scq=ScaleVqY(2); ScY=ScaleVqY(3); Sc0=1; P.SVarDot= [P.tDot,P.Cavity.VDot/ScV,P.Valve.qDot/Scq,... P.Patch.CDot,P.Patch.LsiDot, P.TriSeg.VDot/ScV,P.TriSeg.YDot/ScY]; end $#$Par2P.m#$#% Conversion of Par-structure to network of Chambers, tubes and valves with % nodes. clear; load ParRef global P; % NameNode={}; % for i=1:P.Cavity.n % NameNode=[NameNode,[NameCavity{i},'1']]; % end %====== BACKBONE OF STRUCTURE =============== jCavity=0; jWall=0; jPatch=0; jNode=0; NameCavity={}; NameNode={}; NameWall={}; nPWall=[]; %number of myocardial patches per wall %ARTVEN Name={'Syst','Pulm'}; NameCav={'SystArt','SystVen','PulmArt','PulmVen'}; n=length(Name); ArtVen.n=n; ArtVen.Name=Name; %Cavities iCavity=jCavity+(1:2:2*n); jCavity=iCavity(end)+1; ArtVen.iCavity=iCavity; %link to cavity NameCavity=[NameCavity,NameCav]; %Walls iWall=jWall+(1:2:2*n); jWall=iWall(end)+1; ArtVen.iWall=iWall; %link to wall NameWall=[NameWall,NameCav]; nPWall=[nPWall,zeros(1,2*n)]; %TRISEG Name={'Ventricles'}; n=length(Name); TriSeg.n=n; TriSeg.Name=Name; NameW={'Lv','Sv','Rv'}; %Cavities iCavity=jCavity+(1:2:2*n); jCavity=iCavity(end)+1; TriSeg.iCavity=iCavity;%link to cavity NameCavity=[NameCavity,NameW([1,3])]; %Walls iWall=jWall+(1:3:3*n); jWall=iWall(end)+2; TriSeg.iWall=iWall;%link to wall NameWall=[NameWall,NameW]; nPWall=[nPWall,ones(1,3*n)]; %CHAMBER Name={'La','Ra'}; n=length(Name); Chamber.n=n; Chamber.Name=Name; %Cavities iCavity=jCavity+(1:n); jCavity=iCavity(end); Chamber.iCavity=iCavity;%link to cavity NameCavity=[NameCavity,Name]; %Walls iWall=jWall+(1:n); jWall=iWall(end); Chamber.iWall=iWall;%link to wall NameWall =[NameWall,Name]; nPWall=[nPWall,ones(1,n)]; %NODE NameNode=NameCavity; %PERICARDIUM Name={'Peri'}; n=length(Name); Peri.n=n; Peri.Name=Name; %Walls iWall=jWall+(1:n); jWall=iWall(end); Peri.iWall=iWall;%link to wall NameWall =[NameWall,Name]; nPWall=[nPWall,0]; %VALVE Name={'PulmVen','LAv','SystArt','SystVen','RAv','PulmArt','ASD','VSD','DUCT'}; n=length(Name); Valve.n=n; Valve.Name=Name; %=================== %Memory allocations in Cavity, Wall and Patch %Cavities Cavity.n=jCavity; Cavity.Name=NameCavity; Cavity.iNode=1:Cavity.n; %Walls Wall.n=jWall; Wall.Name=NameWall; Wall.nPatch=nPWall;%number of patches %Patches iPatch=[1,1+cumsum(Wall.nPatch(1:end-1))]; Wall.iPatch=iPatch;%link to patch Patch.n=sum(nPWall); NamePatch={}; for i=1:Wall.n n=Wall.nPatch(i); for j=1:n NamePatch=[NamePatch,[NameWall{i},num2str(j)]]; end end Patch.Name=NamePatch; %Nodes Node.n=Cavity.n; Node.Name= NameNode; P=[]; P.t = Par.t; P.tDot = ones(size(P.t)); P.General= Par.General; P.ArtVen = ArtVen; P.TriSeg = TriSeg; P.Chamber= Chamber; P.Peri = Peri; P.Cavity = Cavity; P.Wall = Wall; P.Patch = Patch; P.Valve = Valve; P.Node = Node; %============= end backbone definition of structure ========= % ArtVen nt=length(Par.TubeLArt.V); % initialization matrices and vectors nArtVen=P.ArtVen.n; MatArtVen=zeros(nt,nArtVen); VecArtVen=zeros(1,nArtVen); nTriSeg=P.TriSeg.n; MatTriSeg=zeros(nt,nTriSeg); VecTriSeg=zeros(1,nTriSeg); nChamber=P.Chamber.n; MatChamber=zeros(nt,nChamber); VecChamber=zeros(1,nChamber); nCavity=P.Cavity.n; MatCavity=zeros(nt,nCavity); VecCavity=zeros(1,nCavity); nWall=P.Wall.n; MatWall=zeros(nt,nWall); VecWall=zeros(1,nWall); nPatch=P.Patch.n; MatPatch=zeros(nt,nPatch); VecPatch=zeros(1,nPatch); MatPeri=zeros(nt,1); nValve=P.Valve.n; MatValve=zeros(nt,nValve); VecValve=zeros(1,nValve); nNode=P.Node.n; MatNode=zeros(nt,nNode); VecNode=zeros(1,nNode); P.Peri.p = MatPeri; P.Cavity.V = MatCavity; P.Cavity.VDot = MatCavity; P.Cavity.p = MatCavity; P.Cavity.Z = MatCavity; P.Cavity.A = MatCavity; P.Wall.AmDead = VecWall; P.Wall.VWall = VecWall; P.Wall.Am = MatWall; P.Wall.Cm = MatWall; P.Wall.Am0 = MatWall; P.Wall.DADT = MatWall; P.Wall.T = MatWall; P.Wall.pTrans = MatWall; P.Patch.Lsi = MatPatch; P.Patch.LsiDot = MatPatch; P.Patch.C = MatPatch; P.Patch.CDot = MatPatch; P.Patch.VWall = VecPatch; P.Patch.AmRef = VecPatch; %Sarcomere P.Patch.ActivationDelay =[VecPatch;VecPatch]; P.Patch.Ef = MatPatch; P.Patch.LsRef = VecPatch; P.Patch.Ls0Pas = VecPatch; P.Patch.dLsPas = VecPatch; P.Patch.SfPas = VecPatch; P.Patch.Lsi0Act = VecPatch; P.Patch.LenSeriesElement= VecPatch; P.Patch.SfAct = VecPatch; P.Patch.vMax = VecPatch; P.Patch.TimeAct = VecPatch; P.Patch.TR = VecPatch; P.Patch.TD = VecPatch; P.Patch.CRest = VecPatch; P.Patch.Ls = MatPatch; P.Patch.SfPasT = MatPatch; P.Patch.Sf = MatPatch; P.Patch.DSfDEf = MatPatch; P.Patch.Am = MatPatch; P.Patch.T = MatPatch; P.Patch.DADT = MatPatch; P.Valve.q = MatValve; P.Valve.qDot = MatValve; P.Valve.AOpen= VecValve; P.Valve.ALeak= VecValve; P.Valve.Len = VecValve; P.Node.q = MatNode; P.Node.p = MatNode; P.Node.Y = MatNode; % ==== Data transfer Par->P ==== % ArtVen and related cavities for i=1:nArtVen VecMat=zeros(2,nArtVen); iCav0 =P.ArtVen.iCavity(i); iWall0=P.ArtVen.iWall(i); P.ArtVen.rhob=1050; NamePar={'LArt','RVen','RArt','LVen'}; for j=0:1 jCavity=iCav0+j; Name=NamePar{jCavity}; Tube=Par.(['Tube',Name]); P.Cavity.V(:,jCavity)= Tube.V; jWall=iWall0+j; Name=P.Wall.Name{jWall}; P.ArtVen.k(j+1,i) = Tube.k; P.ArtVen.Len(j+1,i) = Tube.Len; P.ArtVen.AWall(j+1,i)= Tube.AWall; P.ArtVen.p0(j+1,i ) = Tube.p0; P.ArtVen.A0(j+1,i ) = Tube.A0; P.ArtVen.p0AV= [Par.LRp.R*P.General.q0,P.General.pDropPulm]; P.ArtVen.q0AV= P.General.q0*[1,1]; P.ArtVen.kAV= [1.0, 2.0]; end end %TriSeg and related cavities, walls and patches P.TriSeg.V = Par.Sv.V; P.TriSeg.VDot= ones(size(Par.Sv.V)); P.TriSeg.Y = Par.Sv.Y; P.TriSeg.YDot= ones(size(Par.Sv.Y)); P.TriSeg.Tau=Par.Sv.Tau; for i=1:nTriSeg %each TriSeg % cavity: volumes iCav0 =P.TriSeg.iCavity(i); jWall0=P.TriSeg.iWall(i); for j=0:1 jCavity=iCav0+j; Name=P.Cavity.Name{jCavity}; Aux=Par.(Name);%read P.Cavity.V(:,jCavity)=Aux.V; end % patch: sarcomere Lsi and C for j=0:2 %each wall of TriSeg jWall=jWall0+j; nPatch= P.Wall.nPatch(jWall); RgPatch=P.Wall.iPatch(jWall)+(0:nPatch-1); Vec= ones(1,nPatch)/nPatch; Name=P.Wall.Name{jWall}; Sarc=Par.(Name).Sarc;%read P.Wall.AmDead(jWall) = Par.(Name).AmDead; P.Wall.VWall(jWall) = Par.(Name).VWall; P.Patch.Lsi(:,RgPatch)= Sarc.Lsi; P.Patch.C(:,RgPatch) = Sarc.C; P.Patch.VWall(RgPatch)= Par.(Name).VWall*Vec; P.Patch.AmRef(RgPatch)= Par.(Name).AmRef*Vec; P.Patch.ActivationDelay(:,RgPatch) =Sarc.ActivationDelay([-1,0]+end); P.Patch.LsRef(RgPatch) = Sarc.LsRef; P.Patch.Ls0Pas(RgPatch) = Sarc.Ls0Pas; P.Patch.dLsPas(RgPatch) = Sarc.dLsPas; P.Patch.SfPas(RgPatch) = Sarc.SfPas; P.Patch.Lsi0Act(RgPatch) = Sarc.Lsi0Act; P.Patch.LenSeriesElement(RgPatch)= Sarc.LenSeriesElement; P.Patch.SfAct(RgPatch) = Sarc.SfAct; P.Patch.vMax(RgPatch) = Sarc.vMax; P.Patch.TimeAct(RgPatch) = Sarc.TimeAct; P.Patch.TR(RgPatch) = Sarc.TR; P.Patch.TD(RgPatch) = Sarc.TD; P.Patch.CRest(RgPatch) = Sarc.CRest; end end %Chamber and related cavities, walls and patches for i=1:nChamber % cavity: volumes jCavity=P.Chamber.iCavity(i); Name=P.Cavity.Name{jCavity}; Aux=Par.(Name); P.Cavity.V(:,jCavity)=Aux.V; % patch: sarcomere Lsi and C jWall = P.Chamber.iWall(i); nPatch = P.Wall.nPatch(jWall); RgPatch= P.Wall.iPatch(jWall)+(0:nPatch-1); Vec = ones(1,nPatch)/nPatch; Name = P.Wall.Name{jWall}; Sarc = Par.(Name).Sarc; P.Wall.AmDead(jWall) = Par.(Name).AmDead; P.Wall.VWall(jWall) = Par.(Name).VWall; P.Patch.Lsi(:,RgPatch)= Sarc.Lsi; P.Patch.C(:,RgPatch) = Sarc.C; P.Patch.VWall(RgPatch)= Par.(Name).VWall*Vec; P.Patch.AmRef(RgPatch)= Par.(Name).AmRef*Vec; P.Patch.ActivationDelay(:,RgPatch) =Sarc.ActivationDelay([-1,0]+end); P.Patch.LsRef(RgPatch) = Sarc.LsRef; P.Patch.Ls0Pas(RgPatch) = Sarc.Ls0Pas; P.Patch.dLsPas(RgPatch) = Sarc.dLsPas; P.Patch.SfPas(RgPatch) = Sarc.SfPas; P.Patch.Lsi0Act(RgPatch) = Sarc.Lsi0Act; P.Patch.LenSeriesElement(RgPatch)= Sarc.LenSeriesElement; P.Patch.SfAct(RgPatch) = Sarc.SfAct; P.Patch.vMax(RgPatch) = Sarc.vMax; P.Patch.TimeAct(RgPatch) = Sarc.TimeAct; P.Patch.TR(RgPatch) = Sarc.TR; P.Patch.TD(RgPatch) = Sarc.TD; P.Patch.CRest(RgPatch) = Sarc.CRest; end % Cavity P.Cavity.iNode= 1:P.Cavity.n; P.Node.iCavity= 1:P.Cavity.n;% for now ++++++ % Peri P.Peri.VRef = Par.Peri.VRef ; P.Peri.k = Par.Peri.k ; P.Peri.pAdapt= Par.Peri.pAdapt; % Valve ParName={'LVen','LAv','LArt','RVen','RAv','RArt','ASD','VSD','DUCT'}; for i=1:nValve Aux=Par.(['Valve',ParName{i}]); P.Valve.q(:,i) =Aux.q; P.Valve.AOpen(i)=Aux.AOpen; P.Valve.ALeak(i)=Aux.ALeak(1,:); P.Valve.Len(i) =Aux.Len; end P.Valve.iNodeProx=[4,7,5,2,8,6,7,5,1]; P.Valve.iNodeDist=[7,5,1,8,6,3,8,6,3]; %================= SVar communication ==================== P.General.ScaleVqY=[1e-5,1e-4,1e-1];%????++++++ $#$PatchAdapt.m#$#function PatchAdapt(StrPatch,AdaptType) % function PatchAdapt(StrPatch,AdaptType); % StrPatch= array of Patch names, e.g. {'Lv1','Sv1'} % AdaptType= {'WallVolume','WallArea','EcmStress'} indicates type of adaptation global P % Determine Patch and AdaptType indices iPatch= Str2Index(StrPatch ,P.Patch.Name); iAdapt= Str2Index(AdaptType,{'WallVolume','WallArea','EcmStress'}); Adapt=[0,0,0] ; Adapt(iAdapt)=1; AdaptWallVolume=Adapt(1); AdaptWallArea =Adapt(2); AdaptEcmStress =Adapt(3); Lsi = P.Patch.Lsi; %sarcomere length SfEcm = max(0,P.Patch.SfEcm); % ECM fiber stress SfTit = max(0,P.Patch.SfPasT-P.Patch.SfEcm); % Myocyte passive stress SfAct = max(0,P.Patch.Sf-P.Patch.SfPasT); % Myocyte active stress SfMyo = max(0,P.Patch.Sf-P.Patch.SfEcm); % Total myocyte stress Ef = P.Patch.Ef; % tissue fiber strain EfDot = P.Patch.LsiDot./Lsi; % ~fiber strain rate CDot = P.Patch.CDot/max(P.Patch.C); % ~rate of contraction activation LSe = P.Patch.Ls-Lsi; % length series elastic element %Target values SfEcmMaxT= P.Patch.Adapt.SfPasMax(iPatch); % Ecm-stress SfActMaxT= P.Patch.Adapt.FacSfAct(iPatch) .* P.Patch.SfAct(iPatch); %active stress SfPasActT= P.Patch.Adapt.SfPasAct(iPatch); % ~integrin stress % SfTitActT= (0.0165*Atrium+0.0332*Ventricle)*Sarc.SfAct; SfTitActT= 0.05*P.Patch.Adapt.FacSfAct(iPatch).*P.Patch.SfAct(iPatch); LsPasActT= 2.10;% setting integrin stress weighted sarcomere length %sensed variables, ratio to target value SfEcmMax= max(SfEcm)./SfEcmMaxT; SfActMax= max(SfAct)./SfActMaxT; SfPasAct= max(SfEcm .* SfAct./(max(100,SfEcm+SfAct)))./SfPasActT; SfTitAct= max(SfTit .* SfAct./(SfTit+SfAct))./SfTitActT; LsPasAct= sum(SfEcm.*SfAct.*Lsi)./sum(SfEcm.*SfAct)./LsPasActT; % factor inversion for convenient mathematical manipulation FacSfEcm = 1./SfEcmMax; %1 FacSfAct = 1./SfActMax; %2 FacSfPasAct= 1./SfPasAct; %3 FacSfTitAct= 1./SfTitAct; %4 FacLsPasAct= 1./LsPasAct; %6 Eff2Geom=[3,0,0; 0,1,5; 0,0,-5]; % general geometry <-> local remodeling % Calculate adaptation factors switch 1 % various trials, % otherwise no change case 1 %1236 good correlation, best convergence Sens=log([FacSfEcm;FacSfAct;FacSfPasAct;FacLsPasAct]); % sensed variables Sens2Eff=[... -0.1917 -0.1279 -0.0906 -0.2617 0.0999 0.1401 +0.2021 0.2066 -0.0573 -0.1855 -1.8441 0.5308]; Aux= Eff2Geom'*Sens2Eff'*Sens; case 2 %1346 good correlation, better convergence Sens=log([FacSfEcm;FacSfPasAct;FacSfTitAct;FacLsPasAct]); % sensed variables Sens2Eff=[... -0.0170 -0.2198 -0.2201 -0.0225 0.3120 0.0906 -0.7362 0.4042 0.5689 +5.0679 -4.8282 -3.6713]; Aux= Eff2Geom'*Sens2Eff'*Sens; otherwise %no action end % Clipping of Fac around 1 with range +/-Clip a= 0.10; % gain of adaptation feedback. 1.0 represents best fit matrix%+++++++++++++++ ClipFac= @(x,Clip) exp(Clip*tanh(log(x)/Clip)); Clip=0.10; FacVWall = ClipFac(exp(a*Aux(1,:)),Clip); FacAmRef = ClipFac(exp(a*Aux(2,:)),Clip); FacSfPas = ClipFac(exp(a*Aux(3,:)),Clip); %=== Fixation Septal/Lv wall area (adaptation is critical)+++++++++ % AmRefLS=[sum(GetFt('Patch','AmRef','Lv')) sum(GetFt('Patch','AmRef','Sv'))]; % LvSv=2.0; % Fac= LvSv*AmRefLS(2)/AmRefLS(1); % FacAmRef([3,4])= FacAmRef([3,4]).*[Fac,1/Fac].^0.5; % a=0.1; b=0.6; mat= ( (1-a)*eye(2)+a*[1;1]*[b,1-b] ); % PutFt('Patch','AmRef',{'Lv1','Sv1'},AmRefLS * mat); %--- %=== Carrying out adaptation Error=[]; if AdaptWallVolume Vw0= P.Patch.VWall; Vw1= Vw0 .* FacVWall; P.Patch.VWall=Vw1; Error= [Error;abs(log(FacVWall))]; end if AdaptWallArea %Strain softening of passive matrix by creep % Sheet.AmRef= Sheet.AmRef*FacAmRef; P.Patch.AmRef= P.Patch.AmRef .* FacAmRef; Error= [Error;abs(log(FacAmRef))]; 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 P.Patch.SfPas= P.Patch.SfPas .* FacSfPas; Error= [Error; abs(log(FacSfPas))]; % Softening by changing dLsPas seems very OK % Sarc.dLsPas= Sarc.dLsPas * FacSfPas.^-0.3; end ErrorT= sqrt(mean(Error.^2,2)); str=[]; for i=1:P.Patch.n str=[str,' ',P.Patch.Name{i}]; end disp(str); disp(['VWall ',num2str(round(1000*[log(FacVWall),ErrorT(1)]),... '%+5.3d')]); disp(['AmRef ',num2str(round(1000*[log(FacAmRef),ErrorT(2)]),... '%+5.3d')]); disp(['SfPas ',num2str(round(1000*[log(FacSfPas),ErrorT(3)]),... '%+5.3d')]); end function Index=Str2Index(Str,Names) n=size(Names,2); Index=[]; if ischar(Str) %if single string if strcmp(Str,'All') Index=1:n; else Index=strmatch(Str,Names); end else % if array of strings for i=1:length(Str) Index= [Index,strmatch(Str{i},Names)]; end end end $#$PatchWallA2T.m#$#function PatchWallA2T % function PatchWallA2T % State variables of the sarcomere -> Patch area Am is written as a % linear function of wall tension T: Am(T)=Am0+T*DADT % Theo Arts, Maastricht University, Oct 30, 2011 global P; % Patch Am= Am0+DADT*T, provides Am0 and DADT AmRef=SparseDiag(P.Patch.AmRef); % midwall area for ref sarcomere length 2mu LsRef=SparseDiag(P.Patch.LsRef); % ref sarcomere length 2mu VWall=SparseDiag(P.Patch.VWall); % wall volume Lsi = P.Patch.Lsi; % unloaded sarc length= state variable sarc length due to passive stress only. No series elastic length. Lambda= Lsi/LsRef; %extension factor Am = Lambda.^2*AmRef; %actual midwall area %area resulting from Ls = lsi only. Zero length series elastic element. Ef = log(Lambda); %natural fiber strain with series elastic element length = 0. P.Patch.Ef= Ef; % set strain in patch module. % based on sarcomere length Lsi. SarcEf2Sf; % sarcomere strain->stress % calculate the stiffness of all patches based on Ls = Lsi. Sf = P.Patch.Sf; % sarcomere stress % sarcomere passive stress at length Ls = Lsi. T = (Sf *(0.5 *VWall)) ./ Am ; % tension % wall tension at length Ls = Lsi. DADT = (Am.^2 ./ max(P.Patch.DSfDEf-2*Sf,1e3))/(0.25*VWall); %compliance %at length Ls = Lsi. P.Patch.T = T; % not used! P.Patch.DADT= DADT; P.Patch.Am0 = Am - T.*DADT; % zero load midwall area % true unloaded patch area with effect of passive stress removed. % Wall is composed of patches: Also for wall: Am(T)=Am0+DADT*T for iWall=1:P.Wall.n iPatch= (P.Wall.iPatch(iWall)-1)+(1:P.Wall.nPatch(iWall)); P.Wall.VWall(iWall) = sum(P.Patch.VWall(iPatch)); P.Wall.Am0(:,iWall) = P.Wall.AmDead(iWall)+sum(P.Patch.Am0(:,iPatch),2); P.Wall.DADT(:,iWall) = sum(P.Patch.DADT(:,iPatch),2); end end $#$pCavity.m#$#function pCavity % function pCavity % pericardium encloses a number of walls and cavities. Pressures are % calculated by adding transmural pressures % Theo Arts, Maastricht University, Oct 30, 2011 global P % Pericardium P.Bag.p=P.Wall.pTrans(:,P.Bag.iWall); %TriSeg +Pericard pressure iCavity=P.TriSeg.iCavity; iWall =P.TriSeg.iWall; P.Cavity.p(:,iCavity) =P.Bag.p-P.Wall.pTrans(:,iWall); %pLV P.Cavity.p(:,iCavity+1)=P.Bag.p+P.Wall.pTrans(:,iWall+2); %pRV %Chamber (La, Ra)+Pericard pressure iCavity=P.Chamber.iCavity; Row0=zeros(1,P.Chamber.n); iWall =P.Chamber.iWall; P.Cavity.p(:,iCavity)=P.Bag.p(:,1+Row0)+P.Wall.pTrans(:,iWall); %ArtVen, not enclosed by other walls iC=P.ArtVen.iCavity; iCavity=[iC;iC+1]; iW=P.ArtVen.iWall ; iWall=[iW;iW+1]; P.Cavity.p(:,iCavity)=P.Wall.pTrans(:,iWall); end $#$PNew.m#$#function PNew % based on ParRef % should be made stand-alone % Theo Arts, Maastricht University, Oct 30, 2011 global P;P=[]; % load ParRef; P.General.rhob=1050; P.General.q0=45e-6; P.General.p0=12000; P.General.tCycle=0.600; P.General.FacpControl=1; pLv=19000; pRv=7500; pLa=2800; pRa=1400; %============================= qExc=3*P.General.q0; VStroke= P.General.q0*P.General.tCycle; VStrokeExc=1.5*VStroke; %===== INPUT: naming ArtVen, TriSeg and Chambers P.ArtVen.Name = {'Sy','Pu'}; P.Chamber.Name= {'La','Ra'}; P.TriSeg.Name = {'v'}; %=============================================== jCavity=0; jWall=0; jPatch=0; jNode=0; P.Cavity.Name= {}; P.Cavity.n= 0; P.Wall.Name = {}; P.Wall.n = 0; P.Wall.nPatch=[]; P.Patch.Name = {}; P.Patch.n = 0; P.Node.Name = {}; P.Node.n = 0; % ===== ArtVen -> Cavity P.ArtVen.n = length(P.ArtVen.Name); str={'Art','Ven'}; NameCav={}; iCav=[]; NameWall={}; iWall=[]; for i=1:P.ArtVen.n str1=P.ArtVen.Name{i}; iCav=[iCav,jCavity+1]; iWall=[iWall,jWall+1]; for j=1:2 jCavity=jCavity+1; NameCav=[NameCav,[str1,str{j}]]; jWall=jWall+1; NameWall=[NameWall,[str1,str{j}]]; end end P.ArtVen.iCavity=iCav; P.Cavity.Name= [P.Cavity.Name,NameCav]; P.Cavity.n = P.Cavity.n+length(NameCav); P.ArtVen.iWall=iWall; P.Wall.Name= [P.Wall.Name,NameWall]; P.Wall.n = jWall; P.Wall.nPatch=[P.Wall.nPatch,zeros(size(NameWall))]; % ===== Chamber -> Cavity, Wall P.Chamber.n = length(P.Chamber.Name); NameCav ={}; iCav =[]; NameWall={}; iWall=[]; for i=1:P.Chamber.n str1=P.Chamber.Name{i}; jCavity=jCavity+1; iCav=[iCav,jCavity]; jWall=jWall+1; iWall=[iWall,jWall]; NameCav = [NameCav,str1]; NameWall= [NameWall,str1]; end P.Chamber.iCavity= iCav; P.Cavity.Name = [P.Cavity.Name,NameCav]; P.Cavity.n = P.Cavity.n+length(NameCav); P.Chamber.iWall = iWall; P.Wall.Name = [P.Wall.Name,NameWall]; P.Wall.n = jWall; P.Wall.nPatch =[P.Wall.nPatch,ones(size(NameWall))]; % ===== TriSeg-> Cavity, Wall P.TriSeg.n = length(P.TriSeg.Name); strC={'L','R'}; strW={'L','S','R'}; NameCav ={}; iCav= []; NameWall={}; iWall=[]; for i=1:P.TriSeg.n str1=P.TriSeg.Name{i}; iCav =[iCav,jCavity+1]; iWall=[iWall,jWall+1]; for j=1:2 jCavity=jCavity+1; NameCav=[NameCav,[strC{j},str1]]; end for j=1:3 jWall=jWall+1; NameWall=[NameWall,[strW{j},str1]]; end end P.TriSeg.iCavity= iCav; P.Cavity.Name = [P.Cavity.Name,NameCav]; P.Cavity.n = P.Cavity.n+length(NameCav); P.TriSeg.iWall = iWall; P.Wall.Name = [P.Wall.Name,NameWall]; P.Wall.n = jWall; P.Wall.nPatch =[P.Wall.nPatch,ones(size(NameWall))]; % === Bag = passively elastic bag like pericardium P.Bag.Name={'Peri'}; P.Bag.n= size(P.Bag.Name,1); P.Bag.iWall= jWall+(1:P.Bag.n); jWall= jWall+P.Bag.n; P.Wall.Name=[P.Wall.Name,P.Bag.Name]; P.Wall.n =jWall; P.Wall.nPatch=[P.Wall.nPatch,zeros(1,P.Bag.n)]; % Cavity-> Nodes (Default) NameNode ={}; iNode =[]; for i=1:P.Cavity.n str1=P.Cavity.Name{i}; jNode=jNode+1; iNode=[iNode,jNode]; NameNode= [NameNode,str1]; end P.Cavity.iNode = iNode; P.Node.iCavity = iNode; % transfer of cavity properties to related node P.Node.Name = [P.Node.Name,NameNode]; P.Node.n = P.Node.n+length(NameNode); % ===== INPUT: VALVE with Node connections ProxDist=[... {'SyVen','Ra' };... {'Ra' ,'Rv' };... {'Rv' ,'PuArt'};... {'PuVen','La' };... {'La' ,'Lv' };... {'Lv' ,'SyArt'};... {'La' ,'Ra' };... {'Lv' ,'Rv' };... {'SyArt','PuArt'}]; nValve= size(ProxDist,1); NameValve ={}; iProx=[]; iDist=[]; for i=1:nValve StrProx=ProxDist{i,1}; StrDist=ProxDist{i,2}; iP=strmatch(StrProx,P.Node.Name); iD=strmatch(StrDist,P.Node.Name); iProx=[iProx,iP]; iDist=[iDist,iD]; Str=[StrProx,StrDist]; NameValve=[NameValve,Str]; end P.Valve.Name = NameValve; P.Valve.n = nValve; P.Valve.iNodeProx = iProx; P.Valve.iNodeDist = iDist; % ===== INPUT number of Patches per Wall P.Patch.n=sum(P.Wall.nPatch); iPatch=[1,1+cumsum(P.Wall.nPatch(1:end-1))]; P.Wall.iPatch=iPatch;%link to patch NamePatch={}; for i=1:P.Wall.n n=P.Wall.nPatch(i); for j=1:n jPatch=jPatch+1; NamePatch=[NamePatch,[P.Wall.Name{i},num2str(j)]]; end end P.Patch.Name= NamePatch; P.Patch.dT=zeros(1,P.Patch.n); %============= end backbone definition of structure ========= PutFt('Patch','Lsi' ,'All',1.9); PutFt('Patch','C' ,'All',0.001); PutFt('Patch','ActivationDelay' ,'All',[0.0;0.0]); PutFt('Patch','LsRef' ,'All',2.0); PutFt('Patch','Ls0Pas' ,'All',1.8); PutFt('Patch','dLsPas' ,'All',0.6); PutFt('Patch','SfPas' ,'All',[50,50,22,22,22]*1000);%+++++++++++ PutFt('Patch','Lsi0Act' ,'All',1.51); PutFt('Patch','LenSeriesElement','All',0.04); PutFt('Patch','SfAct' ,'All',[84,84,120,120,120]*1e3); PutFt('Patch','vMax' ,'All',[14,14,7,7,7]); PutFt('Patch','TimeAct' ,'All',[.15,.15,.42,.42,.42]); PutFt('Patch','TR' ,'All',[.4,.4,.25,.25,.25]); PutFt('Patch','TD' ,'All',[.4,.4,.25,.25,.25]); PutFt('Patch','CRest' ,'All',0.0); % VWall=VStrokeExc*[3.3,3.9,5.8,2.4,7.4].*[pLa,pRa,pLv,pLv,pRv]./P.Patch.SfAct; % AmRef=(VStrokeExc*[4.1,4.1,9.6,2.3,12.3]).^(2/3); VWall=VStrokeExc*[3.3,3.9,5.8,2.4,7.4].*[pLa,pRa,pLv,pLv,pRv]./P.Patch.SfAct; AmRef=(VStrokeExc*[4.1,4.1,9.0,3.0,12.0]).^(2/3); PutFt('Patch','VWall','All',VWall); PutFt('Patch','AmRef','All',AmRef); %======= Adaptation setpoints % ArtVen.Adapt Adapt=[]; Mat=zeros(2,P.ArtVen.n); Adapt.WallStress=Mat+500e3; Adapt.vFlowMean =Mat+0.17; Adapt.vImpact =Mat+3; P.ArtVen.Adapt =Adapt; Adapt=[]; Vec=zeros(1,P.Patch.n); Adapt.LsBe =Vec+2.3000; Adapt.LsEe =Vec+1.7500; Adapt.SfPasMax =Vec; iPatch=Str2iD({'La','Ra','Lv','Sv','Rv'},P.Patch.Name);%++++ Adapt.SfPasMax(iPatch)=[60000,60000,6000,6000,6000]; Adapt.SfPasAct(iPatch)=[6800,6800,4800,4800,4800]; Adapt.FacSfAct(iPatch)=[0.35,0.35,0.61,0.61,0.61]; P.Patch.Adapt=Adapt; %============== %=================== iC=P.ArtVen.iCavity; iCav=[iC;iC+1]; PutFt('ArtVen','k','All',[8;10]); PutFt('ArtVen','Len','All',[0.4,0.2;0.4,0.2]); PutFt('ArtVen','A0','All',P.General.q0./P.ArtVen.Adapt.vFlowMean); PutFt('ArtVen','p0','All',[[12;0.12],[1.8;0.5]]*1e3); PutFt('ArtVen','AWall','All',[0.23,0.18; 0.09,0.11].*P.ArtVen.A0); PutFt('ArtVen','p0AV',{'Sy','Pu'},[P.General.p0,1500]); PutFt('ArtVen','q0AV','All',P.General.q0); PutFt('ArtVen','kAV',{'Sy','Pu'},[1.0,2.0]); %++++++++++++++++++++++ % ArtVen specials % Cavity: starting volumes HCav={'La','Ra','Lv','Rv'}; nCav=P.Cavity.n; P.Cavity.V=zeros(1,nCav); PutFt('Cavity','V',HCav,VStrokeExc*[0.5,0.41,1.00,0.72]); iCavArt=P.ArtVen.iCavity; iCavVen=iCavArt+1; V=P.ArtVen.A0 .* P.ArtVen.Len; P.Cavity.V(iCavArt)=V(1,:); P.Cavity.V(iCavVen)=V(2,:); %TriSeg PutFt('TriSeg','V','v',0.52*VStroke); PutFt('TriSeg','Y','v',0.80*VStroke^(1/3)); P.TriSeg.Tau = 0.005; % Peri VWall=sum(GetFt('Patch','VWall',{'La1','Ra1','Lv1','Sv1','Rv1'})); VCav=sum(GetFt('Cavity','V',{'La','Ra','Lv','Rv'})); P.Bag.VRef = (67/53)*(VCav+VWall); P.Bag.k = 10.0; A0=GetFt('ArtVen','A0','Sy'); A=A0(1); PutFt('Valve','q','All',0.0); PutFt('Valve','AOpen','All',A); PutFt('Valve','ALeak','All',A*1e-6); PutFt('Valve','Len','All',sqrt(A)); PutFt('Valve','AOpen',{'RaRv','LaLv'},... 1.5* GetFt('Valve','AOpen',{'RaRv','LaLv'}) ); PutFt('Valve','ALeak',{'SyVenRa','PuVenLa'},... GetFt('Valve','AOpen',{'SyVenRa','PuVenLa'}) ); PutFt('Valve','AOpen',{'LaRa','LvRv','SyArtPuArt'},... GetFt('Valve','ALeak',{'LaRa','LvRv','SyArtPuArt'}) ); % Wall: AmDead ALv=sum(GetFt('Valve','AOpen',{'LvSyArt','LaLv'})); ARv=sum(GetFt('Valve','AOpen',{'RvPuArt','RaRv'})); ALa=sum(GetFt('Valve','AOpen',{'PuVenLa','LaLv'})); ARa=sum(GetFt('Valve','AOpen',{'SyVenRa','RaRv'})); PutFt('Wall','AmDead',{'Lv','Rv','Ra','La'},[ALv,ARv,ALa,ARa]); P.t=0; %======= Adaptation setpoints P.Bag.pAdapt= 100; % pericardial pressure Adapt=[]; Vec=zeros(1,P.Cavity.n); Adapt.WallStress=Vec; Adapt.vFlowMean =Vec; Adapt.vImpact =Vec; iCav=Str2iD({'SyArt','SyVen','PuArt','PuVen'},P.Cavity.Name);%++++ Adapt.WallStress(iCav)= 500e3; Adapt.vFlowMean(iCav) = 0.17; Adapt.vImpact(iCav) = 3; P.Cavity.Adapt=Adapt; Adapt=[]; Vec=zeros(1,P.Patch.n); Adapt.LsBe =Vec+2.3000; Adapt.LsEe =Vec+1.7500; Adapt.SfPasMax =Vec; iPatch=Str2iD({'La','Ra','Lv','Sv','Rv'},P.Patch.Name);%++++ Adapt.SfPasMax(iPatch)=[60000,60000,6000,6000,6000]; Adapt.SfPasAct(iPatch)=[6800,6800,4800,4800,4800]; Adapt.FacSfAct(iPatch)=[0.35,0.35,0.61,0.61,0.61]; P.Patch.Adapt=Adapt; % %================= General information ==================== P.General.ScaleVqY=[1e-5,1e-4,1e-1]; P.General.Dt=0.002; P.General.tEnd=36.0; P.General.tCycleRest=0.8500; P.General.tCycle= 0.8500; P.General.TimeFac=1; P.General.TauAv=0.16; P.General.q0=85e-6; P.General.p0=12200; P.General.PressFlowContr=1; end function iD= Str2iD(Str,Name) n=length(Str); iD=zeros(1,n); for i=1:n; iD(i)=strmatch(Str{i},Name); end end $#$pNodeVDot.m#$#function pNodeVDot % function pNodeVDot % Pressures in cavities p -> pressure in Nodes: p % -> flows to cavities: VDot % Theo Arts, Maastricht University, Oct 30, 2011 global P P.Node.q=0*P.Node.q; P.Node.Y=P.Node.q;% zero initialization iNodeProx=P.Valve.iNodeProx; % nodes proximal to valves iNodeDist=P.Valve.iNodeDist; % nodes distal to valves nValve=P.Valve.n; % number of valves % flow of valves to/from nodes for i=1:nValve iProx=iNodeProx(i); iDist=iNodeDist(i); q=P.Valve.q(:,i); % valve flow P.Node.q(:,iProx)=P.Node.q(:,iProx)-q; % valve flow out of nodes P.Node.q(:,iDist)=P.Node.q(:,iDist)+q; % valve flow into nodes end % flow of ArtVen object to/from nodes p0AV= SparseDiag(P.ArtVen.p0AV); % reference AV pressure drop q0AV= SparseDiag(P.ArtVen.q0AV); % reference AV flow kAV = P.ArtVen.kAV; % exponent non-linearity AV 'resistance' iCav= P.ArtVen.iCavity; % related cavities iNodeP= P.Cavity.iNode(iCav); % nodes related to arterial cavity iNodeD= P.Cavity.iNode(iCav+1); % nodes related to venous cavity Dp= P.Cavity.p(:,iCav)-P.Cavity.p(:,iCav+1); % AV cavity pressure drop Col1= ones(size(Dp,1),1); q=(Dp/p0AV).^kAV(Col1,:)*q0AV; % AV flow P.ArtVen.q=q; %Blood pressure control Facq=P.General.FacpControl; % FacpControl= current p / pRef P.Node.q(:,iNodeP)=P.Node.q(:,iNodeP)-q*Facq; % flow out of node P.Node.q(:,iNodeD)=P.Node.q(:,iNodeD)+q/Facq; % flow into node % Blood pressure control % node pressure derived for single node-cavity connection iCavNode= P.Cavity.iNode; % iNode pointed by cavity Y= 1./P.Cavity.Z; % should be a summation, not needed now Yp= P.Cavity.p .* Y; % should be a summation, not needed now P.Node.Y(:,iCavNode)=Y; % internal Y conductance of flow source P.Node.q(:,iCavNode)= P.Node.q(:,iCavNode)+Yp; % short circuit flow of node P.Node.p= P.Node.q./P.Node.Y; % node pressure by solving NodeFlow q=0 % flow into all Cavities, connected to nodes P.Cavity.VDot= (P.Node.p(:,iCavNode)-P.Cavity.p)./P.Cavity.Z; end function X=SparseDiag(x) X=sparse(diag(x(:))); end $#$PutFt.m#$#function PutFt(Type,Variable,Location,Value) % function PutFt(Type,Variable,Location,Value) % Type=string, Variable=string, Location=string % e.g. PutFt('Node','p','SystArt',2000) % inverse action of GetFt global P; Names=P.(Type).Name; %Field names of structure P iLoc=[]; % column indices [nt,nVal]=size(Value); if ischar(Location) if strcmp(Location,'All') iLoc=1:P.(Type).n; else iLoc=strmatch(Location,Names); end else for i=1:length(Location) iLoc= [iLoc,strmatch(Location{i},Names)]; end end nLoc= size(iLoc,2); if ( nLoc>1 && nVal==1 ) Value=repmat(Value,[1,nLoc]); end if ~isfield(P.(Type),Variable) P.(Type).(Variable)= zeros(nt,P.(Type).n); end P.(Type).(Variable)(end-nt+1:end,iLoc)=Value; end $#$SarcEf2Sf.m#$#function SarcEf2Sf; %Theo Arts, Maastricht University. July 30, 2006. global P; % general time Sarc= P.Patch; %==== Input variables t = P.t; Ef = Sarc.Ef ; nt = size(Ef,1); % nt: number of times; nr: number of sarcomeres Col1 = ones(nt,1); tc = Tc(t,Sarc.ActivationDelay); Lsi = Sarc.Lsi; C = Sarc.C; LenSeriesElement= SparseDiag(Sarc.LenSeriesElement); TR = SparseDiag(Sarc.TR ); %TR atrial muscle > ventricular muscle TD = SparseDiag(Sarc.TD ); TimeAct = SparseDiag(Sarc.TimeAct); %time scale of contraction pulse Ls0 = SparseDiag(Sarc.Lsi0Act); %zero active stress sarcomere length Ls0Pas = SparseDiag(Sarc.Ls0Pas ); dLsPas = SparseDiag(Sarc.dLsPas ); SfPas = SparseDiag(Sarc.SfPas ); CRest = SparseDiag(Sarc.CRest ); %Resting C-value (Ca++ contractility) LsRef = SparseDiag(Sarc.LsRef ); SfAct = SparseDiag(Sarc.SfAct ); vMax = SparseDiag(Sarc.vMax ); % series elasticity and sarcomere shortening velocity Ls = exp(Ef)*LsRef; Sarc.Ls = Ls; %=== Active sarcomere % constants related to timing are mainly based on experimental findings L = max(Lsi/Ls0-1,0.0001) ; % normalized sarc length for active contraction tA = (0.65+1.0570*L)*TimeAct; % 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(0.75*9.1204*L.^2); % regulates increase of contractility with Ls % FL= tanh((9.1204/2)*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) * (1.51*SfAct) ; SfRest = L*(1.51*CRest*SfAct) ; k1= 10; k2= 0.01; kk3= 2*(LsRef/dLsPas); LfP = exp(Ef)*(LsRef/Ls0Pas); yEcm = LfP.^k1; SfEcm = 0.0349*(yEcm-1)*SfPas;% Ls=Ls0Pas, zero stress y = exp(log(LfP)*kk3); SfTit = (y-1)*(k2*SfAct); % titin is softer than ecm, proportional with SfAct SfPasT= SfTit + SfEcm; DSfPasDEf= y*(k2*SfAct*kk3) + yEcm*(0.0349*k1*SfPas); %=== Stress/Ls and stiffness, collected for output/problem Buckle!!+++++ Sarc.SfEcm = SfEcm;% passive stress ECM Sarc.SfPasT = SfPasT;% total passive stress +++++++++++ to be removed LNormSe = (Ls-Lsi)/LenSeriesElement; Sarc.LsiDot = (LNormSe-1)*vMax; Sarc.Sf = SfPasT + (SfIso+SfRest).*LNormSe - SfRest; Sarc.DSfDEf = DSfPasDEf+(SfIso.*Ls)/LenSeriesElement; % estimate of sarcomere stiffness P.Patch=Sarc; end 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 end function X=SparseDiag(x); X=sparse(diag(x(:))); end $#$SparseDiag.m#$#function X=SparseDiag(x) X=sparse(diag(x(:))); end $#$SplitMerge.m#$#function SplitMerge(PatchName,nSM) % function SplitMerge(PatchName,nSM); % SplitMerge splits or merges patches within a wall % PatchName is string % nSM= number of patches after splitting, % if nSM<0, - number of merging patches, starting from named patch % if more merging than possible, than nSM merging is maximized. % Example: PatchName='Lv1', nSM=3-> LV patch split in 3 patches % followed by: PatchName='Lv1', nSM=-2 -> LV patch 2,3 merged % All variables are copied, wall volume and area is subdivided/averaged global P % PatchName='Lv1'; nSM=-2; WallName=PatchName(isletter(PatchName)); % wall identity nP=GetFt('Wall','nPatch',WallName); % current number of patches in wall FieldPatch= fieldnames(P.Patch); % all field names in P.Patch nfield=length(FieldPatch); FieldAdapt= fieldnames(P.Patch.Adapt); % all field names in P.Patch nAdapt=length(FieldAdapt); iP=strmatch(PatchName,P.Patch.Name); %location to insert Patch.records if nSM>1 PutFt('Wall','nPatch',WallName,nP+nSM-1); % new value of nPatch P.Patch.n= P.Patch.n+nSM-1; % new number of patches RgP=iP+(0:nSM-1); % indices of patch group for i=1:nAdapt % copying adaptation target values Var= P.Patch.Adapt.(FieldAdapt{i}); nVar=size(Var,2); if nVar>1 Var=Var(:,[1:iP-1,repmat(iP,[1,nSM]),iP+1:end]); P.Patch.Adapt.(FieldAdapt{i})=Var; end end for i=1:nfield % copy fields Var= P.Patch.(FieldPatch{i}); nVar=size(Var,2); if nVar>1 Var=Var(:,[1:iP-1,repmat(iP,[1,nSM]),iP+1:end]); P.Patch.(FieldPatch{i})=Var; end end % sub division in smaller part for volumes and areas P.Patch.VWall(RgP)=P.Patch.VWall(RgP)/nSM; P.Patch.AmRef(RgP)=P.Patch.AmRef(RgP)/nSM; P.Patch.DADT(:,RgP)=P.Patch.DADT(:,RgP)/nSM; P.Patch.Am0(:,RgP)=P.Patch.Am0(:,RgP)/nSM; P.Patch.Am(:,RgP)=P.Patch.Am(:,RgP)/nSM; P.Patch.dT(RgP)=P.Patch.dT(RgP)/nSM; else % Merge is inverse of Split iPMax=GetFt('Wall','iPatch',WallName)+nP-1; nMerge=min(iPMax-iP+1,-nSM); %number of patches to merge PutFt('Wall','nPatch',WallName,nP-nMerge+1); % new value of nPatch P.Patch.n= P.Patch.n-nMerge+1; % new number of patches RgP=iP+(0:nMerge-1); % index patches to merge for i=1:nfield Var= P.Patch.(FieldPatch{i}); nVar=size(Var,2); if nVar>1 if isnumeric(Var) Var(:,iP)= mean(Var(:,RgP),2); end Var=Var(:,[1:iP,iP+nMerge:end]); P.Patch.(FieldPatch{i})=Var; end end for i=1:nAdapt Var= P.Patch.Adapt.(FieldAdapt{i}); nVar=size(Var,2); if nVar>1 if isnumeric(Var) Var(:,iP)= mean(Var(:,RgP),2); end Var=Var(:,[1:iP,iP+nMerge:end]); P.Patch.Adapt.(FieldAdapt{i})=Var; end end % volumes and areas have to be summed instead of averaged P.Patch.VWall(iP)=P.Patch.VWall(iP)*nMerge; P.Patch.AmRef(iP)=P.Patch.AmRef(iP)*nMerge; P.Patch.DADT(:,iP)=P.Patch.DADT(:,iP)*nMerge; P.Patch.Am0(:,iP)=P.Patch.Am0(:,iP)*nMerge; P.Patch.Am(:,iP)=P.Patch.Am(:,iP)*nMerge; P.Patch.dT(iP)=P.Patch.dT(iP)*nMerge; end n2iPatch; % finish end function n2iPatch global P P.Wall.iPatch=[1,1+cumsum(P.Wall.nPatch(1:end-1))]; %renumber patches NamePatch={}; % new patch names for i=1:P.Wall.n n=P.Wall.nPatch(i); for j=1:n NamePatch=[NamePatch,[P.Wall.Name{i},num2str(j)]]; end end P.Patch.Name=NamePatch; P2SVar; % new set of state variables end $#$SteadyStateP.m#$#function OutNew=SteadyStateP % function OutNew=SteadyStateP % OutNew= new estimate vector of steady state parameters, to be used % as start condition for next heart beat global P % read input-output record, take logarithm for relative change In = log(P.Adapt.In ); Out = log(P.Adapt.Out); [nx,np]=size(In); % [number of samples, number of parameters] yRef= Out(end,:); 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); if nxn>1 X=In(Rgn,:); Y=Out(Rgn,:); %U=Out(Rgn-1,:); nt=size(X,1); Col1=ones(nt,1); YmX =Y-X; M1 = [YmX ,Col1]; N1 = pinv(M1)*(Y-Col1*yRef); dy = N1(end,:); ymx=YmX(end,:); %last instationarity difference % ymx=X(end,:)-Y(end-1,:); %last dy fac=sqrt( sum(ymx.^2)/sum(dy.^2+1e-10) ); dy= min(fac,1.2)*dy; % correction amplitude less than last difference yNew=yRef+dy; else yNew=yRef; end OutNew=exp(yNew); % prediction after exponential end % xm=mean(X); ym=mean(Y); % X1=X-Col1*xm; Y1=Y-Col1*ym; % DXDY= pinv(Y1)*X1; % yNew= (yRef-ym)*DXDY + yRef; % 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; % % % % $#$SVar2P.m#$#function SVar2P % function SVar2P % Transfer of scaled state variables in P.SVar to P-structure (SI-units) % Theo Arts, Maastricht University, Oct 30, 2011 global P ScaleVqY=P.General.ScaleVqY; % scaling factors for volume, flow, distance ScV=ScaleVqY(1); Scq=ScaleVqY(2); ScY=ScaleVqY(3); Sc0=1; nCav =P.Cavity.n; % number of cavities nValve=P.Valve.n; % number of valves nPatch=P.Patch.n; % number of patches with representative sarcomere % finding indices for value transfer a=cumsum([0,1,nCav,nValve,nPatch,nPatch,1,1]); iB=a(1:end-1)+1; iE=a(2:end); % successive begin and end indices % value transfer i=1; P.t = P.SVar(:,iB(i):iE(i)); i=i+1; P.Cavity.V = ScV*P.SVar(:,iB(i):iE(i)); i=i+1; P.Valve.q = Scq*P.SVar(:,iB(i):iE(i)); i=i+1; P.Patch.C = P.SVar(:,iB(i):iE(i)); i=i+1; P.Patch.Lsi= P.SVar(:,iB(i):iE(i)); i=i+1; P.TriSeg.V = ScV*P.SVar(:,iB(i):iE(i)); i=i+1; P.TriSeg.Y = ScY*P.SVar(:,iB(i):iE(i)); i=i+1; end $#$SVarDot.m#$#function OutDot=SVarDot(tDummy,SVar,flag) % function OutDot=SVarDot(tDummy,SVar,flag) % State Variables -> Time derivatives of State Variables % ready to use in MatLab function odeXXX() to solve set of Diff Eq % Theo Arts, Maastricht University, Oct 30, 2011 %====test by execution of %Par2P; P2SVar; SVDt=SVarDot(0,P.SVar',0); %==== global P P.SVar= SVar'; % store state variables SVar SVar2P; % state variables SVar -> physiologic representation P.xx P.tDot=ones(size(P.t)); % time derivative of time = 1 MemAlloc; % necessary memory allocations ArtVenV2p; % arteries to veins compliant network PatchWallA2T; % patch and wall: Am= Am0+T*DADT ChamberV2p; % Chamber, pTrans and Wall.Am,T TriSegV2p; % TriSeg, pTrans and Wall.Am,T Wall2Patch2Sarc; % filling Sarc with LsiDot,CDot BagV2p; % pTrans of Pericardium pCavity; % Determines p in cavities by adding P.Wall.pTrans(:,xx) pNodeVDot; % Node pressures and cavity VDot's ValveqDot; % flow derivatives qDot P2SVarDot; % transfer of derivatives to P-structure OutDot= real(P.SVarDot)'; % odeXX requires SVarDot to be a row vector end $#$TimingP.m#$#function TimingP %SHOULD BE IMPROVED!!!! global P TauDisp=0.0; % dispersion of activation per wall, if relevant % Reading timing parameters G = P.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; % atrial activation duration tv= (0.10*tCycleRef +0.40*tCycle)*TimeFac; % ventricular " " iWLv= P.TriSeg.iWall; iWSv= iWLv+1; iWRv= iWLv+2; iWLa= P.Chamber.iWall(1); iWRa= P.Chamber.iWall(2); iWVec= [iWLv,iWSv,iWRv,iWLa,iWRa]; tAVec= [tv,tv,tv,ta,ta]; % Delay times electrical activation tRa2La= 0.0284 * (tCycleRef/0.85) * TimeFac; tRa2Rv= TauAv+P.General.dTauAv; % AV-Delay tRv2Lv= 0.0; % pacing delay LBBB, negative-> prepacing tRv2Sv= 0.33*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= P.t(end); tEnd = tStart+tCycle; % assume Ra as main trigger for activation tRa=tStart; tLa=[]; tLv=[]; tSv=[]; tRv=[]; t=tStart; while t dimensions of the 'double bubble', % myofiber stress Sf, wall tension T and cavity pressures p % VS and YS repesent septal volume displacement and junction radius. % State variables P.TriSeg V and Y represent initial estimates to calculate % VS and YS accurately. % Theo Arts, Maastricht University, Oct 30, 2011 global P; TriSeg=P.TriSeg; n = TriSeg.n; % number of TriSeg's ic = TriSeg.iCavity; iCavity=[ic;ic+1]; %related cavities iw = TriSeg.iWall ; iWall=[iw;iw+1;iw+2]; %related walls rhob=P.General.rhob; % blood density for i=1:n %for all TriSeg's iC = iCavity(:,i); % 2 cavities iW = iWall(:,i); % 3 walls Am0 = P.Wall.Am0(:,iW); %zero stress wall area DADT = P.Wall.DADT(:,iW); %wall compliance nt = size(Am0,1); %number of time points VWall= P.Wall.VWall(iW); % 3 wall volumes VWL = (VWall(1)+VWall(2))/2; % enclosed wall volume 1st cavity VWR = (VWall(3)+VWall(2))/2; % enclosed wall volume 2nd cavity V = max(0,P.Cavity.V(:,iC))+repmat([VWL,VWR],[nt,1]); %2 midwall volumes(t) VS=TriSeg.V(:,i); YS= TriSeg.Y(:,i); % 1st estimate of [V,Y] VRef=mean(V(:)); % reference volume dv=0.01*VRef; dy=0.02*VRef^(1/3); % increments for d[Txy]/dVY [Tx0,Ty0]=VY2Txy(VS,YS,Am0,DADT,V); % tension Txy =fu(VY) [TxV,TyV]=VY2Txy(VS+dv,YS,Am0,DADT,V); % partial V derivative [TxY,TyY]=VY2Txy(VS,YS+dy,Am0,DADT,V); % partial Y derivative DTxX=(TxV-Tx0)/dv; DTyX=(TyV-Tx0)/dv; % Jacobian matrix inversion DTxY=(TxY-Tx0)/dy; DTyY=(TyY-Tx0)/dy; DET= DTxX.*DTyY-DTxY.*DTyX; % determinant % 1st iteration to solve TriSeg geometry VY dV=(-DTyY.*Tx0+DTxY.*Ty0)./DET; dY=(+DTyX.*Tx0-DTxX.*Ty0)./DET; VS=VS+dV; YS=YS+dY; Tau=TriSeg.Tau; TriSeg.VDot(:,i)= dV/Tau; % keep track of solution TriSeg.YDot(:,i)= dY/Tau; for j=1:2 % extra iterations for solution of TriSeg geometry [Tx0,Ty0]=VY2Txy(VS,YS,Am0,DADT,V); dV=(-DTyY.*Tx0+DTxY.*Ty0)./DET; dY=(+DTyX.*Tx0-DTxX.*Ty0)./DET; VS=VS+dV; YS=YS+dY; end % writing geometric data TriSeg TriSeg.YS(:,i)=YS; % final solution Rv-Sv-Lv junction radius TriSeg.VS(:,i)=VS; % volume of septal cap % writing wall data [Tx0,Ty0,Am,Cm,T]=VY2Txy(VS,YS,Am0,DADT,V); % solution TriSeg P.Wall.Am(:,iWall)= Am; % wall area P.Wall.Cm(:,iWall)= Cm; % wall curvature P.Wall.T(:,iWall )= T ; % wall tension P.Wall.pTrans(:,iWall)= 2*Cm.*T; % transmural pressure % Cavity impedance properties, needed to make node connection Vw= repmat([VWL,VWR],[nt,1]); Vm= V + Vw; Len= 2*Vm.^(1/3); A = ( V + 0.1*Vw ) ./Len; P.Cavity.A(:,iC) = A; % cross-sectional area for valve inflow and outflow pressure DADT= P.Wall.DADT(:,iWall([1,3])); P.Cavity.Z(:,iCavity) = 0.2*sqrt(rhob*Len./abs(DADT))./A; % Compatibitlity with Tube end P.TriSeg=TriSeg; end function [Tx,Ty,Am,Cm,Tm]=VY2Txy(VS,YS,Am0,DADT,VLR) % 1st order approximation of TriSeg according to J. Lumens et al. % For a wall with zero-stress area Am0 and compliance DADT % cap volume VS, junction radius YS and cavity volumes VLR=[VL,VR] % Result: Summed axial and radial tension components [Tx,Ty] on junction % for 3 walls: Am= midwall area, Cm= midwall curvature, Tm= wall tension Vm= [VLR,VS]* [... -1 0 0 0 0 1 1 1 1]; Ym=[YS,YS,YS]; % 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; R2 = X2+Y2; % Am = pi*R2; % midwall cap area Am = max(Am0,pi*R2); % midwall cap area, buckling with T<0 Cm = 2*Xm./R2; % midwall cap curvature % calculation of tension T and components Tx, Ty Tm=(Am-Am0)./DADT; Sin= Ym.*Cm; Cos= (Y2-X2)./R2; Txi = Cos.*Tm; % Tyi = Sin.*Tm; TRef=sqrt(sum(Tm.^2,2)); Tx= sum(Txi,2)./TRef; % axial tension component Ty= sum(Tyi,2)./TRef; % radial tension component end $#$ValveqDot.m#$#function ValveqDot % function ValveqDot % Node pressure differences -> valve flow acceleration qDot % Currently, mitral valve (LaLv) and tricuspid valve (RaRv) close % completely with backflow during diastole, still open to improvement. % Theo Arts, Maastricht University, Oct 30, 2011 global P iNodeProx=P.Valve.iNodeProx; iNodeDist=P.Valve.iNodeDist; nt=size(P.t,1); Col1=ones(nt,1); AOpen = P.Valve.AOpen(Col1,:); % open valve cross-section ALeak = P.Valve.ALeak(Col1,:); % closed valve cross-section % Ws=P.Valve.AvWalls; Vs=P.Valve.AvValves; Ws=[7 9]; Vs=[5 2]; % Ws=[]; Vs=[]; % empty arrays turns off papillary muscle function T = P.Wall.T(:,Ws); %wall tension related to pap. muscle DADT= P.Wall.DADT(:,Ws); %wall stiffness Am0 = P.Wall.Am0(:,Ws); %wall zero-stress area Diast= tanh(100*max(0.001,T.*DADT./Am0-0.1).^2 ); % diastole->1.0 ALeak(:,Vs)=0.1*AOpen(:,Vs).*Diast; %ALeak=0.3*Diast*AOpen % No regurgitation due to PMs. % Valve qDot q = P.Valve.q; Len = P.Valve.Len ; % effective length of flow channel rhob = 1050 ; % density of blood Dp = P.Node.p(:,iNodeProx)-P.Node.p(:,iNodeDist); % pressure drop iCavProx= P.Node.iCavity(P.Valve.iNodeProx); % Valve->prox Node->iCavity iCavDist= P.Node.iCavity(P.Valve.iNodeDist); % Valve->dist Node->iCavity AProx = P.Cavity.A(:,iCavProx); % proximal area ADist = P.Cavity.A(:,iCavDist); % distal area AExt = min(AProx,ADist); % minimum external orifice area, fu(t) AOpenEff = min(AOpen,AExt); % open flow orifice area, fu(t) ALeakEff = min(ALeak,AExt); % leak flow orifice area, fu(t) % Slow valve closure to stabilize solution Diff.Eq. Reverse = double(AOpenEff0); % 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 % Closure by a continuous function A= Closed.*ABw + Open.*AFw + Closing.*AClosing; % Bernouilli pressure drop v1= qV./APr; v2= qV./A; v3= qV./ADi; % velocities 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(Col1,:)./A+ 0.5*(1./sqrt(APr)+1./sqrt(ADi))); % inertia P.Valve.L=L; % inertia P.Valve.qDot= (Dp-DpB)./L; % flow derivative end $#$Wall2Patch2Sarc.m#$#function Wall2Patch2Sarc % function Wall2Patch2Sarc % After multiple patch geometry has been solved, for walls and patches % stresses and areas are calculated for output. No effect for further % solution % Theo Arts, Maastricht University, Oct 30, 2011 global P; for iWall=1:P.Wall.n % in each wall tension T is transferred to the patches nPatch=P.Wall.nPatch(iWall); iPatch=P.Wall.iPatch(iWall)+(0:nPatch-1); P.Patch.T(:,iPatch)=P.Wall.T(:,repmat(iWall,[1,nPatch])); %each patch end Am= P.Patch.Am0 + P.Patch.T .* P.Patch.DADT; % Patch area P.Patch.Am= Am; P.Patch.Ef= 0.5*log(Am/SparseDiag(P.Patch.AmRef)); % Sarcomere strain SarcEf2Sf % sarcomere mechanics end $#$