Skip to content

Commit bbb070f

Browse files
WPringleKeith Roberts
and
Keith Roberts
authored
Multiscale meshing--mesh resolution transitions (#119)
* Bug fix to dpoly.m * if sum(inside) == 0 then d_l will not exist, but it seems like it sum(inside) is zero then it doesn't use the d_l anyway so can just return then. * use splitting at initial point generation to ensure mesh resolution transitions between largely disparate nests are sufficiently smooth for good mesh qualities. Co-authored-by: Keith Roberts <[email protected]>
1 parent cb5ac5d commit bbb070f

File tree

5 files changed

+105
-51
lines changed

5 files changed

+105
-51
lines changed

@meshgen/meshgen.m

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,30 @@
1616
%
1717
% You should have received a copy of the GNU General Public License
1818
% along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
%
20+
% Available options:
21+
% ef % edgefx class
22+
% bou % geodata class
23+
% h0 % minimum edge length (optional if bou exists)
24+
% bbox % bounding box [xmin,ymin; xmax,ymax] (manual specification, no bou)
25+
% proj % structure containing the m_map projection info
26+
% plot_on % flag to plot (def: 1) or not (0)
27+
% nscreen % how many it to plot and write temp files (def: 5)
28+
% itmax % maximum number of iterations.
29+
% pfix % fixed node positions (nfix x 2 )
30+
% egfix % edge constraints
31+
% outer % meshing boundary (manual specification, no bou)
32+
% inner % island boundaries (manual specification, no bou)
33+
% mainland % the shoreline boundary (manual specification, no bou)
34+
% fixboxes % a flag that indicates which boxes will use fixed constraints
35+
% memory_gb % memory in GB allowed to use for initial rejector
36+
% cleanup % logical flag or string to trigger cleaning of topology (default is on).
37+
% direc_smooth % logical flag to trigger direct smoothing of mesh in the cleanup
38+
% dj_cutoff % the cutoff area fraction for disjoint portions to delete
39+
% qual_tol % tolerance for the accepted negligible change in quality
40+
% enforceWeirs % whether or not to enforce weirs in meshgen
41+
% enforceMin % whether or not to enfore minimum edgelength for all edgefxs
42+
% big_mesh % set to 1 to remove the bou data from memory
1943
properties
2044
fd % handle to distance function
2145
fh % handle to edge function
@@ -30,18 +54,17 @@
3054
bou % geodata class
3155
ef % edgefx class
3256
itmax % maximum number of iterations.
33-
outer % meshing boundary
34-
inner % island boundaries
35-
mainland % the shoreline boundary
57+
outer % meshing boundary (manual specification, no bou)
58+
inner % island boundaries (manual specification, no bou)
59+
mainland % the shoreline boundary (manual specification, no bou)
3660
boubox % the bbox as a polygon 2-tuple
3761
inpoly_flip % used to flip the inpoly test to determine the signed distance.
3862
memory_gb % memory in GB allowed to use for initial rejector
3963
cleanup % logical flag or string to trigger cleaning of topology (default is on).
4064
direc_smooth % logical flag to trigger direct smoothing of mesh in the cleanup
4165
dj_cutoff % the cutoff area fraction for disjoint portions to delete
4266
grd = msh(); % create empty mesh class to return p and t in.
43-
big_mesh
44-
ns_fix % improve spacing for boundary vertices
67+
big_mesh % release bou data from memory
4568
qual % mean, lower 3rd sigma, and the minimum element quality.
4669
qual_tol % tolerance for the accepted negligible change in quality
4770
proj % structure containing the m_map projection info
@@ -99,7 +122,6 @@
99122
addOptional(p,'direc_smooth',1);
100123
addOptional(p,'dj_cutoff',0.25);
101124
addOptional(p,'big_mesh',defval);
102-
addOptional(p,'ns_fix',defval);
103125
addOptional(p,'proj',defval);
104126
addOptional(p,'qual_tol',defval);
105127
addOptional(p,'enforceWeirs',1);
@@ -122,7 +144,7 @@
122144
'plot_on','nscreen','itmax',...
123145
'memory_gb','qual_tol','cleanup',...
124146
'direc_smooth','dj_cutoff',...
125-
'big_mesh','ns_fix','proj'});
147+
'big_mesh','proj'});
126148
% get the fieldnames of the edge functions
127149
fields = fieldnames(inp);
128150
% loop through and determine which args were passed.
@@ -185,7 +207,7 @@
185207
end
186208
if obj.enforceWeirs
187209
for j = 1 : length(obj.bou)
188-
if ~isempty(obj.bou{j}.weirEgfix) && ~isempty(obj.egfix)
210+
if ~isempty(obj.bou{j}.weirEgfix) && ~isempty(obj.egfix)
189211
obj.egfix = [obj.egfix ; obj.bou{j}.weirEgfix+max(obj.egfix(:))];
190212
else
191213
obj.egfix = obj.bou{j}.weirEgfix;
@@ -195,7 +217,6 @@
195217
obj.egfix = renumberEdges(obj.egfix);
196218
case('fixboxes')
197219
obj.fixboxes= inp.(fields{i});
198-
199220
case('bou')
200221
% got it from user arg
201222
if obj.outer~=0, continue; end
@@ -311,8 +332,6 @@
311332
obj.plot_on= inp.(fields{i});
312333
case('big_mesh')
313334
obj.big_mesh = inp.(fields{i});
314-
case('ns_fix')
315-
obj.ns_fix = inp.(fields{i});
316335
case('nscreen')
317336
obj.nscreen= inp.(fields{i});
318337
if obj.nscreen ~=0
@@ -455,13 +474,12 @@
455474
%%
456475
tic
457476
it = 1 ;
458-
imp = 10;
459-
qual_diff = 0;
460477
Re = 6378.137e3;
461478
geps = 1e-3*min(obj.h0)/Re;
462479
deps = sqrt(eps);
463480
ttol=0.1; Fscale = 1.2; deltat = 0.1;
464481
delIT = 0 ; delImp = 2;
482+
imp = 10; % number of iterations to do mesh improvements (delete/add)
465483

466484
% unpack initial points.
467485
p = obj.grd.p;
@@ -546,14 +564,24 @@
546564
p1 = p1(rand(size(p1,1),1) < r0/max_r0,:); % Rejection method
547565
p = [p; p1]; % Adding p1 to p
548566
end
567+
% add new points along boundary of multiscale nests
568+
if box_num < length(obj.h0)
569+
h0_rat = ceil(h0_l/obj.h0(box_num+1));
570+
nsplits = floor(log(h0_rat)/log(2));
571+
for add = 1:nsplits
572+
new_points = split(p,fh_l);
573+
p = [p; new_points];
574+
end
575+
end
549576
end
550577
else
551578
disp('User-supplied initial points!');
552579
obj.grd.b = [];
553-
imp = 10; % number of iterations to do mesh improvements (delete/add)
554580
h0_l = obj.h0(end); % finest h0 (in case of a restart of meshgen.build).
555581
end
556582

583+
584+
557585
% remove pfix/egfix outside of desired subdomain
558586
nfix = size(obj.pfix,1); % Number of fixed points
559587
negfix = size(obj.egfix,1); % Number of edge constraints
@@ -687,8 +715,8 @@
687715
end
688716

689717
% Termination quality, mesh quality reached is copacetic.
718+
qual_diff = mq_l3sig - obj.qual(max(1,it-imp),2);
690719
if ~mod(it,imp)
691-
qual_diff = mq_l3sig - obj.qual(max(1,it-imp),2);
692720
if abs(qual_diff) < obj.qual_tol
693721
% Do the final elimination of small connectivity
694722
[t,p] = delaunay_elim(p,obj.fd,geps,1);
@@ -752,7 +780,7 @@
752780
% Mesh improvements (deleting and addition)
753781
if ~mod(it,imp)
754782
nn = []; pst = [];
755-
if qual_diff < imp*0.01 && qual_diff > 0
783+
if qual_diff < imp*obj.qual_tol && qual_diff > 0
756784
% Remove elements with small connectivity
757785
nn = get_small_connectivity(p,t);
758786
disp(['Deleting ' num2str(length(nn)) ' due to small connectivity'])
@@ -953,35 +981,7 @@
953981

954982

955983
end % end distmesh2d_plus
956-
957-
function obj = nearshorefix(obj)
958-
%% kjr make sure boundaries have good spacing on boundary.
959-
% This is experimentary.
960-
t = obj.grd.t ; p = obj.grd.t;
961-
[bnde, ~] = extdom_edges2(t,p);
962-
[poly] = extdom_polygon(bnde,p,1);
963-
964-
new = [];
965-
for j = 1 : length(poly)
966-
for i = 1 : length(poly{j})-2
967-
pt = poly{j}(i,:) ; % current point
968-
nxt= poly{j}(i+1,:) ; % next point
969-
nxt2 = poly{j}(i+2,:) ; % next next point
970-
971-
dst1 = sqrt( (nxt(:,1)-pt(:,1)).^2 + (nxt(:,2)-pt(:,2)).^2 ); % dist to next point
972-
dst2 = sqrt( (nxt2(:,1)-nxt(:,1)).^2 + (nxt2(:,2)-nxt(:,2)).^2 ); % dist to next next point
973-
974-
if dst2/dst1 > 2
975-
% split bar
976-
q = (nxt2 + nxt)/2;
977-
new = [new; q];
978-
end
979-
end
980-
end
981-
p = [p; new]; % post fix new points (to avoid problems with pfix.)
982-
t = delaunay_elim(p,obj.fd,geps,0); % Delaunay with elimination
983-
obj.grd.t = t ; obj.grd.p = t;
984-
end
984+
985985

986986

987987
end % end methods

@meshgen/private/dpoly.m

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,12 @@
9696
in_outer = ~in_outer;
9797
end
9898
else
99-
in_boubox = d_l*0;
100-
in_outer = d_l*0;
99+
return
101100
end
102101

103102
% d is signed negative if inside and vice versa.
104103
d_l = (-1).^( in_outer & in_boubox).*d_l;
105-
106-
if sum(inside)==0; return; end
107-
104+
108105
d(inside) = d_l;
109106
end
110107
% EOF

@msh/msh.m

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,9 @@ function write(obj,fname,type)
279279
% i) 'numticks': number of colorbar tickmarks and (optional) range:
280280
% [numticks] or [numticks caxis_lower caxis_upper]
281281
% ii) 'fontsize': figure fontsize
282-
% iii) 'holdon' : =1 to plot on existing figure (otherwise
282+
% iii) 'backcolor': figure background RGB color (where mesh
283+
% doesn't exist), default is [1 1 1] => white
284+
% iv) 'holdon' : =1 to plot on existing figure (otherwise
283285
% will use new figure)
284286
if nargin < 2
285287
type = 'tri';
@@ -292,11 +294,14 @@ function write(obj,fname,type)
292294
end
293295
np_g = length(obj.p) ;
294296
fsz = 12; % default font size
297+
bgc = [1 1 1]; % default background color
295298
numticks = 10; % default num of ticks
296299
holdon = 0;
297300
for kk = 1:2:length(varargin)
298301
if strcmp(varargin{kk},'fontsize')
299302
fsz = varargin{kk+1};
303+
elseif strcmp(varargin{kk},'backcolor')
304+
bgc = varargin{kk+1};
300305
elseif strcmp(varargin{kk},'numticks')
301306
numticks = varargin{kk+1};
302307
elseif strcmp(varargin{kk},'holdon')
@@ -790,6 +795,7 @@ function write(obj,fname,type)
790795
end
791796
ax = gca;
792797
ax.FontSize = fsz;
798+
ax.Color = bgc;
793799
if proj == 1
794800
% now add the box
795801
m_grid('FontSize',fsz);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
% Example_10_Multiscale_Smoother:
2+
% An idealized test for multiscale nesting using boxes with a large min_el
3+
% ratio
4+
5+
bbox = [0, 1; 0,1];
6+
boubox = [0,0; 1,0; 1,1; 0,1; 0,0; NaN NaN ];
7+
min_el = 1e3;
8+
9+
gdat1 = geodata('pslg',boubox,'bbox',bbox,'h0',min_el);
10+
fh1 = edgefx('geodata',gdat1,'g',0.2);
11+
12+
bbox2 = [-1, 2; -1,2];
13+
boubox2 = [-1,-1; 2,-1; 2,2; -1,2; -1,-1; NaN NaN ];
14+
min_el2 = min_el*10;
15+
16+
gdat2 = geodata('pslg',boubox2,'bbox',bbox2,'h0',min_el2);
17+
fh2 = edgefx('geodata',gdat2,'g',0.2);
18+
19+
mshopts = meshgen('ef',{fh2, fh1},'bou',{gdat2,gdat1},...
20+
'plot_on',1,'qual_tol',0.0025,'cleanup',0);
21+
mshopts = mshopts.build;
22+
23+
m = mshopts.grd;
24+
25+
plot(m)
26+
27+
m = m.clean;
28+
29+
plot(m)

utilities/split.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
% Function for adding points to an untriangulated set of points based on
2+
% nearest distance versus the desired edgefx
3+
function [new_points]=split(points,fh)
4+
% find the point closest to each point (that isn't the *same* point)
5+
[idx, ~] = ourKNNsearch(points',points',2);
6+
% the ideal spacing between points
7+
ideal_dst = fh(points);
8+
% where the dst is 2*ideal_dist, add a point in between
9+
long = zeros(length(points)*2,1);
10+
lat = zeros(length(points)*2,1);
11+
long(1:2:end) = points(idx(:,1),1);
12+
long(2:2:end) = points(idx(:,2),1);
13+
lat(1:2:end) = points(idx(:,1),2);
14+
lat(2:2:end) = points(idx(:,2),2);
15+
L = m_lldist(long,lat);
16+
L = L(1:2:end)*1e3; % L = lengths in meters
17+
splits = L > 1.5*ideal_dst;
18+
disp(['Number of splits near multiscale nest: ' num2str(sum(splits))])
19+
new_points = (points(idx(splits,1),:) + points(idx(splits,2),:))./2.0;
20+
end
21+
22+

0 commit comments

Comments
 (0)