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
    
