function model = mttPropagateCausality(model,branch)
is_root_model = (nargin==1) ;
objects = mttGetFieldNames(model,'obj') ;
for i = 1:length(objects)
object_name = objects{i} ;
object = getfield(model,'obj',object_name) ;
if is_root_model
branch = mttDetachText(model.source,'/') ;
end
here = [branch,':',object_name] ;
if isfield(object,'obj')
for i = 1:mttGetFieldLength(object,'interface')
port_name = object.interface(i).name ;
inbond = object.interface(i).in ;
outbond = object.interface(i).out ;
inmap = object.interface(i).map.in ;
outmap = object.interface(i).map.out ;
[inbond_effort,inbond_flow,inbond_unicausal] = mttGetBondCausality(model,inbond) ;
[outbond_effort,outbond_flow,outbond_unicausal] = mttGetBondCausality(model,outbond) ;
[inmap_effort,inmap_flow,inmap_unicausal] = mttGetBondCausality(object,inmap) ;
[outmap_effort,outmap_flow,outmap_unicausal] = mttGetBondCausality(object,outmap) ;
[model,inbond_ok] = mttUpdateBondCausality(model,inbond,inmap_effort,inmap_flow,inmap_unicausal) ;
[model,outbond_ok] = mttUpdateBondCausality(model,outbond,outmap_effort,outmap_flow,outmap_unicausal) ;
[object,inmap_ok] = mttUpdateBondCausality(object,inmap,inbond_effort,inbond_flow,inbond_unicausal) ;
[object,outmap_ok] = mttUpdateBondCausality(object,outmap,outbond_effort,outbond_flow,outbond_unicausal) ;
ok = inbond_ok & outbond_ok & inmap_ok & outmap_ok ;
mttAssert(ok,['Causal conflict at port "',port_name,'" in ',here]) ;
end
if is_root_model
branch = object_name ;
else
branch = [branch,'/',object_name] ;
end
object = mttPropagateCausality(object,branch) ;
model = setfield(model,'obj',object_name,object) ;
else
number_of_interfaces = mttGetFieldLength(object,'interface') ;
switch object.class
case {'Se','Sf','De','Df'}
for i = 1:number_of_interfaces
port_name = object.interface(i).name ;
inbond = object.interface(i).in ;
outbond = object.interface(i).out ;
mttAssert(xor(isempty(inbond),isempty(outbond)),...
['"',object.class,'" objects must have exactly one attached bond in ',here]) ;
switch object.class
case {'Se','De'},
[model,ok] = mttUpdateBondCausality(model,outbond,[],1,1) ; % Constraint
[model] = mttUpdateBondCausality(model,outbond,1,[],1) ; % Preference
case {'Sf','Df'},
[model,ok] = mttUpdateBondCausality(model,outbond,0,[],1) ; % Constraint
[model] = mttUpdateBondCausality(model,outbond,[],0,1) ; % Preference
end
mttAssert(ok,['Causal constraint violation at port "',port_name,'" in ',here])
end
case '0',
mttAssert(number_of_interfaces>1,...
['Less than two interfaces at 0-junction ',here]) ;
imposed_effort = [] ;
resultant_flow = [] ;
for j = 1:number_of_interfaces
inbond = object.interface(j).in ;
outbond = object.interface(j).out ;
if isempty(inbond)
bond(j) = outbond ;
orientation(j) = 0 ;
else
bond(j) = inbond ;
orientation(j) = 1 ;
end
[effort,flow,unicausal] = mttGetBondCausality(model,bond(j)) ;
if ~isempty(effort)
if effort==orientation(j)
mttAssert(isempty(imposed_effort),...
['Over-determined effort at 0-junction ',here]) ;
imposed_effort = bond(j) ;
end
end
if ~isempty(flow)
if flow==orientation(j)
mttAssert(isempty(resultant_flow),...
['Over-determined flow at 0-junction ',here]) ;
resultant_flow = bond(j) ;
end
end
end
for j = 1:number_of_interfaces
if ~isempty(imposed_effort)
if bond(j)~=imposed_effort
model = mttUpdateBondCausality(model,bond(j),~orientation(j),[],[]) ;
end
end
if ~isempty(resultant_flow)
if bond(j)~=resultant_flow
model = mttUpdateBondCausality(model,bond(j),[],~orientation(j),[]) ;
end
end
end
case '1',
mttAssert(number_of_interfaces>1,...
['Less than two interfaces at 1-junction ',here]) ;
imposed_flow = [] ;
resultant_effort = [] ;
for j = 1:number_of_interfaces
inbond = object.interface(j).in ;
outbond = object.interface(j).out ;
if isempty(inbond)
bond(j) = outbond ;
orientation(j) = 0 ;
else
bond(j) = inbond ;
orientation(j) = 1 ;
end
[effort,flow,unicausal] = mttGetBondCausality(model,bond(j)) ;
if ~isempty(effort)
if effort~=orientation(j)
mttAssert(isempty(resultant_effort),...
['Over-determined effort at 1-junction ',here]) ;
resultant_effort = bond(j) ;
end
end
if ~isempty(flow)
if flow~=orientation(j)
mttAssert(isempty(imposed_flow),...
['Over-determined flow at 1-junction ',here]) ;
imposed_flow = bond(j) ;
end
end
end
for j = 1:number_of_interfaces
if ~isempty(resultant_effort)
if bond(j)~=resultant_effort
model = mttUpdateBondCausality(model,bond(j),orientation(j),[],[]) ;
end
end
if ~isempty(imposed_flow)
if bond(j)~=imposed_flow
model = mttUpdateBondCausality(model,bond(j),[],orientation(j),[]) ;
end
end
end
end
end
end