function model = mttFetchBondgraph(filename) mttAssert(mttFileExists(filename),... ['File "',filename,'" not found']) ; mttNotify([' ...processing ',filename]) ; mttWriteNewLine ; model.bondgraph = filename ; content = mttReadFile(filename) ; L = length(content) ; N = 1 ; processing = (N<=L) ; while processing jump = 1 ; line = mttClipText(content{N}) ; if ~isempty(line) no_string_terminator = isempty(findstr('\001',line)) ; if no_string_terminator if mttIsNumericText(line) numbers = round(str2num(line)) ; % only interested in integer values if numbers(1)==2 % ... line definition forward_arrow = numbers(14)~=0 ; reverse_arrow = numbers(15)~=0 ; arrow = (forward_arrow | reverse_arrow) ; depth = numbers(7) ; is_visible = rem(depth,10)==0 ; if is_visible mttAssert(~arrow,... ['Line ',num2str(N),': Arrows are not recognised in bond graphs']) ; number_of_points = numbers(16) ; coordinate_string = [] ; M = 0 ; matching = 1 ; while matching M = M + 1 ; if isempty(coordinate_string) coordinate_string = content{N+M} ; else coordinate_string = [coordinate_string,' ',content{N+M}] ; end mttAssert(mttIsNumericText(coordinate_string),... ['Line ',num2str(N+M),': Coordinates not recognised']) ; coordinates = str2num(coordinate_string) ; matching = length(coordinates)<2*number_of_points ; end mttAssert(length(coordinates)==2*number_of_points,... ['Line ',num2str(N+1),': Wrong number of coordinates']) ; jump = jump + M ; if number_of_points==2 model = create_line(model,coordinates) ; elseif number_of_points>2 model = create_bond(model,coordinates) ; end else jump = jump + forward_arrow + reverse_arrow ; end end end else [last_word,first_part] = mttDetachText(line,' ') ; if mttIsNumericText(first_part) numbers = round(str2num(first_part)) ; % only interested in integer values if numbers(1)==4 % ... text definition depth = numbers(4) ; is_visible = rem(depth,10)==0 ; if is_visible coordinates = numbers(12:13) ; text = mttCompressText(mttCutText(last_word,'\001')) ; identifier = mttCutText(text,'[') ; qualifier = mttExtractText(text,'[',']') ; name.label = [] ; name.domain = [] ; name.domain_item = [] ; name.class = [] ; name.object = [] ; if isempty(identifier) name.label = qualifier ; model = create_label(model,name,coordinates) ; else [name.domain,name.domain_item] = mttCutText(qualifier,'::') ; [name.class,name.object] = mttCutText(identifier,':') ; model = create_object(model,name,coordinates) ; end end end end end end N = N + jump ; processing = (N<=L) ; end model = incorporate_anonymous_objects(model) ; model = identify_causal_assignments(model) ; model = identify_object_binding(model) ; model = identify_object_interfaces(model) ; model = associate_external_domains(model) ; model = tidy_up(model) ; function model = create_line(model,coordinates) next = 1 + mttGetFieldLength(model,'line') ; model.line(next).xy1 = coordinates(1:2) ; model.line(next).xy2 = coordinates(3:4) ; model.line(next).mid = (coordinates(1:2)+coordinates(3:4))/2 ; function model = create_bond(model,coordinates) next = 1 + mttGetFieldLength(model,'bond') ; N = length(coordinates) ; q1 = coordinates(N-1:N) ; p1 = coordinates(1:2) ; p2 = coordinates(3:4) ; q2 = coordinates(N-3:N-2) ; p3 = coordinates(5:6) ; q3 = coordinates(N-5:N-4) ; ps = norm(p2-p1) ; qs = norm(q2-q1) ; orientation = sign(ps-qs) ; switch orientation case 1, % harpoon points forward xy1 = p1 ; xy2 = q2 ; v1 = p2-p1 ; v2 = q2-q3 ; harpoon = q1-q2 ; case -1, % harpoon points backward xy1 = p2 ; xy2 = q1 ; v1 = q2-q1 ; v2 = p2-p3 ; harpoon = p1-p2 ; end harpoon_side = sign_cross_product(v2,harpoon) ; mttAssert(~(orientation==0 | harpoon_side==0),... ['Ambiguous bond orientation between[',num2str(xy1),'] and [',num2str(xy2),']']) ; model.bond(next).xy1 = xy1 ; model.bond(next).xy2 = xy2 ; model.bond(next).v1 = v1 ; model.bond(next).v2 = v2 ; model.bond(next).harpoon = harpoon ; model.bond(next).harpoon_side = harpoon_side ; model.bond(next).from.obj = [] ; model.bond(next).from.interface = [] ; model.bond(next).to.obj = [] ; model.bond(next).to.interface = [] ; model.bond(next).flow = [] ; model.bond(next).effort = [] ; model.bond(next).unicausal = [] ; model.bond(next).domain = [] ; model.bond(next).domain_item = [] ; function model = create_label(model,name,coordinates) inner_name = mttExtractText(name.label,'<','>') ; if isempty(inner_name) label_name = name.label ; is_inline = 0 ; else label_name = inner_name ; is_inline = 1 ; end mttValidateName(label_name) ; next = 1 + mttGetFieldLength(model,'label') ; model.label(next).name = label_name ; model.label(next).is_inline = is_inline ; model.label(next).xy = coordinates ; function model = create_object(model,name,coordinates) global mtt_environment domain_names = mttGetFieldNames(mtt_environment,'domain') ; is_anonymous = 0 ; if isempty(name.object) switch name.class case {'0','1'}, is_anonymous = 1 ; case 'SS', mttAssert(~isempty(name.object),... 'Anonymous "SS" object') ; otherwise, name.object = name.class ; end end if is_anonymous next = 1 + mttGetFieldLength(model,'anonymous') ; model.anonymous(next).class = name.class ; model.anonymous(next).xy = coordinates ; else object_names = mttGetFieldNames(model,'obj') ; if ~isempty(object_names) mttAssert(~ismember(name.object,object_names),... ['Repeated object: "',name.object,'"']) ; end switch name.class case {'0','1'}, mttValidateName(name.object) ; mttAssert(~ismember(name.object,{'in','out'}),... 'Junctions cannot use reserved port names') ; case {'SS','Se','Sf','De','Df'}, mttValidateName(name.object) ; if isempty(name.domain) | isempty(mtt_environment) model = setfield(model,'obj',name.object,'domain',[]) ; model = setfield(model,'obj',name.object,'domain_item',[]) ; else mttAssert(ismember(name.domain,domain_names),... ['Unrecognised domain "',name.domain,'"']) ; dom = getfield(mtt_environment,'domain',name.domain,'dom') ; item = getfield(mtt_environment,'domain',name.domain,'item') ; if isempty(item) public_domain = getfield(mtt_environment,'public_domain',{dom}) ; item_names = mttGetFieldNames(public_domain,'item') ; mttAssert(ismember(name.domain_item,item_names),... ['Item "',name.domain_item,'" not found in public domain "',name.domain,'"']) ; item_name = name.domain_item ; else mttAssert(isempty(name.domain_item),... ['Item unspecified in public domain "',name.domain,'"']) ; item_name = item ; end model = setfield(model,'obj',name.object,'domain',dom) ; model = setfield(model,'obj',name.object,'domain_item',item_name) ; end otherwise, mttValidateName(name.class) ; mttValidateName(name.object) ; mttAssert(~ismember(name.object,{'in','out'}),... 'Objects cannot use reserved port names') ; end model = setfield(model,'obj',name.object,'class',name.class) ; model = setfield(model,'obj',name.object,'xy',coordinates) ; end function model = incorporate_anonymous_objects(model) number_of_objects = mttGetFieldLength(model,'anonymous') ; last = length(num2str(number_of_objects)) ; for i = 1:last object_number(i) = '0' ; end for i = 1:number_of_objects anonymous_object = getfield(model,'anonymous',{i}) ; current_number = num2str(i) ; width = length(current_number) ; first = last-width+1 ; object_number(first:last) = current_number ; object_name = ['mtt_obj',object_number] ; model = setfield(model,'obj',object_name,model.anonymous(i)) ; end function r = sign_cross_product(v1,v2) r = sign(v1(1)*v2(2) - v1(2)*v2(1)) ; function model = identify_causal_assignments(model) L = mttGetFieldLength(model,'line') ; if L>0 N = mttGetFieldLength(model,'bond') ; for j = 1:L for i = 1:N s(i,j) = norm(model.line(j).mid - model.bond(i).xy1) ; f(i,j) = norm(model.line(j).mid - model.bond(i).xy2) ; end end [min_range_start,nearest_bond_start] = min(s) ; [min_range_finish,nearest_bond_finish] = min(f) ; equidistant = min_range_start==min_range_finish ; at_harpoon = min_range_start>min_range_finish ; for j = 1:L fulcrum = num2str(model.line(j).mid) ; mttAssert(~equidistant(j),... ['Ambiguous causal line at [',num2str(model.line(j).mid),']']) ; if at_harpoon(j) index = nearest_bond_finish(j) ; bond = model.bond(index) ; terminal = bond.xy2 ; terminal_vector = bond.v2 ; else index = nearest_bond_start(j) ; bond = model.bond(index) ; terminal = bond.xy1 ; terminal_vector = bond.v1 ; end to_lhs = norm(model.line(j).xy1 - terminal) ; to_mid = norm(model.line(j).mid - terminal) ; to_rhs = norm(model.line(j).xy2 - terminal) ; mttAssert(to_mid<norm(bond.harpoon),... ['Cannot assign causality at [',num2str(fulcrum),']']) ; causality_ok = 0 ; is_unicausal = to_mid<min(to_lhs,to_rhs) ; if is_unicausal [bond.flow,causality_ok] = mttAssign(bond.flow,at_harpoon(j)) ; [bond.effort,causality_ok] = mttAssign(bond.effort,at_harpoon(j)) ; else causal_vector = (right-left) * sign(to_mid>to_left) ; causal_assignment = sign_cross_product(terminal_vector,causal_vector) ; mttAssert(causal_assignment~=0,... ['Cannot determine causality near [',num2str(fulcrum),']']) ; if causal_assignment==bond.harpoon_side [bond.flow,causality_ok] = mttAssign(bond.flow,at_harpoon(j)) ; else [bond.effort,causality_ok] = mttAssign(bond.effort,at_harpoon(j)) ; end end mttAssert(causality_ok,... ['Ambiguous causal assignment near [',num2str(fulcrum),']']) ; bond.unicausal = mttCompare(bond.flow,bond.effort) ; model.bond(index) = bond ; end end function model = identify_object_binding(model) object_names = mttGetFieldNames(model,'obj') ; number_of_objects = length(object_names) ; number_of_bonds = mttGetFieldLength(model,'bond') ; for j = 1:number_of_bonds bond = model.bond(j) ; for i = 1:number_of_objects object = getfield(model,'obj',object_names{i}) ; origin_proximity(i) = norm(object.xy - bond.xy1) ; target_proximity(i) = norm(object.xy - bond.xy2) ; end [range,index] = min(origin_proximity) ; origin_name = object_names{index} ; bond.from.obj = origin_name ; bond.from.interface = [] ; [range,index] = min(target_proximity) ; target_name = object_names{index} ; bond.to.obj = target_name ; bond.to.interface = [] ; model = setfield(model,'bond',{j},bond) ; origin = getfield(model,'obj',origin_name) ; next = 1 + mttGetFieldLength(origin,'bond') ; origin.bond(next).number = j ; origin.bond(next).is_inbond = 0 ; model = setfield(model,'obj',origin_name,origin) ; target = getfield(model,'obj',target_name) ; next = 1 + mttGetFieldLength(target,'bond') ; target.bond(next).number = j ; target.bond(next).is_inbond = 1 ; model = setfield(model,'obj',target_name,target) ; end function model = identify_object_interfaces(model) object_names = mttGetFieldNames(model,'obj') ; number_of_objects = length(object_names) ; number_of_labels = mttGetFieldLength(model,'label') ; number_of_bonds = mttGetFieldLength(model,'bond') ; for j = 1:number_of_labels label = model.label(j) ; for i = 1:number_of_objects object_name = object_names{i} ; object = getfield(model,'obj',object_names{i}) ; proximity(i) = norm(object.xy - label.xy) ; end [range,index] = min(proximity) ; associated_object = object_names{index} ; object = getfield(model,'obj',associated_object) ; switch object.class case {'0','1'}, mttAssert(~label.is_inline,... ['Inline ports not valid for "0" and "1" junctions']) ; end next = 1 + mttGetFieldLength(object,'interface') ; object = setfield(object,'interface',{next},'name',label.name) ; object = setfield(object,'interface',{next},'class',[]) ; object = setfield(object,'interface',{next},'is_inline',label.is_inline) ; object = setfield(object,'interface',{next},'xy',label.xy) ; object = setfield(object,'interface',{next},'in',[]) ; object = setfield(object,'interface',{next},'out',[]) ; object = setfield(object,'interface',{next},'map',[]) ; model = setfield(model,'obj',associated_object,object) ; end for j = 1:number_of_objects object_name = object_names{j} ; object = getfield(model,'obj',object_name) ; number_of_attached_bonds = mttGetFieldLength(object,'bond') ; number_of_interfaces = mttGetFieldLength(object,'interface') ; for k = 1:number_of_interfaces interface = object.interface(k) ; inbond_proximity = [] ; inbond_counter = [] ; outbond_proximity = [] ; outbond_counter = [] ; in_counter = 0 ; out_counter = 0 ; for i = 1:number_of_attached_bonds bond_number = object.bond(i).number ; bond = model.bond(bond_number) ; if object.bond(i).is_inbond if isempty(bond.to.interface) in_counter = in_counter + 1 ; inbond_proximity(in_counter) = norm(interface.xy - bond.xy2) ; inbond_counter(in_counter) = bond_number ; end else if isempty(bond.from.interface) out_counter = out_counter + 1 ; outbond_proximity(out_counter) = norm(interface.xy - bond.xy1) ; outbond_counter(out_counter) = bond_number ; end end end [in_range,inbond_index] = min(inbond_proximity) ; [out_range,outbond_index] = min(outbond_proximity) ; inbond = inbond_counter(inbond_index) ; outbond = outbond_counter(outbond_index) ; if interface.is_inline mttAssert(~isempty(inbond_proximity),... ['No in-bond for interface "',object_name,'[',interface.name,']"']) ; mttAssert(~isempty(outbond_proximity),... ['No out-bond for interface "',object_name,'[',interface.name,']"']) ; interface.in = inbond ; interface.out = outbond ; model = setfield(model,'bond',{outbond},'from','interface',k) ; model = setfield(model,'bond',{inbond},'to','interface',k) ; else mttAssert(~(isempty(inbond_proximity) & isempty(outbond_proximity)),... ['No bond for interface "',object_name,'[',interface.name,']"']) ; if isempty(inbond_proximity) interface.out = outbond ; model = setfield(model,'bond',{outbond},'from','interface',k) ; elseif isempty(outbond_proximity) interface.in = inbond ; model = setfield(model,'bond',{inbond},'to','interface',k) ; else mttAssert(in_range~=out_range,... ['Ambiguous interface "',object_name,'[',interface.name,']"']) ; if out_range<in_range interface.out = outbond ; model = setfield(model,'bond',{outbond},'from','interface',k) ; else interface.in = inbond ; model = setfield(model,'bond',{inbond},'to','interface',k) ; end end end object.interface(k) = interface ; end model = setfield(model,'obj',object_name,object) ; end for i = 1:number_of_objects object_name = object_names{i} ; object = getfield(model,'obj',object_name) ; number_of_interfaces = mttGetFieldLength(object,'interface') ; for k = 1:number_of_interfaces; interface = object.interface(k) ; if interface.is_inline mttAssert(~(isempty(interface.in) | isempty(interface.out)),... ['Unbound interface: ',object_name,'[',interface.name,']']) ; else mttAssert(~(isempty(interface.in) & isempty(interface.out)),... ['Unbound interface: ',object_name,'[',interface.name,']']) ; end end end objects_with_in = [] ; objects_with_out = [] ; for j = 1:number_of_bonds bond = model.bond(j) ; if isempty(bond.from.interface) object_name = bond.from.obj ; object = getfield(model,'obj',object_name) ; if ~ismember(object.class,{'0','1'}) mttAssert(~ismember(object_name,objects_with_out),... ['Object "',object_name,'" has more than one implicit out-bond']) ; if isempty(objects_with_out) objects_with_out = {object_name} ; else objects_with_out = [objects_with_out,{object_name}] ; end end next = 1 + mttGetFieldLength(object,'interface') ; model = setfield(model,'obj',object_name,'interface',{next},'name','out') ; model = setfield(model,'obj',object_name,'interface',{next},'in',[]) ; model = setfield(model,'obj',object_name,'interface',{next},'out',j) ; model = setfield(model,'bond',{j},'from','interface',next) ; end if isempty(bond.to.interface) object_name = bond.to.obj ; object = getfield(model,'obj',object_name) ; if ~ismember(object.class,{'0','1'}) mttAssert(~ismember(object_name,objects_with_in),... ['Object "',object_name,'" has more than one implicit in-bond']) ; if isempty(objects_with_in) objects_with_in = {object_name} ; else objects_with_in = [objects_with_in,{object_name}] ; end end next = 1 + mttGetFieldLength(object,'interface') ; model = setfield(model,'obj',object_name,'interface',{next},'name','in') ; model = setfield(model,'obj',object_name,'interface',{next},'in',j) ; model = setfield(model,'obj',object_name,'interface',{next},'out',[]) ; model = setfield(model,'bond',{j},'to','interface',next) ; end end function model = associate_external_domains(model) object_names = mttGetFieldNames(model,'obj') ; number_of_objects = length(object_names) ; for i = 1:number_of_objects object_name = object_names{i} ; object = getfield(model,'obj',object_name) ; switch object.class case {'SS','Se','Sf','De','Df'}, mttAssert(mttGetFieldLength(object,'interface')==1,... ['Object "',object_name,'" must have a unique bond interface']) ; bond_number = [] ; if ~isempty(object.interface.in) bond_number = object.interface.in ; end if ~isempty(object.interface.out) bond_number = object.interface.out ; end [model,ok] = mttUpdateBondDomain(model,bond_number,object.domain,object.domain_item) ; mttAssert(ok,['Domain conflict on bond connected to object "',object_name,'"']) ; end end function model = tidy_up(model) % remove temperory data and xfig geometry from model structure object_names = mttGetFieldNames(model,'obj') ; number_of_objects = length(object_names) ; for i = 1:number_of_objects object_name = object_names{i} ; object = getfield(model,'obj',object_name) ; object = mttDeleteField(object,'bond') ; object_interfaces = getfield(object,'interface') ; object_interfaces = mttDeleteField(object_interfaces,'is_inline') ; object_interfaces = mttDeleteField(object_interfaces,'xy') ; object = setfield(object,'interface',object_interfaces) ; object = mttDeleteField(object,'xy') ; model = setfield(model,'obj',object_name,object) ; end all_bonds = getfield(model,'bond') ; all_bonds = mttDeleteField(all_bonds,'xy1') ; all_bonds = mttDeleteField(all_bonds,'xy2') ; all_bonds = mttDeleteField(all_bonds,'v1') ; all_bonds = mttDeleteField(all_bonds,'v2') ; all_bonds = mttDeleteField(all_bonds,'harpoon') ; all_bonds = mttDeleteField(all_bonds,'harpoon_side') ; model = setfield(model,'bond',all_bonds) ; model = mttDeleteField(model,'anonymous') ; model = mttDeleteField(model,'line') ; model = mttDeleteField(model,'label') ;