0001 function [mdl] = fix_model(mdl,opt)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042 if ischar(mdl) && strcmp(mdl,'UNIT_TEST'); do_unit_test; return; end
0043
0044 if ischar(mdl) && strcmp(mdl,'options');
0045 if nargin < 2, opt = false; end
0046 mdl = list_options(opt);
0047 return
0048 end
0049 doall = false;
0050 if nargin > 1
0051 opt = fix_options(mdl,opt);
0052 else
0053 doall = true; opt=struct;
0054 end
0055
0056
0057 tmp.nodes = mdl.nodes;
0058 tmp.elems = mdl.elems;
0059 tmp.type = mdl.type;
0060 try, tmp.boundary = mdl.boundary; end
0061 try, tmp.faces = mdl.faces; end
0062
0063 copt.cache_obj = {tmp, doall, opt};
0064 copt.fstr = 'fix_model';
0065
0066 tmp = eidors_cache( @do_fix_model, {tmp, doall, opt}, copt);
0067
0068
0069 flds = fieldnames(tmp); flds(1:3) = [];
0070 for i = 1:numel(flds)
0071 mdl.(flds{i}) = tmp.(flds{i});
0072 end
0073
0074
0075 function fmdl = remove_unused_nodes( fmdl );
0076 usednodes = unique(fmdl.elems(:));
0077 if max(usednodes) > num_nodes(fmdl)
0078 error('more nodes are used than exist');
0079 end
0080 nidx = zeros(num_nodes(fmdl),1);
0081 nidx(usednodes) = 1:length(usednodes);
0082 fmdl.nodes(nidx==0,:) = [];
0083 fmdl.elems = reshape(nidx(fmdl.elems),[],size(fmdl.elems,2));
0084
0085 for i=1:length(fmdl.electrode)
0086 fmdl.electrode(i).nodes = nidx( ...
0087 fmdl.electrode(i).nodes);
0088 end
0089 fmdl.boundary = find_boundary(fmdl);
0090 fmdl.gnd_node = usednodes(fmdl.gnd_node);
0091
0092
0093
0094 function mdl = do_fix_model(mdl, doall, opt)
0095
0096 if doall || opt.boundary
0097 if ~isfield(mdl,'boundary')
0098 mdl.boundary = find_boundary(mdl);
0099 end
0100 end
0101 if doall || opt.faces
0102 if elem_dim(mdl) == mdl_dim(mdl);
0103 [mdl.faces mdl.elem2face] = calc_faces(mdl);
0104 else
0105
0106 mdl.faces = sort(mdl.elems,2);
0107 mdl.elem2face = (1:length(mdl.faces))';
0108 end
0109 end
0110 if doall || opt.face2elem
0111 mdl.face2elem = calc_face2elem(mdl.elem2face);
0112 end
0113 if doall || opt.boundary_face
0114 mdl.boundary_face = mdl.face2elem(:,2)==0;
0115 end
0116 if doall || opt.elem_centre
0117 mdl.elem_centre = interp_mesh(mdl, 0);
0118 end
0119 if doall || opt.face_centre
0120 tmp = mdl;
0121 tmp.elems = tmp.faces;
0122 mdl.face_centre = interp_mesh(tmp,0);
0123 end
0124 if doall || opt.max_edge_len
0125 mdl.max_edge_len = calc_longest_edge(mdl.elems,mdl.nodes);
0126 end
0127 if doall || opt.elem_volume
0128 mdl.elem_volume = get_elem_volume(mdl);
0129 end
0130 if doall || opt.face_area
0131 if elem_dim(mdl) == 2
0132 mdl.face_area = get_elem_volume(mdl);
0133 else
0134 mdl.face_area = calc_face_area(mdl);
0135 end
0136 end
0137 el_dim = elem_dim(mdl);
0138 if doall || opt.edges
0139 if mdl_dim(mdl)==3
0140 [mdl.edges mdl.elem2edge] = calc_faces(mdl,2);
0141 else
0142 mdl.edges = mdl.faces;
0143 mdl.elem2edge = mdl.elem2face;
0144 end
0145 end
0146 if doall || opt.node2elem
0147 mdl.node2elem = calc_edge2elem(mdl.elems,size(mdl.nodes,1));
0148 end
0149 if doall || opt.edge2elem
0150 mdl.edge2elem = calc_edge2elem(mdl.elem2edge, size(mdl.edges,1));
0151 end
0152
0153 if doall || opt.face2edge
0154 if el_dim <3
0155 mdl.face2edge = mdl.elem2edge;
0156 else
0157 mdl.face2edge = uint32(calc_face2edge(mdl));
0158 end
0159 end
0160
0161 if doall || opt.edge_length
0162 mdl.edge_length = calc_edge_length(mdl);
0163 end
0164
0165 if doall || opt.linear_reorder
0166 mdl = linear_reorder(mdl);
0167 end
0168 if doall || opt.normals
0169 mdl.normals = calc_normals(mdl);
0170 end
0171 if doall || opt.inner_normal
0172 if mdl_dim(mdl) == elem_dim(mdl);
0173 mdl.inner_normal = test_inner_normal( mdl );
0174 else
0175 eidors_msg('@@@ Inner normal test for surface meshes not implemented.',1);
0176 end
0177 end
0178
0179
0180 mdl.elems = uint32(mdl.elems);
0181 if doall || opt.faces
0182 mdl.faces = uint32(mdl.faces);
0183 mdl.elem2face = uint32(mdl.elem2face);
0184 end
0185 if doall || opt.face2elem
0186 mdl.face2elem = uint32(mdl.face2elem);
0187 end
0188 if doall || opt.edges
0189 mdl.edges = uint32(mdl.edges);
0190 mdl.elem2edge = uint32(mdl.elem2edge);
0191 end
0192 if doall || opt.edge2elem
0193 mdl.edge2elem = logical(mdl.edge2elem);
0194 end
0195
0196
0197
0198 function inner_normal = test_inner_normal( mdl )
0199 inner_normal = false(size(mdl.elem2face));
0200 d = elem_dim(mdl) + 1;
0201 for i=1:num_elems(mdl);
0202 el_faces = mdl.elem2face(i,:);
0203 el_ctr = repmat( mdl.elem_centre(i,:), d, 1);
0204 vec_fa_el= el_ctr - mdl.face_centre(el_faces,:);
0205 normal_i = mdl.normals(el_faces,:);
0206 dot_prod = sum( normal_i.*vec_fa_el, 2 );
0207 inner_normal(i,:) = dot_prod' > 0;
0208 end
0209
0210 function [faces, elem2face] = calc_faces(mdl, facedim)
0211
0212 e_dim = elem_dim(mdl);
0213 if nargin == 1
0214 facedim = e_dim;
0215 end
0216
0217 idx = nchoosek(1:e_dim+1, facedim);
0218 elem_sorted = sort(mdl.elems,2);
0219 [faces ib ia] = unique(reshape(elem_sorted(:,idx),[],facedim),'rows');
0220 elem2face = reshape(ia,[],size(idx,1));
0221
0222 function edge2elem = calc_edge2elem(elem2edge,n_edges)
0223
0224 [n_elems, el_faces] = size(elem2edge);
0225 elem2edgeno = (1:n_elems)'*ones(1,el_faces);
0226 elem2edgeno = elem2edgeno(:);
0227 elem2edge = elem2edge(:);
0228 edge2elem = sparse(elem2edge,elem2edgeno,ones(size(elem2edgeno)),n_edges,n_elems);
0229
0230 function f2e = calc_face2edge(mdl)
0231
0232 nf = length(mdl.faces);
0233 list(1:3:3*nf,:) = mdl.faces(:,1:2);
0234 list(2:3:3*nf,:) = mdl.faces(:,[1 3]);
0235 list(3:3:3*nf,:) = mdl.faces(:,2:3);
0236 [jnk f2e] = ismember(list, mdl.edges, 'rows');
0237 f2e = reshape(f2e,3,[])';
0238
0239 function face2elem = calc_face2elem(elem2face)
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249 [n_elems, el_faces] = size(elem2face);
0250 elem2faceno = (1:n_elems)'*ones(1,el_faces);
0251 elem2faceno = elem2faceno(:);
0252 elem2face = elem2face(:);
0253 face2elem(elem2face,2) = elem2faceno;
0254
0255 elem2faceno = flipud(elem2faceno);
0256 elem2face = flipud(elem2face);
0257 face2elem(elem2face,1) = elem2faceno;
0258
0259 face2elem( face2elem(:,1) == face2elem(:,2), 2) = 0;
0260
0261
0262 function elem2face = calc_elem2face(face2elem, face_per_elem)
0263 n_elem = max(face2elem(:));
0264 elem2face = zeros(n_elem,face_per_elem);
0265 for i = 1:n_elem
0266 [f jnk] = find(face2elem==i);
0267 elem2face(i,:) = f;
0268 end
0269
0270 function normals = calc_normals(mdl)
0271 [n_faces face_dim] = size(mdl.faces);
0272 switch face_dim
0273 case 2
0274 A = mdl.nodes(mdl.faces(:,1),:);
0275 B = mdl.nodes(mdl.faces(:,2),:);
0276 normals = (B-A)*[0 1; -1 0];
0277 case 3
0278
0279 x1 = mdl.nodes(mdl.faces(:,2),1) - mdl.nodes(mdl.faces(:,1),1);
0280 y1 = mdl.nodes(mdl.faces(:,2),2) - mdl.nodes(mdl.faces(:,1),2);
0281 z1 = mdl.nodes(mdl.faces(:,2),3) - mdl.nodes(mdl.faces(:,1),3);
0282 x2 = mdl.nodes(mdl.faces(:,3),1) - mdl.nodes(mdl.faces(:,1),1);
0283 y2 = mdl.nodes(mdl.faces(:,3),2) - mdl.nodes(mdl.faces(:,1),2);
0284 z2 = mdl.nodes(mdl.faces(:,3),3) - mdl.nodes(mdl.faces(:,1),3);
0285
0286 normals = zeros(n_faces,3);
0287 normals(:,1) = y1.*z2 - z1.*y2;
0288 normals(:,2) = z1.*x2 - x1.*z2;
0289 normals(:,3) = x1.*y2 - y1.*x2;
0290 otherwise;
0291 error('not 2D or 3D')
0292 end
0293 normals = normals./ repmat(sqrt(sum(normals.^2,2))',face_dim,1)';
0294
0295 function A = calc_face_area(mdl)
0296 A = mdl.nodes(mdl.faces(:,2),:) - mdl.nodes(mdl.faces(:,1),:);
0297 B = mdl.nodes(mdl.faces(:,3),:) - mdl.nodes(mdl.faces(:,1),:);
0298 A = sqrt(sum(cross3(A,B).^2,2))/2;
0299
0300 function L = calc_edge_length(mdl)
0301 L = sqrt(sum( (mdl.nodes(mdl.edges(:,1),:) ...
0302 - mdl.nodes(mdl.edges(:,2),:) ).^2 ,2 ));
0303
0304 function len = calc_longest_edge(elems,nodes)
0305 [E_num E_dim] = size(elems);
0306
0307 pairs = nchoosek(1:E_dim,2);
0308 len = zeros(E_num,1);
0309 for i = 1:size(pairs,1)
0310 a = nodes(elems(:,pairs(i,1)),:);
0311 b = nodes(elems(:,pairs(i,2)),:);
0312 tmp = sqrt(sum((a-b).^2,2));
0313 len = max(len,tmp);
0314 end
0315
0316 function out = fix_options(mdl, opt)
0317 out = list_options(false);
0318
0319 flds = fieldnames(opt);
0320 for i = 1:length(flds)
0321 try
0322 out.(flds{i}) = opt.(flds{i});
0323 catch
0324 warning(sprintf('Option %s not recognised. Ignoring', flds{i}));
0325 end
0326 end
0327 if out.boundary_face
0328 out.face2elem = true;
0329 end
0330 if out.inner_normal
0331 out.normals = true;
0332 out.elem_centre = true;
0333 out.face_centre = true;
0334 end
0335 if any([ out.boundary_face out.face_centre out.normals]) && ~isfield(mdl,'faces')
0336 out.faces = true;
0337 end
0338 if any([out.face2elem out.elem2face out.face_area])
0339 out.faces = true;
0340 end
0341 if any([out.edge2elem out.elem2edge out.edge_length out.face2edge])
0342 out.edges = true;
0343 end
0344 if out.edges && elem_dim(mdl) < 4
0345 out.faces = true;
0346 end
0347
0348
0349 function out = list_options(val)
0350 nodes = [0 0; 0 1; 1 1; 1 0];
0351 elems = [1 2 3; 1 3 4];
0352 mdl = eidors_obj('fwd_model','square','nodes', nodes, 'elems', elems);
0353 out = fix_model(mdl);
0354 out = rmfield(out,{'elems','nodes','name','type'});
0355 flds = fieldnames(out);
0356 for i = 1:length(flds)
0357 out.(flds{i}) = val;
0358 end
0359 out.linear_reorder = 0;
0360
0361 function do_unit_test
0362
0363 nodes = [0 0; 0 1; 1 1; 1 0];
0364 elems = [1 2 3; 1 3 4];
0365 mdl = eidors_obj('fwd_model','square','nodes', nodes, 'elems', elems);
0366 out = fix_model(mdl);
0367 unit_test_cmp('2d: faces' ,out.faces ,[1,2;1,3;1,4;2,3;3,4]);
0368 unit_test_cmp('2d: elem2face',out.elem2face,[1,2,4;2,3,5]);
0369 unit_test_cmp('2d: face2elem',out.face2elem,[1,0;2,1;2,0;1,0;2,0]);
0370
0371
0372 nodes = [0 0 0; 0 1 0; 1 1 0; 1 0 0;...
0373 0 0 1; 0 1 1; 1 1 1; 1 0 1];
0374 elems = [1 2 3 6; 3 6 7 8; 1 5 6 8; 1 3 4 8; 1 3 6 8];
0375 mdl = eidors_obj('fwd_model','cube','nodes', nodes, 'elems', elems);
0376 out = fix_model(mdl);
0377 unit_test_cmp('3d: faces' ,out.faces(1:4,:), [1,2,3;1,2,6;1,3,4;1,3,6]);
0378 unit_test_cmp('3d: elem2face',out.elem2face, [1,2,4,10; 12,13,14,16;
0379 7,8,9,15; 3,5,6,11; 4,5,9,13]);
0380 unit_test_cmp('3d: face2elem',out.face2elem(1:5,:), [1,0; 1,0; 4,0; 5,1; 4,5]);
0381
0382
0383
0384 opt = fix_model('options',false);
0385 flds = fieldnames(opt);
0386
0387
0388 for i = 1:length(flds)
0389 opt.(flds{i}) = true;
0390 out = fix_model(mdl, opt);
0391 opt.(flds{i}) = false;
0392 end
0393
0394 mdl = mk_common_model('n3r2',[16,2]); mdl= mdl.fwd_model;
0395 out = fix_model(mdl);
0396