From 3f954873302b0c88c87cd59143df7cf6948ea43d Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Sat, 10 Feb 2024 21:48:36 -0500 Subject: [PATCH 01/15] 3d adaptive tree, no lr --- @treefun3/coeffs2refvals.m | 23 ++++ @treefun3/leaves.m | 7 ++ @treefun3/plot.m | 54 +++++++++ @treefun3/private/isLeaf.m | 6 + @treefun3/refineBox.m | 97 +++++++++++++++ @treefun3/treefun3.m | 235 +++++++++++++++++++++++++++++++++++++ @treefun3/vals2coeffs.m | 34 ++++++ 7 files changed, 456 insertions(+) create mode 100644 @treefun3/coeffs2refvals.m create mode 100644 @treefun3/leaves.m create mode 100644 @treefun3/plot.m create mode 100644 @treefun3/private/isLeaf.m create mode 100644 @treefun3/refineBox.m create mode 100644 @treefun3/treefun3.m create mode 100644 @treefun3/vals2coeffs.m diff --git a/@treefun3/coeffs2refvals.m b/@treefun3/coeffs2refvals.m new file mode 100644 index 0000000..f7d7702 --- /dev/null +++ b/@treefun3/coeffs2refvals.m @@ -0,0 +1,23 @@ +function vals = coeffs2refvals(coeffs) +% +% + +persistent Eval pstored +p = size(coeffs, 1); +nrefpts = 2*p; + +if ( isempty(Eval) || p ~= pstored ) + pstored = p; + Eval = ones(nrefpts, p); + x = linspace(-1, 1, nrefpts).'; + Eval(:,2) = x; + for k=3:p + Eval(:,k) = 2*x.*Eval(:,k-1)-Eval(:,k-2); + end +end + +tmp1 = permute(tensorprod(Eval,coeffs,2,1),[2 3 1]); +tmp2 = permute(tensorprod(Eval,tmp1,2,1),[2 3 1]); +vals = permute(tensorprod(Eval,tmp2,2,1),[2 3 1]); + +end \ No newline at end of file diff --git a/@treefun3/leaves.m b/@treefun3/leaves.m new file mode 100644 index 0000000..ae0cc4c --- /dev/null +++ b/@treefun3/leaves.m @@ -0,0 +1,7 @@ +function ids = leaves(f) +%LEAVES Get the leaf IDs in a TREEFUN2. +% LEAVES(F) returns the leaf IDs in the TREEFUN2 F. + +ids = f.id(f.height == 0); + +end diff --git a/@treefun3/plot.m b/@treefun3/plot.m new file mode 100644 index 0000000..0987aca --- /dev/null +++ b/@treefun3/plot.m @@ -0,0 +1,54 @@ +function varargout = plot(f, varargin) +%PLOT Plot a TREEFUN3. +% PLOT(F) gives a 3D plot of the TREEFUN3 F and shows the tree on +% which F is defined. +% +% See also SURF, MESH. + +% func = varargin{1}; + +holdState = ishold(); +nplotpts = 800; + +% Plot the function +hold on +ids = leaves(f); +% for k = 1:length(ids) +% id = ids(k); +% [x, y] = meshgrid(linspace(f.domain(1,id), f.domain(2,id), nplotpts), ... +% linspace(f.domain(3,id), f.domain(4,id), nplotpts)); +% u = coeffs2plotvals(f.coeffs{id}); +% hk = surface(x, y, 0*u, u, 'EdgeAlpha', 1, varargin{:}); +% if ( nargout > 0 ) +% h(k) = hk; %#ok +% end +% end + +[x, y, z] = meshgrid(linspace(f.domain(1), f.domain(2), nplotpts), ... + linspace(f.domain(3), f.domain(4), nplotpts), ... + linspace(f.domain(5), f.domain(6), nplotpts)); +% v = func( x, y, z); + +% Plot the boxes +xdata = [f.domain([1 2 2 1 1 1 2 2 1 1], ids) ; nan(1, length(ids)); ... % bottom & top + f.domain([2 2 2 2 1 1], ids) ; nan(1, length(ids));]; % the rest to complete the box +ydata = [f.domain([3 3 4 4 3 3 3 4 4 3], ids) ; nan(1, length(ids)); ... + f.domain([3 3 4 4 4 4], ids) ; nan(1, length(ids));]; +zdata = [f.domain([5 5 5 5 5 6 6 6 6 6], ids) ; nan(1, length(ids)); ... + f.domain([5 6 6 5 5 6], ids) ; nan(1, length(ids));]; +line('XData', xdata(:), 'YData', ydata(:), 'ZData', zdata(:), 'LineWidth', 1) + +axis equal +xlim(f.domain(1:2, 1)) +ylim(f.domain(3:4, 1)) +zlim(f.domain(5:6, 1)) + +if ( ~holdState ) + hold off +end + +if ( nargout > 0 ) + varargout = {h}; +end + +end diff --git a/@treefun3/private/isLeaf.m b/@treefun3/private/isLeaf.m new file mode 100644 index 0000000..044e4c9 --- /dev/null +++ b/@treefun3/private/isLeaf.m @@ -0,0 +1,6 @@ +function out = isLeaf(f, id) +%ISLEAF Is this box a leaf? + +out = ( f.height(id) == 0 ); + +end diff --git a/@treefun3/refineBox.m b/@treefun3/refineBox.m new file mode 100644 index 0000000..11deb97 --- /dev/null +++ b/@treefun3/refineBox.m @@ -0,0 +1,97 @@ +function f = refineBox(f, id) + +% Split into four child boxes +dom = f.domain(:,id); +xmid = mean(dom(1:2)); +ymid = mean(dom(3:4)); +zmid = mean(dom(5:6)); + +cid1 = length(f.id)+1; +f.domain(:,cid1) = [dom(1) xmid dom(3) ymid dom(5) zmid]; +f.id(cid1) = cid1; +f.parent(cid1) = id; +f.children(:,cid1) = 0; +f.level(cid1) = f.level(id)+1; +f.height(cid1) = 0; +f.coeffs{cid1} = []; +% f.col(cid1) = 2*f.col(id); +% f.row(cid1) = 2*f.row(id); +% f.morton(cid1) = cartesian2morton(f.col(cid1), f.row(cid1)); + +cid2 = length(f.id)+1; +f.domain(:,cid2) = [xmid dom(2) dom(3) ymid dom(5) zmid]; +f.id(cid2) = cid2; +f.parent(cid2) = id; +f.children(:,cid2) = 0; +f.level(cid2) = f.level(id)+1; +f.height(cid2) = 0; +f.coeffs{cid2} = []; +% f.col(cid2) = 2*f.col(id) + 1; +% f.row(cid2) = 2*f.row(id); +% f.morton(cid2) = cartesian2morton(f.col(cid2), f.row(cid2)); + +cid3 = length(f.id)+1; +f.domain(:,cid3) = [dom(1) xmid ymid dom(4) dom(5) zmid]; +f.id(cid3) = cid3; +f.parent(cid3) = id; +f.children(:,cid3) = 0; +f.level(cid3) = f.level(id)+1; +f.height(cid3) = 0; +f.coeffs{cid3} = []; +% f.col(cid3) = 2*f.col(id); +% f.row(cid3) = 2*f.row(id) + 1; +% f.morton(cid3) = cartesian2morton(f.col(cid3), f.row(cid3)); + +cid4 = length(f.id)+1; +f.domain(:,cid4) = [xmid dom(2) ymid dom(4) dom(5) zmid]; +f.id(cid4) = cid4; +f.parent(cid4) = id; +f.children(:,cid4) = 0; +f.level(cid4) = f.level(id)+1; +f.height(cid4) = 0; +f.coeffs{cid4} = []; +% f.col(cid4) = 2*f.col(id) + 1; +% f.row(cid4) = 2*f.row(id) + 1; +% f.morton(cid4) = cartesian2morton(f.col(cid4), f.row(cid4)); + +cid5 = length(f.id)+1; +f.domain(:,cid5) = [dom(1) xmid dom(3) ymid zmid dom(6)]; +f.id(cid5) = cid5; +f.parent(cid5) = id; +f.children(:,cid5) = 0; +f.level(cid5) = f.level(id)+1; +f.height(cid5) = 0; +f.coeffs{cid5} = []; + +cid6 = length(f.id)+1; +f.domain(:,cid6) = [xmid dom(2) dom(3) ymid zmid dom(6)]; +f.id(cid6) = cid6; +f.parent(cid6) = id; +f.children(:,cid6) = 0; +f.level(cid6) = f.level(id)+1; +f.height(cid6) = 0; +f.coeffs{cid6} = []; + +cid7 = length(f.id)+1; +f.domain(:,cid7) = [dom(1) xmid ymid dom(4) zmid dom(6)]; +f.id(cid7) = cid7; +f.parent(cid7) = id; +f.children(:,cid7) = 0; +f.level(cid7) = f.level(id)+1; +f.height(cid7) = 0; +f.coeffs{cid7} = []; + +cid8 = length(f.id)+1; +f.domain(:,cid8) = [xmid dom(2) ymid dom(4) zmid dom(6)]; +f.id(cid8) = cid8; +f.parent(cid8) = id; +f.children(:,cid8) = 0; +f.level(cid8) = f.level(id)+1; +f.height(cid8) = 0; +f.coeffs{cid8} = []; + +f.children(:,id) = [cid1 cid2 cid3 cid4 cid5 cid6 cid7 cid8]; +f.height(id) = 1; +f.coeffs{id} = []; + +end diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m new file mode 100644 index 0000000..24f97aa --- /dev/null +++ b/@treefun3/treefun3.m @@ -0,0 +1,235 @@ +classdef treefun3 %#ok<*PROP,*PROPLC> +%TREEFUN3 Piecewise polynomial on an adaptive quadtree. +% TREEFUN3(F) constructs a TREEFUN3 object representing the function F on +% the domain [-1, 1] x [-1, 1] x [-1, 1]. F may be a function handle. +% A TREEFUN3 is constructed by recursively subdividing the domain until +% each piece is well approximated by a trivariate polynomial of degree +% (N-1) x (N-1). The default is N = 16. +% +% TREEFUN3(F, N) uses piecewise polynomials of degree (N-1) x (N-1). +% +% TREEFUN3(F, [A B C D E F]) specifies a domain [A, B] x [C, D] x [E, F] +% on which the TREEFUN3 is defined. +% +% TREEFUN3(F, [A B C D E F], N) specifies both a degree and a domain. +% +% TREEFUN3(F, TF) creates a TREEFUN3 approximation to F using the tree +% structure from a previously-defined TREEFUN3. No adaptive construction +% takes place; the function F is simply initialized on the grid inherited +% from TF. +% +% not all preperties are used ... + + properties + + n = 16 + domain + level + height + id + parent + children + coeffs + col + row + morton + flatNeighbors + leafNeighbors + + end + + methods + + function f = treefun3(varargin) + + if ( nargin < 1 ) + return + end + + dom = [-1 1 -1 1 -1 1]; + opts = struct(); + opts.balance = false; % not yet + opts.neighbors = false; + + if ( nargin == 2 ) + if ( isa(varargin{2}, 'treefun3') ) % TREEFUN3(F, TF) + % We were given the tree structure + f = varargin{2}; + func = varargin{1}; + if ( isnumeric(func) && isscalar(func) ) + func = @(x,y) func + 0*x; + elseif ( isa(func, 'chebfun3') ) + func = @(x,y) feval(func, x, y); + end + % We just need to fill in the leaf coefficients + L = leaves(f); + [xx0, yy0] = chebpts2(f.n, f.n, [0 1 0 1]); + for id = L(:).' + dom = f.domain(:,id); + sclx = diff(dom(1:2)); + scly = diff(dom(3:4)); + xx = sclx*xx0 + dom(1); + yy = scly*yy0 + dom(3); + vals = func(xx,yy); + f.coeffs{id} = treefun3.vals2coeffs(vals); + end + return + elseif ( isscalar(varargin{2}) ) % TREEFUN3(F, N) + f.n = varargin{2}; + else + dom = varargin{2}; % TREEFUN3(F, [A B C D E F]) + end + elseif ( nargin == 3 ) + dom = varargin{2}; % TREEFUN3(F, [A B C D E F], N) + f.n = varargin{3}; + elseif ( nargin == 4 ) + dom = varargin{2}; % TREEFUN3(F, [A B C D E F], N, OPTS) + f.n = varargin{3}; + opts1 = varargin{4}; + if ( isfield(opts1, 'balance') ) + opts.balance = opts1.balance; + end + if ( isfield(opts1, 'neighbors') ) + opts.neighbors = opts1.neighbors; + end + elseif ( nargin == 9 ) + % TREEFUN3(DOMAIN, LEVEL, HEIGHT, ID, PARENT, CHILDREN, + % COEFFS, COL, ROW) + [f.domain, f.level, f.height, f.id, f.parent, ... + f.children, f.coeffs, f.col, f.row] = deal(varargin{:}); + f.morton = cartesian2morton(f.col, f.row); + f.n = size(f.coeffs{end}, 1); + f = balance(f); + [f.flatNeighbors, f.leafNeighbors] = generateNeighbors(f); + return + end + + func = varargin{1}; + if ( isnumeric(func) && isscalar(func) ) + func = @(x,y) func + 0*x; + elseif ( isa(func, 'chebfun3') ) + func = @(x,y) feval(func, x, y); + end + + f.domain(:,1) = dom(:); + f.level(1) = 0; + f.height(1) = 0; + f.id(1) = 1; + f.parent(1) = 0; + f.children(:,1) = zeros(8, 1); + f.coeffs{1} = []; + f.col = uint64(0); + f.row = uint64(0); + + % f = buildDepthFirst(f, func); + f = buildBreadthFirst(f, func); + % f.morton = cartesian2morton(f.col, f.row); + + % Now do level restriction + opts.balance = false; + if ( opts.balance ) + f = balance(f); + else + % Do a cumulative sum in reverse to correct the heights + for k = length(f.id):-1:1 + if ( ~isLeaf(f, k) ) + f.height(k) = 1 + max(f.height(f.children(:,k))); + end + end + end + + if ( opts.neighbors ) + [f.flatNeighbors, f.leafNeighbors] = generateNeighbors(f); + end + + end + + end + + methods ( Access = private ) + + f = refineBox(f, id); + + function f = buildBreadthFirst(f, func) + + % Note: the length changes at each iteration here + id = 1; + while ( id <= length(f.id) ) + [resolved, coeffs] = isResolved(func, f.domain(:,id), f.n); + if ( resolved ) + f.coeffs{id} = coeffs; + f.height(id) = 0; + else + % Split into four child boxes + f = refineBox(f, id); + f.height(id) = 1; + end + id = id + 1; + end + + % Do a cumulative sum in reverse to correct the heights + for k = length(f.id):-1:1 + if ( ~isLeaf(f, k) ) + %f.height(k) = f.height(k) + max(f.height(f.children(:,k))); + f.height(k) = 1 + max(f.height(f.children(:,k))); + end + end + + end + + end + + methods ( Static ) + + % u = poisson(f, isource); + coeffs = vals2coeffs(vals); + vals = coeffs2vals(coeffs); + % vals = coeffs2refvals(coeffs); + % refvals = chebvals2refvals(chebvals); + + end + + +end + +function [resolved, coeffs] = isResolved(f, dom, n) + +persistent xx0 yy0 zz0 xxx0 yyy0 zzz0 nstored + +tol = 1e-12; +nalias = n; +nrefpts = 2*n; % Sample at equispaced points to test error + +if ( isempty(xx0) || isempty(xxx0) || n ~= nstored ) + nstored = n; + x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; + [xx0, yy0, zz0] = ndgrid(x0); + [xxx0, yyy0, zzz0] = ndgrid(linspace(0, 1, nrefpts)); +end +sclx = diff(dom(1:2)); +scly = diff(dom(3:4)); +sclz = diff(dom(5:6)); +xx = sclx*xx0 + dom(1); xxx = sclx*xxx0 + dom(1); +yy = scly*yy0 + dom(3); yyy = scly*yyy0 + dom(3); +zz = sclz*zz0 + dom(5); zzz = sclz*zzz0 + dom(5); + +vals = f(xx,yy,zz); +coeffs = treefun3.vals2coeffs(vals); +coeffs = coeffs(1:n,1:n); +Ex = sum(abs(coeffs(end-1:end,:)), 'all') / (2*n); +Ey = sum(abs(coeffs(:,end-1:end)), 'all') / (2*n); +err_cfs = (Ex + Ey) / 2; + +% F = f(xxx,yyy,zzz); +% G = coeffs2refvals(coeffs); +% err_vals = max(abs(F(:) - G(:))); + +err = err_cfs; +%err = min(err_cfs, err_vals); +h = sclx; +eta = 0; + +vmax = max(abs(vals(:))); +resolved = ( err * h^eta < tol * max(vmax, 1) ); + +end diff --git a/@treefun3/vals2coeffs.m b/@treefun3/vals2coeffs.m new file mode 100644 index 0000000..094e0ae --- /dev/null +++ b/@treefun3/vals2coeffs.m @@ -0,0 +1,34 @@ +function coeffs = vals2coeffs(vals) +%VALS2COEFFS Convert values at Chebyshev points to Chebyshev +% coefficients. +% +% See also COEFFS2VALS. + +% Store the vals2coeffs matrices for sizes < cutoff +persistent F +cutoff = 1e+03; % ... later + +% Get the length of the input: +p = size(vals, 1); + +if ( p <= 1 ) + % Trivial case (constant): + coeffs = vals; +elseif ( p < cutoff ) + % Use matrix multiplication for small problems + if ( isempty(F) ) + F = cell(cutoff, 1); + end + if ( isempty(F{p}) ) + F{p} = 2*cos(pi*((1:p)-1)'*(2*(p:-1:1)-1)/(2*p))/p; + F{p}(1,:) = 1/2*F{p}(1,:); + end + tmp1hat = permute(tensorprod(F{p},vals,2,1),[2 3 1]); + tmp2hat = permute(tensorprod(F{p},tmp1hat,2,1),[2 3 1]); + coeffs = permute(tensorprod(F{p},tmp2hat,2,1),[2 3 1]); +else + % Use fast transform ... not yet + % coeffs = chebtech2.vals2coeffs( chebtech2.vals2coeffs(vals).' ).'; +end + +end From 2374be08223bf34333c65491d1663067d182385c Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Sat, 10 Feb 2024 22:46:15 -0500 Subject: [PATCH 02/15] update tree plot routine --- @treefun3/coeffs2checkvals.m | 28 +++ @treefun3/plot.m | 238 ++++++++++++++++++++- @treefun3/private/openFigInCurrentFigure.m | 35 +++ @treefun3/slice.fig | Bin 0 -> 233901 bytes 4 files changed, 292 insertions(+), 9 deletions(-) create mode 100644 @treefun3/coeffs2checkvals.m create mode 100644 @treefun3/private/openFigInCurrentFigure.m create mode 100644 @treefun3/slice.fig diff --git a/@treefun3/coeffs2checkvals.m b/@treefun3/coeffs2checkvals.m new file mode 100644 index 0000000..0094156 --- /dev/null +++ b/@treefun3/coeffs2checkvals.m @@ -0,0 +1,28 @@ +function vals = coeffs2checkvals(coeffs,x,y,z) +% +% + +persistent Evalx Evaly Evalz +p = size(coeffs, 1); +ncheckpts = numel(x); % hopefully not too large... + +Evalx = ones(ncheckpts, p); +Evaly = ones(ncheckpts, p); +Evalz = ones(ncheckpts, p); +Evalx(:,2) = x(:); +Evaly(:,2) = y(:); +Evalz(:,2) = z(:); +for k=3:p + Evalx(:,k) = 2*x(:).*Evalx(:,k-1)-Evalx(:,k-2); + Evaly(:,k) = 2*y(:).*Evaly(:,k-1)-Evaly(:,k-2); + Evalz(:,k) = 2*z(:).*Evalz(:,k-1)-Evalz(:,k-2); +end +% coeffs to value map +vals = zeros(ncheckpts,1); +for k=1:ncheckpts + tmp1 = permute(tensorprod(Evalx(k,:),coeffs,2,1),[2 3 1]); + tmp2 = permute(tensorprod(Evaly(k,:),tmp1,2,1),[2 3 1]); + vals(k) = permute(tensorprod(Evalz(k,:),tmp2,2,1),[2 3 1]); +end + +end \ No newline at end of file diff --git a/@treefun3/plot.m b/@treefun3/plot.m index 0987aca..3d459cc 100644 --- a/@treefun3/plot.m +++ b/@treefun3/plot.m @@ -5,14 +5,14 @@ % % See also SURF, MESH. -% func = varargin{1}; +func = varargin{1}; holdState = ishold(); -nplotpts = 800; +nplotpts = 51; + +h = instantiateSlice3GUI(); +handles = guihandles(h); -% Plot the function -hold on -ids = leaves(f); % for k = 1:length(ids) % id = ids(k); % [x, y] = meshgrid(linspace(f.domain(1,id), f.domain(2,id), nplotpts), ... @@ -24,12 +24,61 @@ % end % end -[x, y, z] = meshgrid(linspace(f.domain(1), f.domain(2), nplotpts), ... - linspace(f.domain(3), f.domain(4), nplotpts), ... - linspace(f.domain(5), f.domain(6), nplotpts)); -% v = func( x, y, z); +[xx, yy, zz] = meshgrid(linspace(f.domain(1), f.domain(2), nplotpts), ... + linspace(f.domain(3), f.domain(4), nplotpts), ... + linspace(f.domain(5), f.domain(6), nplotpts)); +v = func( xx, yy, zz); +if isreal(v) + [row,col,tube] = ind2sub(size(v), find(v(:) == max(v(:)), 1, 'last')); +else + [row, col, tube] = ind2sub(size(v), find(abs(v(:)) == max(abs(v(:))), 1, 'last')); +end +xslice = xx(row,col,tube); +yslice = yy(row,col,tube); +zslice = zz(row,col,tube); + +set(handles.xSlider, 'Min', f.domain(1)); +set(handles.xSlider, 'Max', f.domain(2)); +set(handles.xSlider, 'Value', xslice); + +set(handles.ySlider, 'Min', f.domain(3)); +set(handles.ySlider, 'Max', f.domain(4)); +set(handles.ySlider, 'Value', yslice); + +set(handles.zSlider, 'Min', f.domain(5)); +set(handles.zSlider, 'Max', f.domain(6)); +set(handles.zSlider, 'Value', zslice); + +nSteps = 15; % number of slices allowed +set(handles.xSlider, 'SliderStep', [1/nSteps , 1 ]); +set(handles.ySlider, 'SliderStep', [1/nSteps , 1 ]); +set(handles.zSlider, 'SliderStep', [1/nSteps , 1 ]); + +% Choose default command line output for the slice command: +handles.xx = xx; +handles.yy = yy; +handles.zz = zz; +handles.xslice = xslice; +handles.yslice = yslice; +handles.zslice = zslice; +handles.v = v; + +if isreal(v) + slice(xx, yy, zz, v, xslice, yslice, zslice) + shading interp + colorbar +else + hh = slice(xx, yy, zz, angle(-v), xslice, yslice, zslice); + set(hh, 'EdgeColor','none') + caxis([-pi pi]), + colormap('hsv') + axis('equal') +end + +hold on % Plot the boxes +ids = leaves(f); xdata = [f.domain([1 2 2 1 1 1 2 2 1 1], ids) ; nan(1, length(ids)); ... % bottom & top f.domain([2 2 2 2 1 1], ids) ; nan(1, length(ids));]; % the rest to complete the box ydata = [f.domain([3 3 4 4 3 3 3 4 4 3], ids) ; nan(1, length(ids)); ... @@ -37,12 +86,22 @@ zdata = [f.domain([5 5 5 5 5 6 6 6 6 6], ids) ; nan(1, length(ids)); ... f.domain([5 6 6 5 5 6], ids) ; nan(1, length(ids));]; line('XData', xdata(:), 'YData', ydata(:), 'ZData', zdata(:), 'LineWidth', 1) +hold off axis equal xlim(f.domain(1:2, 1)) ylim(f.domain(3:4, 1)) zlim(f.domain(5:6, 1)) +% +handles.xdata = xdata(:); +handles.ydata = ydata(:); +handles.zdata = zdata(:); + +% Update handles structure +guidata(h, handles); +handles.output = handles.xSlider; + if ( ~holdState ) hold off end @@ -51,4 +110,165 @@ varargout = {h}; end +% Force the figure to clear when another plot is drawn on it so that GUI +% widgets don't linger. (NB: This property needs to be reset to 'add' every +% time we change the plot using a slider; otherwise, the slider movement will +% itself clear the figure, which is not what we want.) +set(h, 'NextPlot', 'replacechildren'); + end + +function h = instantiateSlice3GUI() + +% Load up the GUI from the *.fig file. +installDir = treefunroot(); +h = openFigInCurrentFigure([installDir '/@treefun3/slice.fig']); + +% Do any required initialization of the handle graphics objects. +G = get(h, 'Children'); +for (i = 1:1:length(G)) + if ( isa(G(i), 'matlab.ui.control.UIControl') ) + % Adjust the background colors of the sliders. + if ( strcmp(G(i).Style, 'slider') ) + if ( isequal(get(G(i), 'BackgroundColor'), ... + get(0, 'defaultUicontrolBackgroundColor')) ) + set(G(i), 'BackgroundColor', [.9 .9 .9]); + end + end + % Register callbacks. + switch ( G(i).Tag ) + case 'xSlider' + G(i).Callback = @(hObj, data) ... + xSlider_Callback(hObj, data, guidata(hObj)); + case 'ySlider' + G(i).Callback = @(hObj, data) ... + ySlider_Callback(hObj, data, guidata(hObj)); + case 'zSlider' + G(i).Callback = @(hObj, data) ... + zSlider_Callback(hObj, data, guidata(hObj)); + end + end +end + +% Store handles to GUI objects so that the callbacks can access them. +guidata(h, guihandles(h)); + +end + +% --- Executes on xSlider movement. +function xSlider_Callback(hObject, eventdata, handles) +% hObject handle to xSlider (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +nextPlot = get(hObject.Parent, 'NextPlot'); +set(hObject.Parent, 'NextPlot', 'add'); + +xslice = get(hObject, 'Value'); %returns position of slider +yslice = get(handles.ySlider, 'Value'); %returns position of slider +zslice = get(handles.zSlider, 'Value'); %returns position of slider + +% The next slice command clears the title, if there was any. So, get that +% and put it again afterwards. +tit = get(gca(), 'Title'); +titText = tit.String; + +if ( isreal(handles.v) ) + handles.slice = slice(handles.xx, handles.yy, handles.zz, handles.v, ... + xslice, yslice, zslice); + shading interp + colorbar, +else + handles.slice = slice(handles.xx, handles.yy, handles.zz, angle(-handles.v), ... + xslice, yslice, zslice); + set(handles.slice, 'EdgeColor','none') + caxis([-pi pi]), + colormap('hsv') + axis('equal') +end +handles.line = line('XData', handles.xdata(:), 'YData', handles.ydata(:), 'ZData', handles.zdata(:), 'LineWidth', 1); +title(titText) +handles.output = hObject; + +set(hObject.Parent, 'NextPlot', nextPlot); + +end + +function ySlider_Callback(hObject, eventdata, handles) +% --- Executes on ySlider movement. +% hObject handle to ySlider (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +nextPlot = get(hObject.Parent, 'NextPlot'); +set(hObject.Parent, 'NextPlot', 'add'); + +yslice = get(hObject, 'Value'); %returns position of slider +xslice = get(handles.xSlider, 'Value'); %returns position of slider +zslice = get(handles.zSlider, 'Value'); %returns position of slider + +% The next slice command clears the title, if there was any. So, get that +% and put it again afterwards. +tit = get(gca(), 'Title'); +titText = tit.String; + +if ( isreal(handles.v) ) + handles.slice = slice(handles.xx, handles.yy, handles.zz, handles.v, ... + xslice, yslice, zslice); + shading interp + colorbar, +else + handles.slice = slice(handles.xx, handles.yy, handles.zz, angle(-handles.v), ... + xslice, yslice, zslice); + set(handles.slice, 'EdgeColor','none') + caxis([-pi pi]), + colormap('hsv') + axis('equal') +end +handles.line = line('XData', handles.xdata(:), 'YData', handles.ydata(:), 'ZData', handles.zdata(:), 'LineWidth', 1); +title(titText) +handles.output = hObject; + +set(hObject.Parent, 'NextPlot', nextPlot); + +end + +function zSlider_Callback(hObject, eventdata, handles) +% --- Executes on zSlider movement. +% hObject handle to zSlider (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +nextPlot = get(hObject.Parent, 'NextPlot'); +set(hObject.Parent, 'NextPlot', 'add'); + +zslice = get(hObject, 'Value'); %returns position of slider +xslice = get(handles.xSlider, 'Value'); %returns position of slider +yslice = get(handles.ySlider, 'Value'); %returns position of slider + +% The next slice command clears the title, if there was any. So, get that +% and put it again afterwards. +tit = get(gca(), 'Title'); +titText = tit.String; + +if ( isreal(handles.v) ) + handles.slice = slice(handles.xx, handles.yy, handles.zz, handles.v, ... + xslice, yslice, zslice); + shading interp + colorbar +else + handles.slice = slice(handles.xx, handles.yy, handles.zz, angle(-handles.v), ... + xslice, yslice, zslice); + set(handles.slice, 'EdgeColor','none') + caxis([-pi pi]), + colormap('hsv') + axis('equal') +end +handles.line = line('XData', handles.xdata, 'YData', handles.ydata, 'ZData', handles.zdata, 'LineWidth', 1); +title(titText) +handles.output = hObject; + +set(hObject.Parent, 'NextPlot', nextPlot); + +end + diff --git a/@treefun3/private/openFigInCurrentFigure.m b/@treefun3/private/openFigInCurrentFigure.m new file mode 100644 index 0000000..ff37808 --- /dev/null +++ b/@treefun3/private/openFigInCurrentFigure.m @@ -0,0 +1,35 @@ +function h = openFigInCurrentFigure(figfile) +%OPENFIGINCURRENTFIGURE Wrapper for OPENFIG to open in current figure. +% H = OPENFIGINCURRENTFIGURE(FIGFILE) calls OPENFIG to open the *.fig +% figure specified by the string FIGFILE but does some additional work +% to force it to be opened in the current figure instead of in a new one. + +% Copyright 2017 by The University of Oxford and The Chebfun Developers. +% See http://www.chebfun.org/ for Chebfun information. + +% Developer note: This function exists to allow a user to force the +% special GUI-based plots for chebfun3 (e.g., isosurface(), slice(), etc.) +% to appear in a figure of choice (e.g., by calling figure() to set the +% current figure) instead of appearing in a new figure every time, just +% like ordinary MATLAB plots. + +% Get rid of everything in the current figure. +hgcf = gcf(); +clf(); + +% Reset the 'NextPlot' property. (Should we call newplot() here instead?) +set(hgcf, 'NextPlot', 'add'); + +% Open the *.fig file in a hidden window. +h = openfig(figfile, 'invisible'); + +% Copy the graphics elements from the *.fig figure into the current figure. +copyobj(allchild(h), hgcf); + +% We don't need the *.fig figure anymore, so delete it. +delete(h); + +% Return a handle to the current figure. +h = hgcf; + +end diff --git a/@treefun3/slice.fig b/@treefun3/slice.fig new file mode 100644 index 0000000000000000000000000000000000000000..dc97e5144ef88969e84f87b3f031599cebefa98f GIT binary patch literal 233901 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
?A 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

bB)LkD4e;M^9 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%0AR_Qgy{(|~1mx4}p&>*bbFFE0 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 Date: Sun, 11 Feb 2024 17:38:46 -0500 Subject: [PATCH 03/15] bug fix in err_cfs --- @treefun3/treefun3.m | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index 24f97aa..dd1b472 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -215,10 +215,11 @@ vals = f(xx,yy,zz); coeffs = treefun3.vals2coeffs(vals); -coeffs = coeffs(1:n,1:n); -Ex = sum(abs(coeffs(end-1:end,:)), 'all') / (2*n); -Ey = sum(abs(coeffs(:,end-1:end)), 'all') / (2*n); -err_cfs = (Ex + Ey) / 2; +coeffs = coeffs(1:n,1:n,1:n); +Ex = sum(abs(coeffs(end-1:end,:,:)), 'all') / (2*n^2); +Ey = sum(abs(coeffs(:,end-1:end,:)), 'all') / (2*n^2); +Ez = sum(abs(coeffs(:,:,end-1:end)), 'all') / (2*n^2); +err_cfs = (Ex + Ey + Ez) / 3; % F = f(xxx,yyy,zzz); % G = coeffs2refvals(coeffs); From 3969aff5c3994e4aafac88ce7afd9d119e55b54f Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Sun, 11 Feb 2024 19:17:09 -0500 Subject: [PATCH 04/15] use global error estimate --- @treefun3/treefun3.m | 99 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index dd1b472..c249d90 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -35,6 +35,7 @@ morton flatNeighbors leafNeighbors + rint end @@ -120,7 +121,26 @@ f.coeffs{1} = []; f.col = uint64(0); f.row = uint64(0); + + %%% rint %%% + nalias = f.n; + x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; + [xx0, yy0, zz0] = ndgrid(x0); + l = floor(nalias/2)+1; + v = [2*exp(1i*pi*(0:nalias-l)/nalias)./(1-4*(0:nalias-l).^2) zeros(1,l)]; + w0 = real(ifft(v(1:nalias) + conj(v(nalias+1:-1:2))))'/2; + [wx0, wy0, wz0] = ndgrid(w0); + sclx = diff(dom(1:2)); + scly = diff(dom(3:4)); + sclz = diff(dom(5:6)); + xx = sclx*xx0 + dom(1); + yy = scly*yy0 + dom(3); + zz = sclz*zz0 + dom(5); + ww0 = wx0.*wy0.*wz0; + vals = func(xx,yy,zz); + f.rint = sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')); + % f = buildDepthFirst(f, func); f = buildBreadthFirst(f, func); % f.morton = cartesian2morton(f.col, f.row); @@ -152,17 +172,44 @@ function f = buildBreadthFirst(f, func) + persistent xx0 yy0 zz0 ww0 nstored + nalias = f.n; + if ( isempty(xx0) || f.n ~= nstored ) + nstored = f.n; + x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; + [xx0, yy0, zz0] = ndgrid(x0); + l = floor(nalias/2)+1; + v = [2*exp(1i*pi*(0:nalias-l)/nalias)./(1-4*(0:nalias-l).^2) zeros(1,l)]; + w0 = real(ifft(v(1:nalias) + conj(v(nalias+1:-1:2))))'/2; + [wx0, wy0, wz0] = ndgrid(w0); + ww0 = wx0.*wy0.*wz0; + end + % Note: the length changes at each iteration here id = 1; while ( id <= length(f.id) ) - [resolved, coeffs] = isResolved(func, f.domain(:,id), f.n); + [resolved, coeffs, rintvals] = isResolved(func, f.domain(:,id), f.n, f.rint); if ( resolved ) f.coeffs{id} = coeffs; f.height(id) = 0; else - % Split into four child boxes + % Split into eight child boxes f = refineBox(f, id); f.height(id) = 1; + % whenever split, update rint to be more accurate ... + % (f.rint)^2 - (rintvals)^2 + eight child (rintvals)^2 + r8intvals = 0; + for k = 1:8 + dom = f.domain(:,f.children(k,id)); + sclx = diff(dom(1:2)); + scly = diff(dom(3:4)); + sclz = diff(dom(5:6)); + xx = sclx*xx0 + dom(1); + yy = scly*yy0 + dom(3); + zz = sclz*zz0 + dom(5); + r8intvals = r8intvals + (sclx*scly*sclz)*sum(func(xx,yy,zz).^2.*ww0, 'all'); + end + f.rint = sqrt(f.rint^2 - rintvals^2 + r8intvals); end id = id + 1; end @@ -192,9 +239,9 @@ end -function [resolved, coeffs] = isResolved(f, dom, n) +function [resolved, coeffs, rintvals] = isResolved(f, dom, n, rint) -persistent xx0 yy0 zz0 xxx0 yyy0 zzz0 nstored +persistent xx0 yy0 zz0 ww0 xxx0 yyy0 zzz0 nstored tol = 1e-12; nalias = n; @@ -205,6 +252,11 @@ x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; [xx0, yy0, zz0] = ndgrid(x0); [xxx0, yyy0, zzz0] = ndgrid(linspace(0, 1, nrefpts)); + l = floor(nalias/2)+1; + v = [2*exp(1i*pi*(0:nalias-l)/nalias)./(1-4*(0:nalias-l).^2) zeros(1,l)]; + w0 = real(ifft(v(1:nalias) + conj(v(nalias+1:-1:2))))'/2; + [wx0, wy0, wz0] = ndgrid(w0); + ww0 = wx0.*wy0.*wz0; end sclx = diff(dom(1:2)); scly = diff(dom(3:4)); @@ -214,23 +266,30 @@ zz = sclz*zz0 + dom(5); zzz = sclz*zzz0 + dom(5); vals = f(xx,yy,zz); +rintvals = sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')); coeffs = treefun3.vals2coeffs(vals); coeffs = coeffs(1:n,1:n,1:n); -Ex = sum(abs(coeffs(end-1:end,:,:)), 'all') / (2*n^2); -Ey = sum(abs(coeffs(:,end-1:end,:)), 'all') / (2*n^2); -Ez = sum(abs(coeffs(:,:,end-1:end)), 'all') / (2*n^2); -err_cfs = (Ex + Ey + Ez) / 3; - -% F = f(xxx,yyy,zzz); -% G = coeffs2refvals(coeffs); -% err_vals = max(abs(F(:) - G(:))); - -err = err_cfs; -%err = min(err_cfs, err_vals); -h = sclx; -eta = 0; - -vmax = max(abs(vals(:))); -resolved = ( err * h^eta < tol * max(vmax, 1) ); + +if 0 + Ex = sum(abs(coeffs(end-1:end,:,:)), 'all') / (3*n^2); + Ey = sum(abs(coeffs(:,end-1:end,:)), 'all') / (3*n^2); + Ez = sum(abs(coeffs(:,:,end-1:end)), 'all') / (3*n^2); + err_cfs = (Ex + Ey + Ez) / 3; + + % F = f(xxx,yyy,zzz); + % G = coeffs2refvals(coeffs); + % err_vals = max(abs(F(:) - G(:))); + + err = err_cfs; + %err = min(err_cfs, err_vals); + h = sclx; + eta = 0; + + vmax = max(abs(vals(:))); + resolved = ( err * h^eta < tol * max(vmax, 1) ); +else + erra = sqrt(sum(coeffs.^2, 'all') - sum(coeffs(1:end-2,1:end-2,1:end-2).^2, 'all')) / (n^3 - (n-2)^3); + resolved = ( erra < tol* sqrt(1/(sclx*scly*sclz)) * rint ); +end end From 74155d4641d442d26f84d2ba3c4af5bfcff44b27 Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Sun, 11 Feb 2024 20:19:55 -0500 Subject: [PATCH 05/15] with checkpts option --- @treefun3/treefun3.m | 47 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index c249d90..6d350fc 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -51,6 +51,8 @@ opts = struct(); opts.balance = false; % not yet opts.neighbors = false; + opts.tol = 1e-12; + opts.checkpts = []; if ( nargin == 2 ) if ( isa(varargin{2}, 'treefun3') ) % TREEFUN3(F, TF) @@ -93,6 +95,12 @@ if ( isfield(opts1, 'neighbors') ) opts.neighbors = opts1.neighbors; end + if ( isfield(opts1, 'tol') ) + opts.tol = opts1.tol; + end + if ( isfield(opts1, 'checkpts') ) + opts.checkpts = opts1.checkpts; + end elseif ( nargin == 9 ) % TREEFUN3(DOMAIN, LEVEL, HEIGHT, ID, PARENT, CHILDREN, % COEFFS, COL, ROW) @@ -138,11 +146,11 @@ zz = sclz*zz0 + dom(5); ww0 = wx0.*wy0.*wz0; vals = func(xx,yy,zz); - f.rint = sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')); + f.rint = max(sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')),1e-16); % initialze l2 % f = buildDepthFirst(f, func); - f = buildBreadthFirst(f, func); + f = buildBreadthFirst(f, func, opts.tol, opts.checkpts); % f.morton = cartesian2morton(f.col, f.row); % Now do level restriction @@ -170,7 +178,7 @@ f = refineBox(f, id); - function f = buildBreadthFirst(f, func) + function f = buildBreadthFirst(f, func, tol, checkpts) persistent xx0 yy0 zz0 ww0 nstored nalias = f.n; @@ -188,7 +196,7 @@ % Note: the length changes at each iteration here id = 1; while ( id <= length(f.id) ) - [resolved, coeffs, rintvals] = isResolved(func, f.domain(:,id), f.n, f.rint); + [resolved, coeffs, rintvals] = isResolved(func, f.domain(:,id), f.n, tol, checkpts, f.rint); if ( resolved ) f.coeffs{id} = coeffs; f.height(id) = 0; @@ -233,17 +241,17 @@ vals = coeffs2vals(coeffs); % vals = coeffs2refvals(coeffs); % refvals = chebvals2refvals(chebvals); + checkvals = coeffs2checkvals(coeffs,x,y,z); end end -function [resolved, coeffs, rintvals] = isResolved(f, dom, n, rint) +function [resolved, coeffs, rintvals] = isResolved(f, dom, n, tol, checkpts, rint) persistent xx0 yy0 zz0 ww0 xxx0 yyy0 zzz0 nstored -tol = 1e-12; nalias = n; nrefpts = 2*n; % Sample at equispaced points to test error @@ -269,6 +277,10 @@ rintvals = sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')); coeffs = treefun3.vals2coeffs(vals); coeffs = coeffs(1:n,1:n,1:n); +h = sclx; +eta = 0; + +vmax = max(abs(vals(:))); if 0 Ex = sum(abs(coeffs(end-1:end,:,:)), 'all') / (3*n^2); @@ -282,14 +294,31 @@ err = err_cfs; %err = min(err_cfs, err_vals); - h = sclx; - eta = 0; - vmax = max(abs(vals(:))); resolved = ( err * h^eta < tol * max(vmax, 1) ); else erra = sqrt(sum(coeffs.^2, 'all') - sum(coeffs(1:end-2,1:end-2,1:end-2).^2, 'all')) / (n^3 - (n-2)^3); resolved = ( erra < tol* sqrt(1/(sclx*scly*sclz)) * rint ); end +if ( ~isempty(checkpts) ) % check if func values @ checkpts agree + % func = @(x,y,z) exp(-(x.^2+y.^2+z.^2)*5000000) + exp(-((x-1/2).^2+(y-1/3).^2+(z-3/5).^2)*1000000) + exp(-((x+1/2).^2+(y+1/3).^2+(z+3/5).^2)*2000000); + % f = treefun3(func,[-2 2 -2 2 -2 2],10,struct('checkpts',[0 1/2 -1/2;0 1/3 -1/3;0 3/5 -3/5])); vs f = treefun3(func,[-2 2 -2 2 -2 2],10); + % plot(f,func) + % for later, reduce checkpts if resolved + xxx = 2 * ((checkpts(1,:)' - dom(1))/sclx) - 1; + yyy = 2 * ((checkpts(2,:)' - dom(3))/sclx) - 1; + zzz = 2 * ((checkpts(3,:)' - dom(5))/sclx) - 1; + in = ( xxx>=-1 & xxx<=1 & ... + yyy>=-1 & yyy<=1 & ... + zzz>=-1 & zzz<=1); + if ( any(in) ) + F = f(checkpts(1,in),checkpts(2,in),checkpts(3,in)); + G = treefun3.coeffs2checkvals(coeffs,xxx(in),yyy(in),zzz(in)); + err_checkvals = max(abs(F(:) - G(:))); + resolved = resolved && ( err_checkvals * h^eta < tol * max(vmax, 1) ); + end + +end + end From 00ae1da14976cd9aa3f40d3c13ffbce02e9ae1c7 Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Tue, 20 Feb 2024 11:59:46 -0500 Subject: [PATCH 06/15] add discussed refineBox update --- @treefun3/refineBox.m | 123 ++++++++++++++++++++++++++++++++++++++---- @treefun3/treefun3.m | 102 +++++++++++++---------------------- 2 files changed, 148 insertions(+), 77 deletions(-) diff --git a/@treefun3/refineBox.m b/@treefun3/refineBox.m index 11deb97..0f0ad05 100644 --- a/@treefun3/refineBox.m +++ b/@treefun3/refineBox.m @@ -1,6 +1,19 @@ -function f = refineBox(f, id) +function f = refineBox(f, id, func) -% Split into four child boxes +persistent xx0 yy0 zz0 ww0 nstored +nalias = f.n; +if ( isempty(xx0) || f.n ~= nstored ) + nstored = f.n; + x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; + [xx0, yy0, zz0] = ndgrid(x0); + l = floor(nalias/2)+1; + v = [2*exp(1i*pi*(0:nalias-l)/nalias)./(1-4*(0:nalias-l).^2) zeros(1,l)]; + w0 = real(ifft(v(1:nalias) + conj(v(nalias+1:-1:2))))'/2; + [wx0, wy0, wz0] = ndgrid(w0); + ww0 = wx0.*wy0.*wz0; +end + +% Split into eight child boxes dom = f.domain(:,id); xmid = mean(dom(1:2)); ymid = mean(dom(3:4)); @@ -13,7 +26,18 @@ f.children(:,cid1) = 0; f.level(cid1) = f.level(id)+1; f.height(cid1) = 0; -f.coeffs{cid1} = []; +cdom1 = f.domain(:,cid1); +csclx1 = diff(cdom1(1:2)); +cscly1 = diff(cdom1(3:4)); +csclz1 = diff(cdom1(5:6)); +cxx1 = csclx1*xx0 + cdom1(1); +cyy1 = cscly1*yy0 + cdom1(3); +czz1 = csclz1*zz0 + cdom1(5); +cvals1 = func(cxx1,cyy1,czz1); +ccoeffs1 = treefun3.vals2coeffs(cvals1); +f.coeffs{cid1} = ccoeffs1(1:f.n,1:f.n,1:f.n); % to replace f.coeffs{cid1} = []; +f.rint(cid1) = sqrt((csclx1*cscly1*csclz1)*sum(cvals1.^2.*ww0, 'all')); +f.vmax(cid1) = max(abs(cvals1(:))); % f.col(cid1) = 2*f.col(id); % f.row(cid1) = 2*f.row(id); % f.morton(cid1) = cartesian2morton(f.col(cid1), f.row(cid1)); @@ -25,7 +49,18 @@ f.children(:,cid2) = 0; f.level(cid2) = f.level(id)+1; f.height(cid2) = 0; -f.coeffs{cid2} = []; +cdom2 = f.domain(:,cid2); +csclx2 = diff(cdom2(1:2)); +cscly2 = diff(cdom2(3:4)); +csclz2 = diff(cdom2(5:6)); +cxx2 = csclx2*xx0 + cdom2(1); +cyy2 = cscly2*yy0 + cdom2(3); +czz2 = csclz2*zz0 + cdom2(5); +cvals2 = func(cxx2,cyy2,czz2); +ccoeffs2 = treefun3.vals2coeffs(cvals2); +f.coeffs{cid2} = ccoeffs2(1:f.n,1:f.n,1:f.n); +f.rint(cid2) = sqrt((csclx2*cscly2*csclz2)*sum(cvals2.^2.*ww0, 'all')); +f.vmax(cid2) = max(abs(cvals2(:))); % f.col(cid2) = 2*f.col(id) + 1; % f.row(cid2) = 2*f.row(id); % f.morton(cid2) = cartesian2morton(f.col(cid2), f.row(cid2)); @@ -37,7 +72,18 @@ f.children(:,cid3) = 0; f.level(cid3) = f.level(id)+1; f.height(cid3) = 0; -f.coeffs{cid3} = []; +cdom3 = f.domain(:,cid3); +csclx3 = diff(cdom3(1:2)); +cscly3 = diff(cdom3(3:4)); +csclz3 = diff(cdom3(5:6)); +cxx3 = csclx3*xx0 + cdom3(1); +cyy3 = cscly3*yy0 + cdom3(3); +czz3 = csclz3*zz0 + cdom3(5); +cvals3 = func(cxx3,cyy3,czz3); +ccoeffs3 = treefun3.vals2coeffs(cvals3); +f.coeffs{cid3} = ccoeffs3(1:f.n,1:f.n,1:f.n); +f.rint(cid3) = sqrt((csclx3*cscly3*csclz3)*sum(cvals3.^2.*ww0, 'all')); +f.vmax(cid3) = max(abs(cvals3(:))); % f.col(cid3) = 2*f.col(id); % f.row(cid3) = 2*f.row(id) + 1; % f.morton(cid3) = cartesian2morton(f.col(cid3), f.row(cid3)); @@ -49,7 +95,18 @@ f.children(:,cid4) = 0; f.level(cid4) = f.level(id)+1; f.height(cid4) = 0; -f.coeffs{cid4} = []; +cdom4 = f.domain(:,cid4); +csclx4 = diff(cdom4(1:2)); +cscly4 = diff(cdom4(3:4)); +csclz4 = diff(cdom4(5:6)); +cxx4 = csclx4*xx0 + cdom4(1); +cyy4 = cscly4*yy0 + cdom4(3); +czz4 = csclz4*zz0 + cdom4(5); +cvals4 = func(cxx4,cyy4,czz4); +ccoeffs4 = treefun3.vals2coeffs(cvals4); +f.coeffs{cid4} = ccoeffs4(1:f.n,1:f.n,1:f.n); +f.rint(cid4) = sqrt((csclx4*cscly4*csclz4)*sum(cvals4.^2.*ww0, 'all')); +f.vmax(cid4) = max(abs(cvals4(:))); % f.col(cid4) = 2*f.col(id) + 1; % f.row(cid4) = 2*f.row(id) + 1; % f.morton(cid4) = cartesian2morton(f.col(cid4), f.row(cid4)); @@ -61,7 +118,18 @@ f.children(:,cid5) = 0; f.level(cid5) = f.level(id)+1; f.height(cid5) = 0; -f.coeffs{cid5} = []; +cdom5 = f.domain(:,cid5); +csclx5 = diff(cdom5(1:2)); +cscly5 = diff(cdom5(3:4)); +csclz5 = diff(cdom5(5:6)); +cxx5 = csclx5*xx0 + cdom5(1); +cyy5 = cscly5*yy0 + cdom5(3); +czz5 = csclz5*zz0 + cdom5(5); +cvals5 = func(cxx5,cyy5,czz5); +ccoeffs5 = treefun3.vals2coeffs(cvals5); +f.coeffs{cid5} = ccoeffs5(1:f.n,1:f.n,1:f.n); +f.rint(cid5) = sqrt((csclx5*cscly5*csclz5)*sum(cvals5.^2.*ww0, 'all')); +f.vmax(cid5) = max(abs(cvals5(:))); cid6 = length(f.id)+1; f.domain(:,cid6) = [xmid dom(2) dom(3) ymid zmid dom(6)]; @@ -70,7 +138,18 @@ f.children(:,cid6) = 0; f.level(cid6) = f.level(id)+1; f.height(cid6) = 0; -f.coeffs{cid6} = []; +cdom6 = f.domain(:,cid6); +csclx6 = diff(cdom6(1:2)); +cscly6 = diff(cdom6(3:4)); +csclz6 = diff(cdom6(5:6)); +cxx6 = csclx6*xx0 + cdom6(1); +cyy6 = cscly6*yy0 + cdom6(3); +czz6 = csclz6*zz0 + cdom6(5); +cvals6 = func(cxx6,cyy6,czz6); +ccoeffs6 = treefun3.vals2coeffs(cvals6); +f.coeffs{cid6} = ccoeffs6(1:f.n,1:f.n,1:f.n); +f.rint(cid6) = sqrt((csclx6*cscly6*csclz6)*sum(cvals6.^2.*ww0, 'all')); +f.vmax(cid6) = max(abs(cvals6(:))); cid7 = length(f.id)+1; f.domain(:,cid7) = [dom(1) xmid ymid dom(4) zmid dom(6)]; @@ -79,7 +158,18 @@ f.children(:,cid7) = 0; f.level(cid7) = f.level(id)+1; f.height(cid7) = 0; -f.coeffs{cid7} = []; +cdom7 = f.domain(:,cid7); +csclx7 = diff(cdom7(1:2)); +cscly7 = diff(cdom7(3:4)); +csclz7 = diff(cdom7(5:6)); +cxx7 = csclx7*xx0 + cdom7(1); +cyy7 = cscly7*yy0 + cdom7(3); +czz7 = csclz7*zz0 + cdom7(5); +cvals7 = func(cxx7,cyy7,czz7); +ccoeffs7 = treefun3.vals2coeffs(cvals7); +f.coeffs{cid7} = ccoeffs7(1:f.n,1:f.n,1:f.n); +f.rint(cid7) = sqrt((csclx7*cscly7*csclz7)*sum(cvals7.^2.*ww0, 'all')); +f.vmax(cid7) = max(abs(cvals7(:))); cid8 = length(f.id)+1; f.domain(:,cid8) = [xmid dom(2) ymid dom(4) zmid dom(6)]; @@ -88,10 +178,21 @@ f.children(:,cid8) = 0; f.level(cid8) = f.level(id)+1; f.height(cid8) = 0; -f.coeffs{cid8} = []; +cdom8 = f.domain(:,cid8); +csclx8 = diff(cdom8(1:2)); +cscly8 = diff(cdom8(3:4)); +csclz8 = diff(cdom8(5:6)); +cxx8 = csclx8*xx0 + cdom8(1); +cyy8 = cscly8*yy0 + cdom8(3); +czz8 = csclz8*zz0 + cdom8(5); +cvals8 = func(cxx8,cyy8,czz8); +ccoeffs8 = treefun3.vals2coeffs(cvals8); +f.coeffs{cid8} = ccoeffs8(1:f.n,1:f.n,1:f.n); +f.rint(cid8) = sqrt((csclx8*cscly8*csclz8)*sum(cvals8.^2.*ww0, 'all')); +f.vmax(cid8) = max(abs(cvals8(:))); f.children(:,id) = [cid1 cid2 cid3 cid4 cid5 cid6 cid7 cid8]; f.height(id) = 1; f.coeffs{id} = []; -end +end \ No newline at end of file diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index 6d350fc..e53eb0b 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -36,6 +36,7 @@ flatNeighbors leafNeighbors rint + vmax end @@ -120,17 +121,7 @@ func = @(x,y) feval(func, x, y); end - f.domain(:,1) = dom(:); - f.level(1) = 0; - f.height(1) = 0; - f.id(1) = 1; - f.parent(1) = 0; - f.children(:,1) = zeros(8, 1); - f.coeffs{1} = []; - f.col = uint64(0); - f.row = uint64(0); - - %%% rint %%% + % initialize vals, rint, coeffs... wrap this up? nalias = f.n; x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; [xx0, yy0, zz0] = ndgrid(x0); @@ -146,7 +137,22 @@ zz = sclz*zz0 + dom(5); ww0 = wx0.*wy0.*wz0; vals = func(xx,yy,zz); - f.rint = max(sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')),1e-16); % initialze l2 + coeffs = treefun3.vals2coeffs(vals); + rint = max(sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')),1e-16); % initialze l2 + + % + f.domain(:,1) = dom(:); + f.level(1) = 0; + f.height(1) = 0; + f.id(1) = 1; + f.parent(1) = 0; + f.children(:,1) = zeros(8, 1); + f.coeffs{1} = []; + f.col = uint64(0); + f.row = uint64(0); + f.coeffs{1} = coeffs(1:f.n,1:f.n,1:f.n); + f.rint(1) = rint; + f.vmax(1) = max(abs(vals(:))); % f = buildDepthFirst(f, func); @@ -176,48 +182,23 @@ methods ( Access = private ) - f = refineBox(f, id); + f = refineBox(f, id, func); function f = buildBreadthFirst(f, func, tol, checkpts) - persistent xx0 yy0 zz0 ww0 nstored - nalias = f.n; - if ( isempty(xx0) || f.n ~= nstored ) - nstored = f.n; - x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; - [xx0, yy0, zz0] = ndgrid(x0); - l = floor(nalias/2)+1; - v = [2*exp(1i*pi*(0:nalias-l)/nalias)./(1-4*(0:nalias-l).^2) zeros(1,l)]; - w0 = real(ifft(v(1:nalias) + conj(v(nalias+1:-1:2))))'/2; - [wx0, wy0, wz0] = ndgrid(w0); - ww0 = wx0.*wy0.*wz0; - end - % Note: the length changes at each iteration here id = 1; + rint = f.rint(1); while ( id <= length(f.id) ) - [resolved, coeffs, rintvals] = isResolved(func, f.domain(:,id), f.n, tol, checkpts, f.rint); + resolved = isResolved(f.coeffs{id}, f.domain(:,id), f.n, tol, f.vmax(id), func, checkpts, rint); if ( resolved ) - f.coeffs{id} = coeffs; f.height(id) = 0; else % Split into eight child boxes - f = refineBox(f, id); + f = refineBox(f, id, func); f.height(id) = 1; - % whenever split, update rint to be more accurate ... - % (f.rint)^2 - (rintvals)^2 + eight child (rintvals)^2 - r8intvals = 0; - for k = 1:8 - dom = f.domain(:,f.children(k,id)); - sclx = diff(dom(1:2)); - scly = diff(dom(3:4)); - sclz = diff(dom(5:6)); - xx = sclx*xx0 + dom(1); - yy = scly*yy0 + dom(3); - zz = sclz*zz0 + dom(5); - r8intvals = r8intvals + (sclx*scly*sclz)*sum(func(xx,yy,zz).^2.*ww0, 'all'); - end - f.rint = sqrt(f.rint^2 - rintvals^2 + r8intvals); + f.coeffs{id} = []; % delete coeffs + rint = sqrt(rint^2 - f.rint(id)^2 + sum(f.rint(end-7:end).^2)); % whenever split, update rint to be more accurate ... end id = id + 1; end @@ -248,41 +229,29 @@ end -function [resolved, coeffs, rintvals] = isResolved(f, dom, n, tol, checkpts, rint) +function resolved = isResolved(coeffs, dom, n, tol, vmax, f, checkpts, rint) +% need f if checkpts -persistent xx0 yy0 zz0 ww0 xxx0 yyy0 zzz0 nstored +persistent xxx0 yyy0 zzz0 nstored -nalias = n; +% nalias = n; nrefpts = 2*n; % Sample at equispaced points to test error -if ( isempty(xx0) || isempty(xxx0) || n ~= nstored ) +if ( isempty(xxx0) || n ~= nstored ) nstored = n; - x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; - [xx0, yy0, zz0] = ndgrid(x0); [xxx0, yyy0, zzz0] = ndgrid(linspace(0, 1, nrefpts)); - l = floor(nalias/2)+1; - v = [2*exp(1i*pi*(0:nalias-l)/nalias)./(1-4*(0:nalias-l).^2) zeros(1,l)]; - w0 = real(ifft(v(1:nalias) + conj(v(nalias+1:-1:2))))'/2; - [wx0, wy0, wz0] = ndgrid(w0); - ww0 = wx0.*wy0.*wz0; end + sclx = diff(dom(1:2)); scly = diff(dom(3:4)); sclz = diff(dom(5:6)); -xx = sclx*xx0 + dom(1); xxx = sclx*xxx0 + dom(1); -yy = scly*yy0 + dom(3); yyy = scly*yyy0 + dom(3); -zz = sclz*zz0 + dom(5); zzz = sclz*zzz0 + dom(5); - -vals = f(xx,yy,zz); -rintvals = sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')); -coeffs = treefun3.vals2coeffs(vals); -coeffs = coeffs(1:n,1:n,1:n); + h = sclx; eta = 0; -vmax = max(abs(vals(:))); +if 0 % did not check + vmax = max(abs(vals(:))); % if needed, compute and store when refineBox -if 0 Ex = sum(abs(coeffs(end-1:end,:,:)), 'all') / (3*n^2); Ey = sum(abs(coeffs(:,end-1:end,:)), 'all') / (3*n^2); Ez = sum(abs(coeffs(:,:,end-1:end)), 'all') / (3*n^2); @@ -307,8 +276,8 @@ % plot(f,func) % for later, reduce checkpts if resolved xxx = 2 * ((checkpts(1,:)' - dom(1))/sclx) - 1; - yyy = 2 * ((checkpts(2,:)' - dom(3))/sclx) - 1; - zzz = 2 * ((checkpts(3,:)' - dom(5))/sclx) - 1; + yyy = 2 * ((checkpts(2,:)' - dom(3))/scly) - 1; + zzz = 2 * ((checkpts(3,:)' - dom(5))/sclz) - 1; in = ( xxx>=-1 & xxx<=1 & ... yyy>=-1 & yyy<=1 & ... zzz>=-1 & zzz<=1); @@ -322,3 +291,4 @@ end end + From 292a76eed9349a725ea5c8aa9d3c7fa5323039fb Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Tue, 20 Feb 2024 23:33:53 -0500 Subject: [PATCH 07/15] bug fix: erra = sqrt(coeffs - subcoeffs) could lead to imaginary value --- @treefun3/treefun3.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index e53eb0b..86ffe88 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -266,7 +266,9 @@ resolved = ( err * h^eta < tol * max(vmax, 1) ); else - erra = sqrt(sum(coeffs.^2, 'all') - sum(coeffs(1:end-2,1:end-2,1:end-2).^2, 'all')) / (n^3 - (n-2)^3); + erra = sqrt( sum(coeffs(end-1:end,:,:).^2,'all') ... + + sum(coeffs(1:end-2,end-1:end,:).^2,'all') ... + + sum(coeffs(1:end-2,1:end-2,end-1:end).^2,'all')) / (n^3 - (n-2)^3); resolved = ( erra < tol* sqrt(1/(sclx*scly*sclz)) * rint ); end From 370e64a00e8f1d66e72033c3b17005467b75e942 Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Wed, 21 Feb 2024 16:52:47 -0500 Subject: [PATCH 08/15] loop over nd rhs... plot is not ready, did not check coeffs2refvals --- @treefun3/coeffs2checkvals.m | 10 ++--- @treefun3/coeffs2refvals.m | 8 ++-- @treefun3/plot.m | 4 +- @treefun3/refineBox.m | 72 ++++++++++++++++++++---------------- @treefun3/treefun3.m | 36 ++++++++++-------- @treefun3/vals2coeffs.m | 8 ++-- 6 files changed, 77 insertions(+), 61 deletions(-) diff --git a/@treefun3/coeffs2checkvals.m b/@treefun3/coeffs2checkvals.m index 0094156..14014a3 100644 --- a/@treefun3/coeffs2checkvals.m +++ b/@treefun3/coeffs2checkvals.m @@ -3,7 +3,7 @@ % persistent Evalx Evaly Evalz -p = size(coeffs, 1); +[p,~,~,nd] = size(coeffs); ncheckpts = numel(x); % hopefully not too large... Evalx = ones(ncheckpts, p); @@ -18,11 +18,11 @@ Evalz(:,k) = 2*z(:).*Evalz(:,k-1)-Evalz(:,k-2); end % coeffs to value map -vals = zeros(ncheckpts,1); +vals = zeros(nd,ncheckpts); for k=1:ncheckpts - tmp1 = permute(tensorprod(Evalx(k,:),coeffs,2,1),[2 3 1]); - tmp2 = permute(tensorprod(Evaly(k,:),tmp1,2,1),[2 3 1]); - vals(k) = permute(tensorprod(Evalz(k,:),tmp2,2,1),[2 3 1]); + tmp1 = permute(tensorprod(Evalx(k,:),coeffs,2,1),[2 3 1 4]); + tmp2 = permute(tensorprod(Evaly(k,:),tmp1,2,1),[2 3 1 4]); + vals(:,k) = squeeze(permute(tensorprod(Evalz(k,:),tmp2,2,1),[2 3 1 4])); end end \ No newline at end of file diff --git a/@treefun3/coeffs2refvals.m b/@treefun3/coeffs2refvals.m index f7d7702..db4858a 100644 --- a/@treefun3/coeffs2refvals.m +++ b/@treefun3/coeffs2refvals.m @@ -3,7 +3,7 @@ % persistent Eval pstored -p = size(coeffs, 1); +[p,~,~,nd] = size(coeffs); nrefpts = 2*p; if ( isempty(Eval) || p ~= pstored ) @@ -16,8 +16,8 @@ end end -tmp1 = permute(tensorprod(Eval,coeffs,2,1),[2 3 1]); -tmp2 = permute(tensorprod(Eval,tmp1,2,1),[2 3 1]); -vals = permute(tensorprod(Eval,tmp2,2,1),[2 3 1]); +tmp1 = permute(tensorprod(Eval,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(Eval,tmp1,2,1),[2 3 1 4]); +vals = squeeze(permute(tensorprod(Eval,tmp2,2,1),[2 3 1 4])); end \ No newline at end of file diff --git a/@treefun3/plot.m b/@treefun3/plot.m index 3d459cc..18fe8f2 100644 --- a/@treefun3/plot.m +++ b/@treefun3/plot.m @@ -27,7 +27,9 @@ [xx, yy, zz] = meshgrid(linspace(f.domain(1), f.domain(2), nplotpts), ... linspace(f.domain(3), f.domain(4), nplotpts), ... linspace(f.domain(5), f.domain(6), nplotpts)); -v = func( xx, yy, zz); +% v = func( xx, yy, zz); +vtmp = func( xx(:), yy(:), zz(:)); +v = reshape(vtmp(:,1),[nplotpts nplotpts nplotpts]); if isreal(v) [row,col,tube] = ind2sub(size(v), find(v(:) == max(v(:)), 1, 'last')); else diff --git a/@treefun3/refineBox.m b/@treefun3/refineBox.m index 0f0ad05..a725031 100644 --- a/@treefun3/refineBox.m +++ b/@treefun3/refineBox.m @@ -33,11 +33,12 @@ cxx1 = csclx1*xx0 + cdom1(1); cyy1 = cscly1*yy0 + cdom1(3); czz1 = csclz1*zz0 + cdom1(5); -cvals1 = func(cxx1,cyy1,czz1); +cvals1 = func(cxx1(:),cyy1(:),czz1(:)); +cvals1 = reshape(cvals1,[nalias nalias nalias size(cvals1,2)]); % additional for nd ccoeffs1 = treefun3.vals2coeffs(cvals1); -f.coeffs{cid1} = ccoeffs1(1:f.n,1:f.n,1:f.n); % to replace f.coeffs{cid1} = []; -f.rint(cid1) = sqrt((csclx1*cscly1*csclz1)*sum(cvals1.^2.*ww0, 'all')); -f.vmax(cid1) = max(abs(cvals1(:))); +f.coeffs{cid1} = ccoeffs1(1:f.n,1:f.n,1:f.n,:); % to replace f.coeffs{cid1} = []; +f.rint(:,cid1) = squeeze(sqrt((csclx1*cscly1*csclz1)*sum(cvals1.^2.*ww0, [1 2 3]))); +f.vmax(:,cid1) = squeeze(max(abs(cvals1),[],[1 2 3])); % f.col(cid1) = 2*f.col(id); % f.row(cid1) = 2*f.row(id); % f.morton(cid1) = cartesian2morton(f.col(cid1), f.row(cid1)); @@ -56,11 +57,12 @@ cxx2 = csclx2*xx0 + cdom2(1); cyy2 = cscly2*yy0 + cdom2(3); czz2 = csclz2*zz0 + cdom2(5); -cvals2 = func(cxx2,cyy2,czz2); +cvals2 = func(cxx2(:),cyy2(:),czz2(:)); +cvals2 = reshape(cvals2,[nalias nalias nalias size(cvals2,2)]); % additional for nd ccoeffs2 = treefun3.vals2coeffs(cvals2); -f.coeffs{cid2} = ccoeffs2(1:f.n,1:f.n,1:f.n); -f.rint(cid2) = sqrt((csclx2*cscly2*csclz2)*sum(cvals2.^2.*ww0, 'all')); -f.vmax(cid2) = max(abs(cvals2(:))); +f.coeffs{cid2} = ccoeffs2(1:f.n,1:f.n,1:f.n,:); +f.rint(:,cid2) = squeeze(sqrt((csclx2*cscly2*csclz2)*sum(cvals2.^2.*ww0, [1 2 3]))); +f.vmax(:,cid2) = squeeze(max(abs(cvals2),[],[1 2 3])); % f.col(cid2) = 2*f.col(id) + 1; % f.row(cid2) = 2*f.row(id); % f.morton(cid2) = cartesian2morton(f.col(cid2), f.row(cid2)); @@ -79,11 +81,12 @@ cxx3 = csclx3*xx0 + cdom3(1); cyy3 = cscly3*yy0 + cdom3(3); czz3 = csclz3*zz0 + cdom3(5); -cvals3 = func(cxx3,cyy3,czz3); +cvals3 = func(cxx3(:),cyy3(:),czz3(:)); +cvals3 = reshape(cvals3,[nalias nalias nalias size(cvals3,2)]); % additional for nd ccoeffs3 = treefun3.vals2coeffs(cvals3); -f.coeffs{cid3} = ccoeffs3(1:f.n,1:f.n,1:f.n); -f.rint(cid3) = sqrt((csclx3*cscly3*csclz3)*sum(cvals3.^2.*ww0, 'all')); -f.vmax(cid3) = max(abs(cvals3(:))); +f.coeffs{cid3} = ccoeffs3(1:f.n,1:f.n,1:f.n,:); +f.rint(:,cid3) = squeeze(sqrt((csclx3*cscly3*csclz3)*sum(cvals3.^2.*ww0, [1 2 3]))); +f.vmax(:,cid3) = squeeze(max(abs(cvals3),[],[1 2 3])); % f.col(cid3) = 2*f.col(id); % f.row(cid3) = 2*f.row(id) + 1; % f.morton(cid3) = cartesian2morton(f.col(cid3), f.row(cid3)); @@ -102,11 +105,12 @@ cxx4 = csclx4*xx0 + cdom4(1); cyy4 = cscly4*yy0 + cdom4(3); czz4 = csclz4*zz0 + cdom4(5); -cvals4 = func(cxx4,cyy4,czz4); +cvals4 = func(cxx4(:),cyy4(:),czz4(:)); +cvals4 = reshape(cvals4,[nalias nalias nalias size(cvals4,2)]); % additional for nd ccoeffs4 = treefun3.vals2coeffs(cvals4); -f.coeffs{cid4} = ccoeffs4(1:f.n,1:f.n,1:f.n); -f.rint(cid4) = sqrt((csclx4*cscly4*csclz4)*sum(cvals4.^2.*ww0, 'all')); -f.vmax(cid4) = max(abs(cvals4(:))); +f.coeffs{cid4} = ccoeffs4(1:f.n,1:f.n,1:f.n,:); +f.rint(:,cid4) = squeeze(sqrt((csclx4*cscly4*csclz4)*sum(cvals4.^2.*ww0, [1 2 3]))); +f.vmax(:,cid4) = squeeze(max(abs(cvals4),[],[1 2 3])); % f.col(cid4) = 2*f.col(id) + 1; % f.row(cid4) = 2*f.row(id) + 1; % f.morton(cid4) = cartesian2morton(f.col(cid4), f.row(cid4)); @@ -125,11 +129,12 @@ cxx5 = csclx5*xx0 + cdom5(1); cyy5 = cscly5*yy0 + cdom5(3); czz5 = csclz5*zz0 + cdom5(5); -cvals5 = func(cxx5,cyy5,czz5); +cvals5 = func(cxx5(:),cyy5(:),czz5(:)); +cvals5 = reshape(cvals5,[nalias nalias nalias size(cvals5,2)]); % additional for nd ccoeffs5 = treefun3.vals2coeffs(cvals5); -f.coeffs{cid5} = ccoeffs5(1:f.n,1:f.n,1:f.n); -f.rint(cid5) = sqrt((csclx5*cscly5*csclz5)*sum(cvals5.^2.*ww0, 'all')); -f.vmax(cid5) = max(abs(cvals5(:))); +f.coeffs{cid5} = ccoeffs5(1:f.n,1:f.n,1:f.n,:); +f.rint(:,cid5) = squeeze(sqrt((csclx5*cscly5*csclz5)*sum(cvals5.^2.*ww0, [1 2 3]))); +f.vmax(:,cid5) = squeeze(max(abs(cvals5),[],[1 2 3])); cid6 = length(f.id)+1; f.domain(:,cid6) = [xmid dom(2) dom(3) ymid zmid dom(6)]; @@ -145,11 +150,12 @@ cxx6 = csclx6*xx0 + cdom6(1); cyy6 = cscly6*yy0 + cdom6(3); czz6 = csclz6*zz0 + cdom6(5); -cvals6 = func(cxx6,cyy6,czz6); +cvals6 = func(cxx6(:),cyy6(:),czz6(:)); +cvals6 = reshape(cvals6,[nalias nalias nalias size(cvals6,2)]); % additional for nd ccoeffs6 = treefun3.vals2coeffs(cvals6); -f.coeffs{cid6} = ccoeffs6(1:f.n,1:f.n,1:f.n); -f.rint(cid6) = sqrt((csclx6*cscly6*csclz6)*sum(cvals6.^2.*ww0, 'all')); -f.vmax(cid6) = max(abs(cvals6(:))); +f.coeffs{cid6} = ccoeffs6(1:f.n,1:f.n,1:f.n,:); +f.rint(:,cid6) = squeeze(sqrt((csclx6*cscly6*csclz6)*sum(cvals6.^2.*ww0, [1 2 3]))); +f.vmax(:,cid6) = squeeze(max(abs(cvals6),[],[1 2 3])); cid7 = length(f.id)+1; f.domain(:,cid7) = [dom(1) xmid ymid dom(4) zmid dom(6)]; @@ -165,11 +171,12 @@ cxx7 = csclx7*xx0 + cdom7(1); cyy7 = cscly7*yy0 + cdom7(3); czz7 = csclz7*zz0 + cdom7(5); -cvals7 = func(cxx7,cyy7,czz7); +cvals7 = func(cxx7(:),cyy7(:),czz7(:)); +cvals7 = reshape(cvals7,[nalias nalias nalias size(cvals7,2)]); % additional for nd ccoeffs7 = treefun3.vals2coeffs(cvals7); -f.coeffs{cid7} = ccoeffs7(1:f.n,1:f.n,1:f.n); -f.rint(cid7) = sqrt((csclx7*cscly7*csclz7)*sum(cvals7.^2.*ww0, 'all')); -f.vmax(cid7) = max(abs(cvals7(:))); +f.coeffs{cid7} = ccoeffs7(1:f.n,1:f.n,1:f.n,:); +f.rint(:,cid7) = squeeze(sqrt((csclx7*cscly7*csclz7)*sum(cvals7.^2.*ww0, [1 2 3]))); +f.vmax(:,cid7) = squeeze(max(abs(cvals7),[],[1 2 3])); cid8 = length(f.id)+1; f.domain(:,cid8) = [xmid dom(2) ymid dom(4) zmid dom(6)]; @@ -185,11 +192,12 @@ cxx8 = csclx8*xx0 + cdom8(1); cyy8 = cscly8*yy0 + cdom8(3); czz8 = csclz8*zz0 + cdom8(5); -cvals8 = func(cxx8,cyy8,czz8); +cvals8 = func(cxx8(:),cyy8(:),czz8(:)); +cvals8 = reshape(cvals8,[nalias nalias nalias size(cvals8,2)]); % additional for nd ccoeffs8 = treefun3.vals2coeffs(cvals8); -f.coeffs{cid8} = ccoeffs8(1:f.n,1:f.n,1:f.n); -f.rint(cid8) = sqrt((csclx8*cscly8*csclz8)*sum(cvals8.^2.*ww0, 'all')); -f.vmax(cid8) = max(abs(cvals8(:))); +f.coeffs{cid8} = ccoeffs8(1:f.n,1:f.n,1:f.n,:); +f.rint(:,cid8) = squeeze(sqrt((csclx8*cscly8*csclz8)*sum(cvals8.^2.*ww0, [1 2 3]))); +f.vmax(:,cid8) = squeeze(max(abs(cvals8),[],[1 2 3])); f.children(:,id) = [cid1 cid2 cid3 cid4 cid5 cid6 cid7 cid8]; f.height(id) = 1; diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index 86ffe88..8317675 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -136,9 +136,10 @@ yy = scly*yy0 + dom(3); zz = sclz*zz0 + dom(5); ww0 = wx0.*wy0.*wz0; - vals = func(xx,yy,zz); + vals = func(xx(:),yy(:),zz(:)); + vals = reshape(vals,[nalias nalias nalias size(vals,2)]); % additional for nd coeffs = treefun3.vals2coeffs(vals); - rint = max(sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, 'all')),1e-16); % initialze l2 + rint = max(squeeze(sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, [1 2 3]))),1e-16); % initialze l2 % f.domain(:,1) = dom(:); @@ -151,8 +152,8 @@ f.col = uint64(0); f.row = uint64(0); f.coeffs{1} = coeffs(1:f.n,1:f.n,1:f.n); - f.rint(1) = rint; - f.vmax(1) = max(abs(vals(:))); + f.rint(:,1) = rint; + f.vmax(:,1) = squeeze(max(abs(vals),[],[1 2 3])); % f = buildDepthFirst(f, func); @@ -188,9 +189,9 @@ % Note: the length changes at each iteration here id = 1; - rint = f.rint(1); + rint = f.rint(:,1); while ( id <= length(f.id) ) - resolved = isResolved(f.coeffs{id}, f.domain(:,id), f.n, tol, f.vmax(id), func, checkpts, rint); + resolved = isResolved(f.coeffs{id}, f.domain(:,id), f.n, tol, f.vmax(:,id), func, checkpts, rint); if ( resolved ) f.height(id) = 0; else @@ -198,7 +199,7 @@ f = refineBox(f, id, func); f.height(id) = 1; f.coeffs{id} = []; % delete coeffs - rint = sqrt(rint^2 - f.rint(id)^2 + sum(f.rint(end-7:end).^2)); % whenever split, update rint to be more accurate ... + rint = sqrt(rint.^2 - f.rint(:,id).^2 + sum(f.rint(:,end-7:end).^2,2)); % whenever split, update rint to be more accurate ... end id = id + 1; end @@ -248,6 +249,7 @@ h = sclx; eta = 0; +[~,~,~,nd] = size(coeffs); % or rint... if 0 % did not check vmax = max(abs(vals(:))); % if needed, compute and store when refineBox @@ -266,10 +268,13 @@ resolved = ( err * h^eta < tol * max(vmax, 1) ); else - erra = sqrt( sum(coeffs(end-1:end,:,:).^2,'all') ... - + sum(coeffs(1:end-2,end-1:end,:).^2,'all') ... - + sum(coeffs(1:end-2,1:end-2,end-1:end).^2,'all')) / (n^3 - (n-2)^3); - resolved = ( erra < tol* sqrt(1/(sclx*scly*sclz)) * rint ); + resolved = 1; + for k = 1:nd + erra = sqrt( sum(coeffs(end-1:end,:,:,k).^2,'all') ... + + sum(coeffs(1:end-2,end-1:end,:,k).^2,'all') ... + + sum(coeffs(1:end-2,1:end-2,end-1:end,k).^2,'all')) / (n^3 - (n-2)^3); + resolved = resolved && ( erra < tol* sqrt(1/(sclx*scly*sclz)) * rint(k) ); + end end if ( ~isempty(checkpts) ) % check if func values @ checkpts agree @@ -284,12 +289,13 @@ yyy>=-1 & yyy<=1 & ... zzz>=-1 & zzz<=1); if ( any(in) ) - F = f(checkpts(1,in),checkpts(2,in),checkpts(3,in)); + F = f(checkpts(1,in)',checkpts(2,in)',checkpts(3,in)')'; % nd x n checkpts G = treefun3.coeffs2checkvals(coeffs,xxx(in),yyy(in),zzz(in)); - err_checkvals = max(abs(F(:) - G(:))); - resolved = resolved && ( err_checkvals * h^eta < tol * max(vmax, 1) ); + err_checkvals = max(abs(F - G),[],2); + for k = 1:nd + resolved = resolved && ( err_checkvals(k) * h^eta < tol * max(vmax(k), 1) ); + end end - end end diff --git a/@treefun3/vals2coeffs.m b/@treefun3/vals2coeffs.m index 094e0ae..a3b8e60 100644 --- a/@treefun3/vals2coeffs.m +++ b/@treefun3/vals2coeffs.m @@ -9,7 +9,7 @@ cutoff = 1e+03; % ... later % Get the length of the input: -p = size(vals, 1); +[p,~,~,nd] = size(vals); % vals of dim p^3 x nd if ( p <= 1 ) % Trivial case (constant): @@ -23,9 +23,9 @@ F{p} = 2*cos(pi*((1:p)-1)'*(2*(p:-1:1)-1)/(2*p))/p; F{p}(1,:) = 1/2*F{p}(1,:); end - tmp1hat = permute(tensorprod(F{p},vals,2,1),[2 3 1]); - tmp2hat = permute(tensorprod(F{p},tmp1hat,2,1),[2 3 1]); - coeffs = permute(tensorprod(F{p},tmp2hat,2,1),[2 3 1]); + tmp1hat = permute(tensorprod(F{p},vals,2,1),[2 3 1 4]); + tmp2hat = permute(tensorprod(F{p},tmp1hat,2,1),[2 3 1 4]); + coeffs = permute(tensorprod(F{p},tmp2hat,2,1),[2 3 1 4]); else % Use fast transform ... not yet % coeffs = chebtech2.vals2coeffs( chebtech2.vals2coeffs(vals).' ).'; From 29b22ce0f85495d9a7d32e483875605f4ffff7c1 Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Wed, 21 Feb 2024 17:35:31 -0500 Subject: [PATCH 09/15] treefun3 interface change --- @treefun3/plot.m | 7 ++++-- @treefun3/refineBox.m | 57 +++++++++++++++++++++++++++++++------------ @treefun3/treefun3.m | 15 +++++++++--- 3 files changed, 57 insertions(+), 22 deletions(-) diff --git a/@treefun3/plot.m b/@treefun3/plot.m index 18fe8f2..7f6fa15 100644 --- a/@treefun3/plot.m +++ b/@treefun3/plot.m @@ -28,8 +28,11 @@ linspace(f.domain(3), f.domain(4), nplotpts), ... linspace(f.domain(5), f.domain(6), nplotpts)); % v = func( xx, yy, zz); -vtmp = func( xx(:), yy(:), zz(:)); -v = reshape(vtmp(:,1),[nplotpts nplotpts nplotpts]); +nd = numel(func); +v = zeros(size(xx)); +for k = 1:nd + v = v + func{k}(xx,yy,zz); +end if isreal(v) [row,col,tube] = ind2sub(size(v), find(v(:) == max(v(:)), 1, 'last')); else diff --git a/@treefun3/refineBox.m b/@treefun3/refineBox.m index a725031..0d8f731 100644 --- a/@treefun3/refineBox.m +++ b/@treefun3/refineBox.m @@ -2,6 +2,7 @@ persistent xx0 yy0 zz0 ww0 nstored nalias = f.n; +nd = numel(func); if ( isempty(xx0) || f.n ~= nstored ) nstored = f.n; x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; @@ -33,8 +34,11 @@ cxx1 = csclx1*xx0 + cdom1(1); cyy1 = cscly1*yy0 + cdom1(3); czz1 = csclz1*zz0 + cdom1(5); -cvals1 = func(cxx1(:),cyy1(:),czz1(:)); -cvals1 = reshape(cvals1,[nalias nalias nalias size(cvals1,2)]); % additional for nd +cvals1 = cell(1,nd); +for k = 1:nd + cvals1{k} = func{k}(cxx1,cyy1,czz1); +end +cvals1 = cat(4,cvals1{:}); % additional for nd ccoeffs1 = treefun3.vals2coeffs(cvals1); f.coeffs{cid1} = ccoeffs1(1:f.n,1:f.n,1:f.n,:); % to replace f.coeffs{cid1} = []; f.rint(:,cid1) = squeeze(sqrt((csclx1*cscly1*csclz1)*sum(cvals1.^2.*ww0, [1 2 3]))); @@ -57,8 +61,11 @@ cxx2 = csclx2*xx0 + cdom2(1); cyy2 = cscly2*yy0 + cdom2(3); czz2 = csclz2*zz0 + cdom2(5); -cvals2 = func(cxx2(:),cyy2(:),czz2(:)); -cvals2 = reshape(cvals2,[nalias nalias nalias size(cvals2,2)]); % additional for nd +cvals2 = cell(1,nd); +for k = 1:nd + cvals2{k} = func{k}(cxx2,cyy2,czz2); +end +cvals2 = cat(4,cvals2{:}); % additional for nd ccoeffs2 = treefun3.vals2coeffs(cvals2); f.coeffs{cid2} = ccoeffs2(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid2) = squeeze(sqrt((csclx2*cscly2*csclz2)*sum(cvals2.^2.*ww0, [1 2 3]))); @@ -81,8 +88,11 @@ cxx3 = csclx3*xx0 + cdom3(1); cyy3 = cscly3*yy0 + cdom3(3); czz3 = csclz3*zz0 + cdom3(5); -cvals3 = func(cxx3(:),cyy3(:),czz3(:)); -cvals3 = reshape(cvals3,[nalias nalias nalias size(cvals3,2)]); % additional for nd +cvals3 = cell(1,nd); +for k = 1:nd + cvals3{k} = func{k}(cxx3,cyy3,czz3); +end +cvals3 = cat(4,cvals3{:}); % additional for nd ccoeffs3 = treefun3.vals2coeffs(cvals3); f.coeffs{cid3} = ccoeffs3(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid3) = squeeze(sqrt((csclx3*cscly3*csclz3)*sum(cvals3.^2.*ww0, [1 2 3]))); @@ -105,8 +115,11 @@ cxx4 = csclx4*xx0 + cdom4(1); cyy4 = cscly4*yy0 + cdom4(3); czz4 = csclz4*zz0 + cdom4(5); -cvals4 = func(cxx4(:),cyy4(:),czz4(:)); -cvals4 = reshape(cvals4,[nalias nalias nalias size(cvals4,2)]); % additional for nd +cvals4 = cell(1,nd); +for k = 1:nd + cvals4{k} = func{k}(cxx4,cyy4,czz4); +end +cvals4 = cat(4,cvals4{:}); % additional for nd ccoeffs4 = treefun3.vals2coeffs(cvals4); f.coeffs{cid4} = ccoeffs4(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid4) = squeeze(sqrt((csclx4*cscly4*csclz4)*sum(cvals4.^2.*ww0, [1 2 3]))); @@ -129,8 +142,11 @@ cxx5 = csclx5*xx0 + cdom5(1); cyy5 = cscly5*yy0 + cdom5(3); czz5 = csclz5*zz0 + cdom5(5); -cvals5 = func(cxx5(:),cyy5(:),czz5(:)); -cvals5 = reshape(cvals5,[nalias nalias nalias size(cvals5,2)]); % additional for nd +cvals5 = cell(1,nd); +for k = 1:nd + cvals5{k} = func{k}(cxx5,cyy5,czz5); +end +cvals5 = cat(4,cvals5{:}); % additional for nd ccoeffs5 = treefun3.vals2coeffs(cvals5); f.coeffs{cid5} = ccoeffs5(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid5) = squeeze(sqrt((csclx5*cscly5*csclz5)*sum(cvals5.^2.*ww0, [1 2 3]))); @@ -150,8 +166,11 @@ cxx6 = csclx6*xx0 + cdom6(1); cyy6 = cscly6*yy0 + cdom6(3); czz6 = csclz6*zz0 + cdom6(5); -cvals6 = func(cxx6(:),cyy6(:),czz6(:)); -cvals6 = reshape(cvals6,[nalias nalias nalias size(cvals6,2)]); % additional for nd +cvals6 = cell(1,nd); +for k = 1:nd + cvals6{k} = func{k}(cxx6,cyy6,czz6); +end +cvals6 = cat(4,cvals6{:}); % additional for nd ccoeffs6 = treefun3.vals2coeffs(cvals6); f.coeffs{cid6} = ccoeffs6(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid6) = squeeze(sqrt((csclx6*cscly6*csclz6)*sum(cvals6.^2.*ww0, [1 2 3]))); @@ -171,8 +190,11 @@ cxx7 = csclx7*xx0 + cdom7(1); cyy7 = cscly7*yy0 + cdom7(3); czz7 = csclz7*zz0 + cdom7(5); -cvals7 = func(cxx7(:),cyy7(:),czz7(:)); -cvals7 = reshape(cvals7,[nalias nalias nalias size(cvals7,2)]); % additional for nd +cvals7 = cell(1,nd); +for k = 1:nd + cvals7{k} = func{k}(cxx7,cyy7,czz7); +end +cvals7 = cat(4,cvals7{:}); % additional for nd ccoeffs7 = treefun3.vals2coeffs(cvals7); f.coeffs{cid7} = ccoeffs7(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid7) = squeeze(sqrt((csclx7*cscly7*csclz7)*sum(cvals7.^2.*ww0, [1 2 3]))); @@ -192,8 +214,11 @@ cxx8 = csclx8*xx0 + cdom8(1); cyy8 = cscly8*yy0 + cdom8(3); czz8 = csclz8*zz0 + cdom8(5); -cvals8 = func(cxx8(:),cyy8(:),czz8(:)); -cvals8 = reshape(cvals8,[nalias nalias nalias size(cvals8,2)]); % additional for nd +cvals8 = cell(1,nd); +for k = 1:nd + cvals8{k} = func{k}(cxx8,cyy8,czz8); +end +cvals8 = cat(4,cvals8{:}); % additional for nd ccoeffs8 = treefun3.vals2coeffs(cvals8); f.coeffs{cid8} = ccoeffs8(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid8) = squeeze(sqrt((csclx8*cscly8*csclz8)*sum(cvals8.^2.*ww0, [1 2 3]))); diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index 8317675..6425b41 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -123,6 +123,7 @@ % initialize vals, rint, coeffs... wrap this up? nalias = f.n; + nd = numel(func); x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; [xx0, yy0, zz0] = ndgrid(x0); l = floor(nalias/2)+1; @@ -136,8 +137,11 @@ yy = scly*yy0 + dom(3); zz = sclz*zz0 + dom(5); ww0 = wx0.*wy0.*wz0; - vals = func(xx(:),yy(:),zz(:)); - vals = reshape(vals,[nalias nalias nalias size(vals,2)]); % additional for nd + vals = cell(1,nd); + for k = 1:nd + vals{k} = func{k}(xx,yy,zz); + end + vals = cat(4,vals{:}); coeffs = treefun3.vals2coeffs(vals); rint = max(squeeze(sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, [1 2 3]))),1e-16); % initialze l2 @@ -151,7 +155,7 @@ f.coeffs{1} = []; f.col = uint64(0); f.row = uint64(0); - f.coeffs{1} = coeffs(1:f.n,1:f.n,1:f.n); + f.coeffs{1} = coeffs(1:f.n,1:f.n,1:f.n,:); f.rint(:,1) = rint; f.vmax(:,1) = squeeze(max(abs(vals),[],[1 2 3])); @@ -289,7 +293,10 @@ yyy>=-1 & yyy<=1 & ... zzz>=-1 & zzz<=1); if ( any(in) ) - F = f(checkpts(1,in)',checkpts(2,in)',checkpts(3,in)')'; % nd x n checkpts + F = zeros(nd,sum(in)); + for k = 1:nd + F(k,:) = f{k}(checkpts(1,in)',checkpts(2,in)',checkpts(3,in)')'; % nd x n checkpts + end G = treefun3.coeffs2checkvals(coeffs,xxx(in),yyy(in),zzz(in)); err_checkvals = max(abs(F - G),[],2); for k = 1:nd From 9130d9a448387bd55fe0ff45f1c556748938cb03 Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Sun, 3 Mar 2024 21:09:37 -0500 Subject: [PATCH 10/15] loop over level --- @treefun3/coeffs2refvals.m | 2 +- @treefun3/treefun3.m | 356 ++++++++++++++++++++++++++++++++++--- 2 files changed, 329 insertions(+), 29 deletions(-) diff --git a/@treefun3/coeffs2refvals.m b/@treefun3/coeffs2refvals.m index db4858a..080133b 100644 --- a/@treefun3/coeffs2refvals.m +++ b/@treefun3/coeffs2refvals.m @@ -4,7 +4,7 @@ persistent Eval pstored [p,~,~,nd] = size(coeffs); -nrefpts = 2*p; +nrefpts = p; if ( isempty(Eval) || p ~= pstored ) pstored = p; diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index 6425b41..34eb352 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -35,6 +35,7 @@ morton flatNeighbors leafNeighbors + rint0 rint vmax @@ -54,6 +55,8 @@ opts.neighbors = false; opts.tol = 1e-12; opts.checkpts = []; + opts.ifcoeffs = true; + opts.ifstorecoeffs = true; if ( nargin == 2 ) if ( isa(varargin{2}, 'treefun3') ) % TREEFUN3(F, TF) @@ -124,6 +127,11 @@ % initialize vals, rint, coeffs... wrap this up? nalias = f.n; nd = numel(func); + if nd == 1 && ~iscell(func) + func1 = []; + func1{1} = func; + func = func1; + end x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; [xx0, yy0, zz0] = ndgrid(x0); l = floor(nalias/2)+1; @@ -156,12 +164,13 @@ f.col = uint64(0); f.row = uint64(0); f.coeffs{1} = coeffs(1:f.n,1:f.n,1:f.n,:); + f.rint0(:,1) = zeros(nd,1); f.rint(:,1) = rint; f.vmax(:,1) = squeeze(max(abs(vals),[],[1 2 3])); % f = buildDepthFirst(f, func); - f = buildBreadthFirst(f, func, opts.tol, opts.checkpts); + f = buildBreadthFirst(f, func, opts.tol, opts.checkpts, opts.ifcoeffs, opts.ifstorecoeffs); % f.morton = cartesian2morton(f.col, f.row); % Now do level restriction @@ -189,24 +198,104 @@ f = refineBox(f, id, func); - function f = buildBreadthFirst(f, func, tol, checkpts) + function f = buildBreadthFirst(f, func, tol, checkpts, ifcoeffs, ifstorecoeffs) - % Note: the length changes at each iteration here - id = 1; + % initialization a 2nd tree that's the same as the 1st one rint = f.rint(:,1); - while ( id <= length(f.id) ) - resolved = isResolved(f.coeffs{id}, f.domain(:,id), f.n, tol, f.vmax(:,id), func, checkpts, rint); - if ( resolved ) - f.height(id) = 0; - else - % Split into eight child boxes - f = refineBox(f, id, func); - f.height(id) = 1; - f.coeffs{id} = []; % delete coeffs - rint = sqrt(rint.^2 - f.rint(:,id).^2 + sum(f.rint(:,end-7:end).^2,2)); % whenever split, update rint to be more accurate ... + [~,~,~,nd] = size(f.coeffs{1}); + resolved = isResolved(f.coeffs{1}, f.domain(:,1), f.n, tol, f.vmax(:,1), func, checkpts, ifcoeffs, rint); + f.rint0(:,1) = rint; + if resolved + return + else + refinecnt = 1; + idl_start = 1; + idl = 1; % current level box id + end + if ~ifstorecoeffs % save memory, once isResolved done + f.coeffs{1} = []; + end + + while(refinecnt) + + % process previous level unresolved boxes, idl(1), idl(2),.... + domc = zeros(6,8*refinecnt); % allocate space for all children info of this level's boxes + coeffsc = cell(1,8*refinecnt); + rintc = zeros(nd,8*refinecnt); + rint0c = zeros(nd,8*refinecnt); + vmaxc = zeros(nd,8*refinecnt); + parentc = zeros(1,8*refinecnt); + idc = zeros(1,8*refinecnt); + levelc = zeros(1,8*refinecnt); + for k = 1:refinecnt % 1 by 1 + id = idl(k); + [domck,coeffsck,rintck,vmaxck] = refineBoxv2(f.domain(:,id), f.n, func); % just refine the box... + f.children(:,id) = idl_start+8*(k-1)+(1:8); % start to know + f.height(id) = 1; + % 1 to 8 + cidx = 8*(k-1)+(1:8); + domc(:,cidx) = domck; + coeffsc(cidx) = coeffsck; + rintc(:,cidx) = rintck; + vmaxc(:,cidx) = vmaxck; + parentc(cidx) = id*ones(1,8); % these children's parent id + idc(cidx) = idl_start+8*(k-1)+(1:8); % children self id + levelc(cidx) = (f.level(id)+1)*ones(1,8); + end + childrenc = zeros(8,8*refinecnt); % don't know yet, a bunch of 0s to be concatenate to the end of f.children, will know when next level + heightc = zeros(1,8*refinecnt); % tmp leaf box + f.domain = cat(2,f.domain,domc); % concatenate children info + f.coeffs = cat(2,f.coeffs,coeffsc); + f.rint = cat(2,f.rint,rintc); + f.rint0 = cat(2,f.rint0,rint0c); + f.vmax = cat(2,f.vmax,vmaxc); + f.parent = cat(2,f.parent,parentc); + f.id = cat(2,f.id,idc); + f.level = cat(2,f.level,levelc); + f.children = cat(2,f.children,childrenc); + f.height = cat(2,f.height,heightc); + + % update rint, - num of idl parents contribution + 8*num of idl children contribution + rint = sqrt(rint.^2 - sum(f.rint(:,idl).^2,2) + sum(f.rint(:,(idl_start+1):(idl_start+8*refinecnt)).^2,2)); + + % see if newly created boxes need to be refined next + unresolvedl = true(1,8*length(idl)); % potentially unresolved + idl0 = (idl_start+1):(idl_start+8*length(idl)); % current level id, from start to num of idl refined + for k = 1:8*length(idl) + id = idl_start + k; + resolved = isResolved(f.coeffs{id}, f.domain(:,id), f.n, tol, f.vmax(:,id), func, checkpts, ifcoeffs, rint); + f.rint0(:,id) = rint; + if resolved + unresolvedl(k) = false; end - id = id + 1; + if ~ifstorecoeffs % save memory, once isResolved done + f.coeffs{id} = []; + end + end + + % for next loop + idl_start = idl_start + 8*refinecnt; % next level starts from here + idl = idl0(unresolvedl); % select subset of all potential boxes, based on unresolved + refinecnt = length(idl); + end + + % % Note: the length changes at each iteration here + % id = 1; + % rint = f.rint(:,1); + % while ( id <= length(f.id) ) + % resolved = isResolved(f.coeffs{id}, f.domain(:,id), f.n, tol, f.vmax(:,id), func, checkpts, ifcoeffs, rint); + % if ( resolved ) + % f.height(id) = 0; + % else + % % Split into eight child boxes + % f = refineBox(f, id, func); + % f.height(id) = 1; + % f.coeffs{id} = []; % delete coeffs + % rint = sqrt(rint.^2 - f.rint(:,id).^2 + sum(f.rint(:,end-7:end).^2,2)); % whenever split, update rint to be more accurate ... + % end + % id = id + 1; + % end % Do a cumulative sum in reverse to correct the heights for k = length(f.id):-1:1 @@ -225,7 +314,7 @@ % u = poisson(f, isource); coeffs = vals2coeffs(vals); vals = coeffs2vals(coeffs); - % vals = coeffs2refvals(coeffs); + vals = coeffs2refvals(coeffs); % refvals = chebvals2refvals(chebvals); checkvals = coeffs2checkvals(coeffs,x,y,z); @@ -234,17 +323,19 @@ end -function resolved = isResolved(coeffs, dom, n, tol, vmax, f, checkpts, rint) +function resolved = isResolved(coeffs, dom, n, tol, vmax, f, checkpts, ifcoeffs, rint) % need f if checkpts -persistent xxx0 yyy0 zzz0 nstored +persistent xxx0 yyy0 zzz0 www0 nstored % nalias = n; -nrefpts = 2*n; % Sample at equispaced points to test error +nrefpts = n; % Sample at equispaced points to test error if ( isempty(xxx0) || n ~= nstored ) nstored = n; [xxx0, yyy0, zzz0] = ndgrid(linspace(0, 1, nrefpts)); + [wxxx0, wyyy0, wzzz0] = ndgrid(1/nstored*ones(nstored,1)); + www0 = wxxx0.*wyyy0.*wzzz0; % weight end sclx = diff(dom(1:2)); @@ -255,7 +346,36 @@ eta = 0; [~,~,~,nd] = size(coeffs); % or rint... -if 0 % did not check +if ~ifcoeffs + resolved = 1; + xxx = sclx*xxx0 + dom(1); + yyy = scly*yyy0 + dom(3); + zzz = sclz*zzz0 + dom(5); + vals = cell(1,nd); + for k = 1:nd + vals{k} = f{k}(xxx,yyy,zzz); + end + F = cat(4,vals{:}); + G = treefun3.coeffs2refvals(coeffs); % structured grid + for k = 1:nd + erra = sqrt(sum(squeeze(G(:,:,:,k) - F(:,:,:,k)).^2.*www0,'all')); + resolved = resolved && ( erra < tol * sqrt(1/(sclx*scly*sclz)) * rint(k) ); + end + +elseif ifcoeffs + resolved = 1; + erra = zeros(nd,1); + for k = 1:nd + erra(k) = sqrt( sum(coeffs(end,:,:,k).^2,'all') ... + + sum(coeffs(1:end-1,end,:,k).^2,'all') ... + + sum(coeffs(1:end-1,1:end-1,end,k).^2,'all')) / sqrt(n^3 - (n-1)^3); % only last slice + % erra = sqrt( sum(coeffs(end-1:end,:,:,k).^2,'all') ... + % + sum(coeffs(1:end-2,end-1:end,:,k).^2,'all') ... + % + sum(coeffs(1:end-2,1:end-2,end-1:end,k).^2,'all')) / sqrt(n^3 - (n-2)^3); + resolved = resolved && ( erra(k) < tol* sqrt(1/(sclx*scly*sclz)) * rint(k) ); + end + +else % did not check vmax = max(abs(vals(:))); % if needed, compute and store when refineBox Ex = sum(abs(coeffs(end-1:end,:,:)), 'all') / (3*n^2); @@ -271,14 +391,6 @@ %err = min(err_cfs, err_vals); resolved = ( err * h^eta < tol * max(vmax, 1) ); -else - resolved = 1; - for k = 1:nd - erra = sqrt( sum(coeffs(end-1:end,:,:,k).^2,'all') ... - + sum(coeffs(1:end-2,end-1:end,:,k).^2,'all') ... - + sum(coeffs(1:end-2,1:end-2,end-1:end,k).^2,'all')) / (n^3 - (n-2)^3); - resolved = resolved && ( erra < tol* sqrt(1/(sclx*scly*sclz)) * rint(k) ); - end end if ( ~isempty(checkpts) ) % check if func values @ checkpts agree @@ -307,3 +419,191 @@ end + +function [domain,coeffs,rint,vmax] = refineBoxv2(dom, n, func) + +% if nargin==0, test_refineBox3dv2; return; end + +persistent xx0 yy0 zz0 ww0 nstored +nalias = n; +if ( isempty(xx0) || n ~= nstored ) + nstored = n; + x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; + [xx0, yy0, zz0] = ndgrid(x0); + l = floor(nalias/2)+1; + v = [2*exp(1i*pi*(0:nalias-l)/nalias)./(1-4*(0:nalias-l).^2) zeros(1,l)]; + w0 = real(ifft(v(1:nalias) + conj(v(nalias+1:-1:2))))'/2; + [wx0, wy0, wz0] = ndgrid(w0); + ww0 = wx0.*wy0.*wz0; +end + +% Split into eight child boxes +xmid = mean(dom(1:2)); +ymid = mean(dom(3:4)); +zmid = mean(dom(5:6)); + +% domain and coeffs +domain = zeros(6,8); +coeffs = cell(1,8); +nd = numel(func); + +cdom1 = [dom(1) xmid dom(3) ymid dom(5) zmid]; +csclx1 = diff(cdom1(1:2)); +cscly1 = diff(cdom1(3:4)); +csclz1 = diff(cdom1(5:6)); +cxx1 = csclx1*xx0 + cdom1(1); +cyy1 = cscly1*yy0 + cdom1(3); +czz1 = csclz1*zz0 + cdom1(5); +cvals1 = cell(1,nd); +for k = 1:nd + cvals1{k} = func{k}(cxx1,cyy1,czz1); +end +cvals1 = cat(4,cvals1{:}); +ccoeffs1 = treefun3.vals2coeffs(cvals1); +% f.col(cid1) = 2*f.col(id); +% f.row(cid1) = 2*f.row(id); +% f.morton(cid1) = cartesian2morton(f.col(cid1), f.row(cid1)); +domain(:,1) = cdom1; +coeffs{1} = ccoeffs1(1:n,1:n,1:n,:); % to replace f.coeffs{cid1} = []; + +cdom2 = [xmid dom(2) dom(3) ymid dom(5) zmid]; +csclx2 = diff(cdom2(1:2)); +cscly2 = diff(cdom2(3:4)); +csclz2 = diff(cdom2(5:6)); +cxx2 = csclx2*xx0 + cdom2(1); +cyy2 = cscly2*yy0 + cdom2(3); +czz2 = csclz2*zz0 + cdom2(5); +cvals2 = cell(1,nd); +for k = 1:nd + cvals2{k} = func{k}(cxx2,cyy2,czz2); +end +cvals2 = cat(4,cvals2{:}); +ccoeffs2 = treefun3.vals2coeffs(cvals2); +% f.col(cid2) = 2*f.col(id) + 1; +% f.row(cid2) = 2*f.row(id); +% f.morton(cid2) = cartesian2morton(f.col(cid2), f.row(cid2)); +domain(:,2) = cdom2; +coeffs{2} = ccoeffs2(1:n,1:n,1:n,:); + +cdom3 = [dom(1) xmid ymid dom(4) dom(5) zmid]; +csclx3 = diff(cdom3(1:2)); +cscly3 = diff(cdom3(3:4)); +csclz3 = diff(cdom3(5:6)); +cxx3 = csclx3*xx0 + cdom3(1); +cyy3 = cscly3*yy0 + cdom3(3); +czz3 = csclz3*zz0 + cdom3(5); +cvals3 = cell(1,nd); +for k = 1:nd + cvals3{k} = func{k}(cxx3,cyy3,czz3); +end +cvals3 = cat(4,cvals3{:}); +ccoeffs3 = treefun3.vals2coeffs(cvals3); +% f.col(cid3) = 2*f.col(id); +% f.row(cid3) = 2*f.row(id) + 1; +% f.morton(cid3) = cartesian2morton(f.col(cid3), f.row(cid3)); +domain(:,3) = cdom3; +coeffs{3} = ccoeffs3(1:n,1:n,1:n,:); + +cdom4 = [xmid dom(2) ymid dom(4) dom(5) zmid]; +csclx4 = diff(cdom4(1:2)); +cscly4 = diff(cdom4(3:4)); +csclz4 = diff(cdom4(5:6)); +cxx4 = csclx4*xx0 + cdom4(1); +cyy4 = cscly4*yy0 + cdom4(3); +czz4 = csclz4*zz0 + cdom4(5); +cvals4 = cell(1,nd); +for k = 1:nd + cvals4{k} = func{k}(cxx4,cyy4,czz4); +end +cvals4 = cat(4,cvals4{:}); +ccoeffs4 = treefun3.vals2coeffs(cvals4); +% f.col(cid4) = 2*f.col(id) + 1; +% f.row(cid4) = 2*f.row(id) + 1; +% f.morton(cid4) = cartesian2morton(f.col(cid4), f.row(cid4)); +domain(:,4) = cdom4; +coeffs{4} = ccoeffs4(1:n,1:n,1:n,:); + +cdom5 = [dom(1) xmid dom(3) ymid zmid dom(6)]; +csclx5 = diff(cdom5(1:2)); +cscly5 = diff(cdom5(3:4)); +csclz5 = diff(cdom5(5:6)); +cxx5 = csclx5*xx0 + cdom5(1); +cyy5 = cscly5*yy0 + cdom5(3); +czz5 = csclz5*zz0 + cdom5(5); +cvals5 = cell(1,nd); +for k = 1:nd + cvals5{k} = func{k}(cxx5,cyy5,czz5); +end +cvals5 = cat(4,cvals5{:}); +ccoeffs5 = treefun3.vals2coeffs(cvals5); +domain(:,5) = cdom5; +coeffs{5} = ccoeffs5(1:n,1:n,1:n,:); + +cdom6 = [xmid dom(2) dom(3) ymid zmid dom(6)]; +csclx6 = diff(cdom6(1:2)); +cscly6 = diff(cdom6(3:4)); +csclz6 = diff(cdom6(5:6)); +cxx6 = csclx6*xx0 + cdom6(1); +cyy6 = cscly6*yy0 + cdom6(3); +czz6 = csclz6*zz0 + cdom6(5); +cvals6 = cell(1,nd); +for k = 1:nd + cvals6{k} = func{k}(cxx6,cyy6,czz6); +end +cvals6 = cat(4,cvals6{:}); +ccoeffs6 = treefun3.vals2coeffs(cvals6); +domain(:,6) = cdom6; +coeffs{6} = ccoeffs6(1:n,1:n,1:n,:); + +cdom7 = [dom(1) xmid ymid dom(4) zmid dom(6)]; +csclx7 = diff(cdom7(1:2)); +cscly7 = diff(cdom7(3:4)); +csclz7 = diff(cdom7(5:6)); +cxx7 = csclx7*xx0 + cdom7(1); +cyy7 = cscly7*yy0 + cdom7(3); +czz7 = csclz7*zz0 + cdom7(5); +cvals7 = cell(1,nd); +for k = 1:nd + cvals7{k} = func{k}(cxx7,cyy7,czz7); +end +cvals7 = cat(4,cvals7{:}); +ccoeffs7 = treefun3.vals2coeffs(cvals7); +domain(:,7) = cdom7; +coeffs{7} = ccoeffs7(1:n,1:n,1:n,:); + +cdom8 = [xmid dom(2) ymid dom(4) zmid dom(6)]; +csclx8 = diff(cdom8(1:2)); +cscly8 = diff(cdom8(3:4)); +csclz8 = diff(cdom8(5:6)); +cxx8 = csclx8*xx0 + cdom8(1); +cyy8 = cscly8*yy0 + cdom8(3); +czz8 = csclz8*zz0 + cdom8(5); +cvals8 = cell(1,nd); +for k = 1:nd + cvals8{k} = func{k}(cxx8,cyy8,czz8); +end +cvals8 = cat(4,cvals8{:}); +ccoeffs8 = treefun3.vals2coeffs(cvals8); +domain(:,8) = cdom8; +coeffs{8} = ccoeffs8(1:n,1:n,1:n,:); + +% a few other things +rint = [squeeze(sqrt((csclx1*cscly1*csclz1)*sum(cvals1.^2.*ww0, [1 2 3]))),... + squeeze(sqrt((csclx2*cscly2*csclz2)*sum(cvals2.^2.*ww0, [1 2 3]))),... + squeeze(sqrt((csclx3*cscly3*csclz3)*sum(cvals3.^2.*ww0, [1 2 3]))),... + squeeze(sqrt((csclx4*cscly4*csclz4)*sum(cvals4.^2.*ww0, [1 2 3]))),... + squeeze(sqrt((csclx5*cscly5*csclz5)*sum(cvals5.^2.*ww0, [1 2 3]))),... + squeeze(sqrt((csclx6*cscly6*csclz6)*sum(cvals6.^2.*ww0, [1 2 3]))),... + squeeze(sqrt((csclx7*cscly7*csclz7)*sum(cvals7.^2.*ww0, [1 2 3]))),... + squeeze(sqrt((csclx8*cscly8*csclz8)*sum(cvals8.^2.*ww0, [1 2 3])))]; +vmax = [squeeze(max(abs(cvals1),[],[1 2 3])),... + squeeze(max(abs(cvals2),[],[1 2 3])),... + squeeze(max(abs(cvals3),[],[1 2 3])),... + squeeze(max(abs(cvals4),[],[1 2 3])),... + squeeze(max(abs(cvals5),[],[1 2 3])),... + squeeze(max(abs(cvals6),[],[1 2 3])),... + squeeze(max(abs(cvals7),[],[1 2 3])),... + squeeze(max(abs(cvals8),[],[1 2 3]))]; + +end + From 0cb7058361a32fe45e37eae017f408eb19c9d8d5 Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Sat, 4 May 2024 17:50:26 -0400 Subject: [PATCH 11/15] add 2d balancef.m --- @treefun2/balancef.m | 730 +++++++++++++++++++++++++++++++++++ @treefun2/coeffs2checkvals.m | 29 ++ @treefun2/treefun2.m | 2 + 3 files changed, 761 insertions(+) create mode 100644 @treefun2/balancef.m create mode 100644 @treefun2/coeffs2checkvals.m diff --git a/@treefun2/balancef.m b/@treefun2/balancef.m new file mode 100644 index 0000000..cbdd8f6 --- /dev/null +++ b/@treefun2/balancef.m @@ -0,0 +1,730 @@ +function g = balancef(f) +% fortran vol_tree_fix_lr +% assume square boxes? therefore scale is computed from dom(1:2) +% nd needs to be fixed later... +% flatNeighbors and leafNeighbors also need to be updated in the future +% + +nd = 1; % +dpars = zeros(1000,1); +ipars = zeros(100,1); +zpars = zeros(10,1); + +if ( isempty(f) ) + g = f; + return +end + +% dada +dom = f.domain(:,1)'; +scale = (dom(2)-dom(1))/2; +norder = f.n; +ndim = numel(dom(1:end/2)); + +% convert to vol_tree_build output format +iper = 0; +npbox = norder^ndim; +grid = zeros(ndim,norder^2); +nbmax = 2^ndim*f.id(end); % is 2^ndim enough, since some box needs to be refined more than once... +nlmax = f.height(1); % is this ok? +centers0 = 1/2*(f.domain(1:2:end,:)+f.domain(2:2:end,:)); +centers = zeros(ndim,nbmax); centers(:,1:size(centers0,2)) = centers0; +nlevels = f.height(1); +nboxes = f.id(end); +boxsize = 2*scale./2.^(0:nlmax); +laddr = zeros(2,nlmax+1); +for ilev=0:nlmax + laddr(1,ilev+1) = sum(f.level0); +ichild = zeros(2^ndim,nbmax); ichild(:,1:nboxes) = f.children; ichild(ichild==0) = -1; % -1 convention in fortran tree + +% this does not belong to fortran tree, but needed for treefun plot +coeffs = cell(1,nbmax); coeffs(1:nboxes) = f.coeffs(1:nboxes); + +% call computecoll to get vol_tree_fix_lr coll info +nnbors0 = zeros(nboxes,1); nbors0 = zeros(3^ndim,nboxes); +[nnbors0,nbors0] = ... + computecoll(ndim,nlevels,nboxes,laddr,boxsize,... + centers(:,1:nboxes),iparent(1:nboxes),nchild(1:nboxes),ichild(:,1:nboxes),iper,... + nnbors0,nbors0); +nnbors = zeros(nbmax,1); nbors = zeros(3^ndim,nbmax); +nnbors(1:nboxes) = nnbors0; nbors(:,1:nboxes) = nbors0; + +% now fix lr (with coeffs, which is different from) +[centers,nboxes,laddr,ilevel,iparent,nchild,ichild,nnbors,nbors,coeffs] = ... + vol_tree_fix_lr_treefun(ndim,iper,nd,dpars,zpars,ipars,... + norder,npbox,grid,centers,nlevels,nboxes,boxsize,... + nbmax,nlmax,laddr,ilevel,iparent,nchild,ichild,nnbors,nbors,... + coeffs); + +centers = centers(:,1:nboxes); +ilevel = ilevel(1:nboxes); +iparent = iparent(1:nboxes); +nchild = nchild(1:nboxes); +ichild = ichild(:,1:nboxes); +nnbors = nnbors(1:nboxes); +nbors = nbors(:,1:nboxes); +coeffs = coeffs(1:nboxes); + +% convert back to treefun format, try to follow treefun naming convention +g = f; +scl = boxsize(ilevel+1); % x,y,z all the same +g.domain = zeros(2*ndim,nboxes); +g.domain(1:2:end,:) = centers - 1/2*scl; +g.domain(2:2:end,:) = centers + 1/2*scl; +g.level = ilevel(:)'; +g.id = 1:nboxes; +g.parent = iparent(:)'; +g.children = ichild; +g.height = zeros(1,nboxes); +g.height(nchild>0) = 1; +for k = length(g.id):-1:1 + if ( ~(g.height(k)==0) ) + g.height(k) = 1 + max(g.height(g.children(:,k))); + end +end +% redo some stuff +g.col = uint64(zeros(1,nboxes)); +g.row = uint64(zeros(1,nboxes)); +g.morton = uint64(zeros(1,nboxes)); +mc = 2^ndim; +for ilev = 0:nlevels-1 + for ibox = laddr(1,ilev+1):laddr(2,ilev+1) + if nchild(ibox) > 0 + if ndim == 2 + j = 1; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox); + g.row(jbox) = 2*g.row(ibox); + g.morton(jbox) = cartesian2morton(g.col(jbox), g.row(jbox)); + j = 2; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox) + 1; + g.row(jbox) = 2*g.row(ibox); + g.morton(jbox) = cartesian2morton(g.col(jbox), g.row(jbox)); + j = 3; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox); + g.row(jbox) = 2*g.row(ibox) + 1; + g.morton(jbox) = cartesian2morton(g.col(jbox), g.row(jbox)); + j = 4; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox) + 1; + g.row(jbox) = 2*g.row(ibox) + 1; + g.morton(jbox) = cartesian2morton(g.col(jbox), g.row(jbox)); + % keyboard + end + end + % g.col(ibox) = 2*g.col(iparent(ibox)); + % g.row(ibox) = 2*g.row(iparent(ibox)); + end +end +g.coeffs = coeffs; +end + +function [centers,nboxes,laddr,ilevel,iparent,nchild,ichild,nnbors,nbors,coeffs] = vol_tree_fix_lr_treefun(ndim,iperiod,nd,dpars,zpars,ipars, ... + norder,npbox,grid,centers,nlevels,nboxes,boxsize, ... + nbmax,nlmax,laddr,ilevel,iparent,nchild,ichild,nnbors,nbors, ... + coeffs) + +laddrtail=zeros(2,nlmax+1); +% boxsize(0:nlmax),laddr(2,0:nlmax),laddrtail(2,0:nlmax) +idx1 = 1; +% +bs0=boxsize(0+idx1); +% +mc=2^ndim; +mnbors=3^ndim; + +iflag = zeros(nbmax,1); iflag2 = zeros(nbmax,1); + +for i=1:nboxes + iflag(i) = 0; +end + +% Flag boxes that violate level restriction by "1" Violatioin refers to any +% box that is directly touching a box that is more than one level finer +% Method: +% 1) Carry out upward pass. For each box B, look at the colleagues of B's +% grandparent +% 2) See if any of those colleagues are childless and in contact with B. +% +% Note that we only need to get up to level two, as we will not find a +% violation at level 0 and level 1 For such boxes, we set iflag(i) = 1 +% figure(1), clf, +for ilev=nlevels:-1:2 + distest = 1.05d0*(boxsize(ilev-1+idx1) + boxsize(ilev-2+idx1))/2.0d0; + for ibox = laddr(1,ilev+idx1):laddr(2,ilev+idx1) % look at current box + idad = iparent(ibox); % its parent box + igranddad = iparent(idad); % its grand parent box + + for i=1:nnbors(igranddad) % look at neighbor of grand parent + jbox = nbors(i,igranddad); + if((nchild(jbox)==0) && (iflag(jbox)==0)) % if neighbor of grand parent has no child, and has not been flagged + ict = 0; % count overlapping dimension between ibox & its grand parent's neighbor + for k=1:ndim + dis = centers(k,jbox) - centers(k,idad); + if (iperiod==1) + dp1=dis+bs0; + dm1=dis-bs0; + if (abs(dis).gt.abs(dp1)), dis=dp1; end + if (abs(dis).gt.abs(dm1)), dis=dm1; end + end + if(abs(dis)<=distest), ict = ict + 1; end + end + if(ict==ndim) + iflag(jbox) = 1; + % figure(1), + % plotibox(centers(:,ibox),boxsize(ilev-1+idx1)/2); + % plotibox(centers(:,jbox),4*boxsize(ilev-1+idx1)/2); + % axis equal + end + end + end + end +end + +% Find all boxes that need to be given a flag+ A flag+ box will be denoted +% by setting iflag(box) = 2 This refers to any box that is not already +% flagged and is bigger than and is contacting a flagged box or another box +% that has already been given a flag +. It is found by performing an upward +% pass and looking at the flagged box's parents colleagues and a flag+ +% box's parents colleagues and seeing if they are childless and present the +% case where a bigger box is contacting a flagged or flag+ box. +% figure(2), clf, +%% if secondary violator, 1st send it downstairs during Step 4, then wait to be processed in Step 5 or no need to process at all, seems to be later, from the "worst-case" example +for ilev = nlevels:-1:1 + distest = 1.05d0*(boxsize(ilev+idx1) + boxsize(ilev-1+idx1))/2.0d0; + for ibox = laddr(1,ilev+idx1):laddr(2,ilev+idx1) + if((iflag(ibox)==1) || (iflag(ibox)==2)) % look at current box + idad = iparent(ibox); % its parent box + for i=1:nnbors(idad) % look at neighbor of parent + jbox = nbors(i,idad); + if((nchild(jbox)==0) && (iflag(jbox)==0)) % if neighbor of parent has no child, and has not been flagged + ict = 0; % count overlapping dimension between ibox & its parent's neighbor + for k=1:ndim + dis = centers(k,jbox) - centers(k,ibox); + if (iperiod==1) + dp1=dis+bs0; + dm1=dis-bs0; + if (abs(dis)>abs(dp1)), dis=dp1; end + if (abs(dis)>abs(dm1)), dis=dm1; end + end + if(abs(dis)<=distest), ict = ict + 1; end + end + if(ict==ndim) + iflag(jbox) = 2; + % figure(2), + % plotibox(centers(:,ibox),boxsize(ilev-1+idx1)/2); + % plotibox(centers(:,jbox),4*boxsize(ilev-1+idx1)/2); + % axis equal + end + end + end + end + end +end + +% Subdivide all flag and flag+ boxes. Flag all the children of flagged +% boxes as flag++. Flag++ boxes are denoted by setting iflag(box) = 3. The +% flag++ boxes need to be checked later to see which of them need further +% refinement. While creating new boxes, we will need to update all the tree +% structures as well. Note that all the flagged boxes live between levels 1 +% and nlevels - 2. We process the boxes via a downward pass. We first +% determine the number of boxes that are going to be subdivided at each +% level and everything else accordingly +for ilev = 0:nlevels + laddrtail(1,ilev+idx1) = 0; + laddrtail(2,ilev+idx1) = -1; +end + +nboxes0 = nboxes; +%% Frank's thesis, Appendix A, Step 4, divide all of the primary and secondary violators once. I think +% figure out iflag = 3 case +for ilev = 1:nlevels-2 + + laddrtail(1,ilev+1+idx1) = nboxes+1; + + nbloc = laddr(2,ilev+idx1)-laddr(1,ilev+idx1)+1; + + [iflag,centers,nboxes,ilevel,iparent,nchild,ichild,coeffs] = vol_tree_refine_boxes_flag_treefun(ndim,iflag,nd,npbox,... + dpars,zpars,ipars,grid,nbmax,laddr(1,ilev+idx1),nbloc, ... + centers,boxsize(ilev+1+idx1),nboxes,ilev,ilevel,iparent,nchild,ichild, ... + coeffs); + + laddrtail(2,ilev+1+idx1) = nboxes; + +end + +ishuffle1 = zeros(nboxes,1); +centerstmp = centers(:,1:nboxes); ileveltmp = ilevel(1:nboxes); iparenttmp = iparent(1:nboxes); nchildtmp = nchild(1:nboxes); ichildtmp = ichild(:,1:nboxes); iflagtmp = iflag(1:nboxes); +coeffstmp = coeffs(1:nboxes); +[centerstmp,laddr,ileveltmp,ishuffle1,iparenttmp,nchildtmp,ichildtmp,coeffstmp,iflagtmp] = ... + vol_tree_reorg_treefun(ndim,nboxes,nd,npbox,centerstmp,nlevels,laddr,... + laddrtail,ileveltmp,ishuffle1,iparenttmp,nchildtmp,ichildtmp,coeffstmp,iflagtmp); +centers(:,1:nboxes) = centerstmp; ilevel(1:nboxes) = ileveltmp; iparent(1:nboxes) = iparenttmp; nchild(1:nboxes) = nchildtmp; ichild(:,1:nboxes) = ichildtmp; iflag(1:nboxes) = iflagtmp; +coeffs(1:nboxes) = coeffstmp; + +nnborstmp = nnbors(1:nboxes); nborstmp = nbors(:,1:nboxes); +[nnborstmp,nborstmp] = computecoll(ndim,nlevels,nboxes,laddr,boxsize,... + centers(:,1:nboxes),iparent(1:nboxes),nchild(1:nboxes),... + ichild(:,1:nboxes),iperiod,nnborstmp,nborstmp); +nnbors(1:nboxes) = nnborstmp; nbors(:,1:nboxes) = nborstmp; + + +% Processing of flag and flag+ boxes is done Start processing flag++ boxes. +% We will use a similar strategy as before. We keep checking the flag++ +% boxes that require subdivision if they still violate the level +% restriction criterion, create the new boxes, append them to the end of +% the list to begin with and in the end reorganize the tree structure. We +% shall accomplish this via a downward pass as new boxes that get added in +% the downward pass will also be processed simultaneously. We shall +% additionally also need to keep on updating the colleague information as +% we proceed in the downward pass +% +% Reset the flags array to remove all the flag and flag+ cases. This is to +% ensure reusability of the subdivide_flag routine to handle the flag++ case + +for ibox=1:nboxes + if(iflag(ibox)~=3), iflag(ibox) = 0; end +end + +for ilev = 0:nlevels + laddrtail(1,ilev+idx1) = 0; + laddrtail(2,ilev+idx1) = -1; +end + +% boxsize(0:nlmax),laddr(2,0:nlmax),laddrtail(2,0:nlmax) +%% Frank's thesis, Appendix A, Step 5, at each level, for a descendant of a primary violator, test whether it is still in violation +for ilev = 2:nlevels-2 + + % Step 1 + iflagtmp = iflag(1:nboxes); + iflagtmp = vol_updateflags(ndim,iperiod,ilev,nboxes,nlevels, ... + laddr,nchild(1:nboxes),ichild(:,1:nboxes),nnbors(1:nboxes),nbors(:,1:nboxes),centers(:,1:nboxes),boxsize,iflagtmp); + iflag(1:nboxes) = iflagtmp; + + iflagtmp = iflag(1:nboxes); + iflagtmp = vol_updateflags(ndim,iperiod,ilev,nboxes,nlevels, ... + laddrtail,nchild(1:nboxes),ichild(:,1:nboxes),nnbors(1:nboxes),nbors(:,1:nboxes),centers(:,1:nboxes),boxsize,iflagtmp); + iflag(1:nboxes) = iflagtmp; + + % Step 2 + laddrtail(1,ilev+1+idx1) = nboxes + 1; + + nbloc = laddr(2,ilev+idx1)-laddr(1,ilev+idx1)+1; + [iflag,centers,nboxes,ilevel,iparent,nchild,ichild,coeffs] = vol_tree_refine_boxes_flag_treefun(ndim,iflag,nd,npbox, ... + dpars,zpars,ipars,grid,nbmax,laddr(1,ilev+idx1),nbloc, ... + centers,boxsize(ilev+1+idx1),nboxes,ilev,ilevel,iparent,nchild,ichild, ... + coeffs); + + nbloc = laddrtail(2,ilev+idx1)-laddrtail(1,ilev+idx1)+1; + [iflag,centers,nboxes,ilevel,iparent,nchild,ichild,coeffs] = vol_tree_refine_boxes_flag_treefun(ndim,iflag,nd,npbox, ... + dpars,zpars,ipars,grid,nbmax,laddrtail(1,ilev+idx1),nbloc, ... + centers,boxsize(ilev+1+idx1),nboxes,ilev,ilevel,iparent,nchild,ichild, ... + coeffs); + laddrtail(2,ilev+1+idx1) = nboxes; + + % Step 3 + for ibox = laddrtail(1,ilev+1+idx1):laddrtail(2,ilev+1+idx1) + nnbors(ibox) = 0; + idad = iparent(ibox); + for i=1:nnbors(idad) + jbox = nbors(i,idad); + for j=1:mc + kbox = ichild(j,jbox); + if(kbox>0) + ifnbor=1; + for k=1:ndim + dis=centers(k,kbox)-centers(k,ibox); + if (iperiod==1) + dp1=dis+bs0; + dm1=dis-bs0; + if (abs(dis)>abs(dp1)), dis=dp1; end + if (abs(dis)>abs(dm1)), dis=dm1; end + end + if((abs(dis)>1.05*boxsize(ilev+1+idx1))) + ifnbor=0; + break + end + end + if (ifnbor==1) + nnbors(ibox) = nnbors(ibox)+1; + nbors(nnbors(ibox),ibox) = kbox; + end + end + end + end + % End of computing colleagues of box i + end + +end + +ishuffle2 = zeros(nboxes,1); +centerstmp = centers(:,1:nboxes); ileveltmp = ilevel(1:nboxes); iparenttmp = iparent(1:nboxes); nchildtmp = nchild(1:nboxes); ichildtmp = ichild(:,1:nboxes); iflagtmp = iflag(1:nboxes); +coeffstmp = coeffs(1:nboxes); +[centerstmp,laddr,ileveltmp,ishuffle2,iparenttmp,nchildtmp,ichildtmp,coeffstmp,iflagtmp] = ... + vol_tree_reorg_treefun(ndim,nboxes,nd,npbox,centerstmp,nlevels,laddr,... + laddrtail,ileveltmp,ishuffle2,iparenttmp,nchildtmp,ichildtmp,coeffstmp,iflagtmp); +centers(:,1:nboxes) = centerstmp; ilevel(1:nboxes) = ileveltmp; iparent(1:nboxes) = iparenttmp; nchild(1:nboxes) = nchildtmp; ichild(:,1:nboxes) = ichildtmp; iflag(1:nboxes) = iflagtmp; +coeffs(1:nboxes) = coeffstmp; + +nnborstmp = nnbors(1:nboxes); nborstmp = nbors(:,1:nboxes); +[nnborstmp,nborstmp] = computecoll(ndim,nlevels,nboxes,laddr,boxsize,... + centers(:,1:nboxes),iparent(1:nboxes),nchild(1:nboxes),... + ichild(:,1:nboxes),iperiod,nnborstmp,nborstmp); +nnbors(1:nboxes) = nnborstmp; nbors(:,1:nboxes) = nborstmp; + +end + +function [iflag,centers,nbctr,ilevel,iparent,nchild,ichild,coeffs] = vol_tree_refine_boxes_flag_treefun(ndim,iflag,nd,npbox,dpars,zpars,ipars,grid,nboxes,ifirstbox,nbloc,centers,bs,nbctr,nlctr,ilevel,iparent,nchild,ichild,coeffs) + +% myfun = @(x,y) sin(x).*cos(y)+exp(x.*y); +% myvals = myfun(2*(xx0-1/2),2*(yy0-1/2)); +% mycoeffs = treefun2.vals2coeffs(myvals); +% mycvals1 = reshape(treefun2.coeffs2checkvals(mycoeffs,cxx1(:),cyy1(:)),[norder norder]); +% mycvals1_2 = myfun(cxx1,cyy1); +% mycvals2 = reshape(treefun2.coeffs2checkvals(mycoeffs,cxx2(:),cyy2(:)),[norder norder]); +% mycvals3 = reshape(treefun2.coeffs2checkvals(mycoeffs,cxx3(:),cyy3(:)),[norder norder]); +% mycvals4 = reshape(treefun2.coeffs2checkvals(mycoeffs,cxx4(:),cyy4(:)),[norder norder]); +% figure(1),clf,surf(cxx1,cyy1,mycvals1), +% hold on, surf(cxx2,cyy2,mycvals2) +% hold on, surf(cxx3,cyy3,mycvals3) +% hold on, surf(cxx4,cyy4,mycvals4) + +% additional +persistent xx0 yy0 xxx0 yyy0 nstored +norder=(npbox)^(1/ndim); +n=norder; +if ndim == 2 + nalias = n; + nrefpts = n; % Sample at equispaced points to test error + + if ( isempty(xx0) || isempty(xxx0) || n ~= nstored ) + nstored = n; + [xx0, yy0] = chebpts2(nalias, nalias, [0 1 0 1]); + [xxx0, yyy0] = meshgrid(linspace(0, 1, nrefpts)); + end + sclx = 2*bs; + scly = 2*bs; +end + +if ndim == 3 +end + +% +isgn=zeros(ndim,2^ndim); +isgn=get_child_box_sign(ndim,isgn); + +mc=2^ndim; + +ilastbox = ifirstbox+nbloc-1; + +bsh = bs/2; + +isum=zeros(nbloc,1); + +isum = cumsum(iflag(ifirstbox:(ifirstbox+nbloc-1))>0); % there are iflag = 2 entries + +for i = 1:nbloc + ibox = ifirstbox + i-1; + if(iflag(ibox)>0) + nbl = nbctr + (isum(i)-1)*mc; + nchild(ibox) = mc; + % additional + if ndim == 2 + xx = sclx*xx0 + (centers(1,ibox)-1/2*sclx); + yy = scly*yy0 + (centers(2,ibox)-1/2*scly); + vals = treefun2.coeffs2vals(coeffs{ibox}); + % split into four chilx boxes + dom = [-1 1 -1 1]; + xmid = mean(dom(1:2)); + ymid = mean(dom(3:4)); + j=1; + jbox = nbl+j; + cdom1 = [dom(1) xmid dom(3) ymid]; + csclx1 = diff(cdom1(1:2)); + cscly1 = diff(cdom1(3:4)); + cxx1 = csclx1*xx0 + cdom1(1); + cyy1 = cscly1*yy0 + cdom1(3); + cvals1 = reshape(treefun2.coeffs2checkvals(coeffs{ibox},cxx1(:),cyy1(:)),[norder norder]); + ccoeffs1 = treefun2.vals2coeffs(cvals1); + coeffs{jbox} = ccoeffs1; + j=2; + jbox = nbl+j; + cdom2 = [xmid dom(2) dom(3) ymid]; + csclx2 = diff(cdom2(1:2)); + cscly2 = diff(cdom2(3:4)); + cxx2 = csclx2*xx0 + cdom2(1); + cyy2 = cscly2*yy0 + cdom2(3); + cvals2 = reshape(treefun2.coeffs2checkvals(coeffs{ibox},cxx2(:),cyy2(:)),[norder norder]); + ccoeffs2 = treefun2.vals2coeffs(cvals2); + coeffs{jbox} = ccoeffs2; + j=3; + jbox = nbl+j; + cdom3 = [dom(1) xmid ymid dom(4)]; + csclx3 = diff(cdom3(1:2)); + cscly3 = diff(cdom3(3:4)); + cxx3 = csclx3*xx0 + cdom3(1); + cyy3 = cscly3*yy0 + cdom3(3); + cvals3 = reshape(treefun2.coeffs2checkvals(coeffs{ibox},cxx3(:),cyy3(:)),[norder norder]); + ccoeffs3 = treefun2.vals2coeffs(cvals3); + coeffs{jbox} = ccoeffs3; + j=4; + jbox = nbl+j; + cdom4 = [xmid dom(2) ymid dom(4)]; + csclx4 = diff(cdom4(1:2)); + cscly4 = diff(cdom4(3:4)); + cxx4 = csclx4*xx0 + cdom4(1); + cyy4 = cscly4*yy0 + cdom4(3); + cvals4 = reshape(treefun2.coeffs2checkvals(coeffs{ibox},cxx4(:),cyy4(:)),[norder norder]); + ccoeffs4 = treefun2.vals2coeffs(cvals4); + coeffs{jbox} = ccoeffs4; + % + coeffs{ibox} = []; + end + for j=1:mc + jbox = nbl+j; + for k=1:ndim + centers(k,jbox) = centers(k,ibox)+isgn(k,j)*bsh; + end + iparent(jbox) = ibox; + nchild(jbox) = 0; + for l=1:mc + ichild(l,jbox) = -1; + end + ichild(j,ibox) = jbox; + ilevel(jbox) = nlctr+1; + if(iflag(ibox)==1), iflag(jbox) = 3; end + if(iflag(ibox)==2), iflag(jbox) = 0; end + end + end +end + +if(nbloc>0), nbctr = nbctr + isum(nbloc)*mc; end + +end + +function iflag = vol_updateflags(ndim,iperiod,curlev,nboxes,nlevels,laddr,nchild,ichild,nnbors,nbors,centers,boxsize,iflag) +% +% + +idx1 = 1; +bs0=boxsize(0+idx1); + +mc=2^ndim; +distest = 1.05d0*(boxsize(curlev+idx1) + boxsize(curlev+1+idx1))/2.0d0; + +for ibox = laddr(1,curlev+idx1):laddr(2,curlev+idx1) + if(iflag(ibox)==3) + iflag(ibox) = 0; + for i=1:nnbors(ibox) + jbox = nbors(i,ibox); + + for j=1:mc + kbox = ichild(j,jbox); + if(kbox>0) + if(nchild(kbox)>0) + ict = 0; + for k=1:ndim + dis = centers(k,kbox) - centers(k,ibox); + if (iperiod==1) + dp1=dis+bs0; + dm1=dis-bs0; + if (abs(dis)>abs(dp1)), dis=dp1; end + if (abs(dis)>abs(dm1)), dis=dm1; end + end + if(abs(dis)<=distest), ict = ict + 1; end + end + if(ict==ndim) + iflag(ibox) = 1; + break; + end + end + end + end + if iflag(ibox) == 1 + break; + end + end + % 1111 continue + end +end + +end + +function [nnbors,nbors] = computecoll(ndim,nlevels,nboxes,laddr,boxsize,centers,iparent,nchild,ichild,iperiod,nnbors,nbors) + +% laddr(2,0:nlevels) +% boxsize(0:nlevels) + +idx1 = 1; + +bs0=boxsize(0+idx1); + +mc= 2^ndim; +mnbors=3^ndim; + +for i=1:nboxes + nnbors(i) = 0; + for j=1:mnbors + nbors(j,i) = -1; + end +end + +nnbors(1) = 1; +nbors(1,1) = 1; +for ilev = 1:nlevels + ifirstbox = laddr(1,ilev+idx1); + ilastbox = laddr(2,ilev+idx1); + + for ibox = ifirstbox:ilastbox + dad = iparent(ibox); + for i=1:nnbors(dad) + jbox = nbors(i,dad); + for j=1:mc + kbox = ichild(j,jbox); + if(kbox>0) + ifnbor=1; + for k=1:ndim + dis=abs(centers(k,kbox)-centers(k,ibox)); + if (iperiod==1) + dp1=bs0-dis; + if (dp11.05*boxsize(ilev+idx1)) + ifnbor=0; + break + end + end + + if(ifnbor==1) + nnbors(ibox) = nnbors(ibox)+1; + nbors(nnbors(ibox),ibox) = kbox; + end + end + end + end + end +end + +end + +function [centers,laddr,ilevel,ishuffle,iparent,nchild,ichild,coeffs,iflag] = vol_tree_reorg_treefun(ndim,nboxes,nd,npbox,centers,nlevels,laddr,laddrtail,ilevel,ishuffle,iparent,nchild,ichild,coeffs,iflag) +% +% + +% laddrtail(2,0:nlevels) +% laddr(2,0:nlevels) +% tladdr(2,0:nlevels) + +tladdr = zeros(2,nlevels+1); + +idx1 = 1; + +mc=2^ndim; + +tilevel=zeros(nboxes,1); tiparent=zeros(nboxes,1); tnchild=zeros(nboxes,1); +tichild=zeros(mc,nboxes); tiflag=zeros(nboxes,1); iboxtocurbox=zeros(nboxes,1); +tcenters=zeros(ndim,nboxes); + +for ilev = 0:nlevels + tladdr(1,ilev+idx1) = laddr(1,ilev+idx1); + tladdr(2,ilev+idx1) = laddr(2,ilev+idx1); +end + +tcenters = centers; +tilevel = ilevel; +tiparent = iparent; +tnchild = nchild; +tichild = ichild; +tcoeffs = coeffs; + +for ibox=1:nboxes + tiflag(ibox) = iflag(ibox); +end + +for ilev = 0:1 + for ibox = laddr(1,ilev+idx1):laddr(2,ilev+idx1) + iboxtocurbox(ibox) = ibox; + end +end + +ilevptr=zeros(nlevels+1,1); ilevptr2=zeros(nlevels,1); + +ilevptr(2) = laddr(1,2+idx1); + +for ilev=2:nlevels + nblev = laddr(2,ilev+idx1)-laddr(1,ilev+idx1)+1; % original num of boxes at ilev + ilevptr2(ilev) = ilevptr(ilev) + nblev; + nblev = laddrtail(2,ilev+idx1)-laddrtail(1,ilev+idx1)+1; % additional number of boxes at ilev due to refinement + ilevptr(ilev+1) = ilevptr2(ilev) + nblev; +end + +curbox = laddr(1,2+idx1); +ishuffle = zeros(nboxes,1); +for ilev=2:nlevels + laddr(1,ilev+idx1) = curbox; + for ibox = tladdr(1,ilev+idx1):tladdr(2,ilev+idx1) + ilevel(curbox) = tilevel(ibox); + nchild(curbox) = tnchild(ibox); + ishuffle(ibox) = curbox; % ibox info copied to curbox + for k=1:ndim + centers(k,curbox) = tcenters(k,ibox); + end + coeffs{curbox} = tcoeffs{ibox}; + iflag(curbox) = tiflag(ibox); + iboxtocurbox(ibox) = curbox; + + curbox = curbox + 1; + end + for ibox = laddrtail(1,ilev+idx1):laddrtail(2,ilev+idx1) + ilevel(curbox) = tilevel(ibox); + ishuffle(ibox) = curbox; % ibox info copied to curbox + for k=1:ndim + centers(k,curbox) = tcenters(k,ibox); + end + coeffs{curbox} = tcoeffs{ibox}; + nchild(curbox) = tnchild(ibox); + iflag(curbox) = tiflag(ibox); + iboxtocurbox(ibox) = curbox; + + curbox = curbox + 1; + end + laddr(2,ilev+idx1) = curbox-1; +end + +for ibox=1:nboxes + if(tiparent(ibox)==-1), iparent(iboxtocurbox(ibox)) = -1; end + if(tiparent(ibox)>0) + iparent(iboxtocurbox(ibox)) = iboxtocurbox(tiparent(ibox)); end + for i=1:mc + if(tichild(i,ibox)==-1), ichild(i,iboxtocurbox(ibox)) = -1; end + if(tichild(i,ibox)>0) + ichild(i,iboxtocurbox(ibox)) = iboxtocurbox(tichild(i,ibox)); end + end +end + +end + +function isgn = get_child_box_sign(ndim,isgn) + +mc = 2^ndim; +for j=1:ndim + isgn(j,1)=-1; +end + +for j=1:ndim + for i=1:2^(j-1):mc + if (i>1), isgn(j,i)=-isgn(j,i-2^(j-1)); end + for k=1:2^(j-1)-1 + isgn(j,i+k)=isgn(j,i); + end + end +end + +end \ No newline at end of file diff --git a/@treefun2/coeffs2checkvals.m b/@treefun2/coeffs2checkvals.m new file mode 100644 index 0000000..536b2b4 --- /dev/null +++ b/@treefun2/coeffs2checkvals.m @@ -0,0 +1,29 @@ +function vals = coeffs2checkvals(coeffs,x,y) +% +% + +persistent Evalx Evaly +[p,~,nd] = size(coeffs); +ncheckpts = numel(x); % hopefully not too large... + +Evalx = ones(ncheckpts, p); +Evaly = ones(ncheckpts, p); +Evalx(:,2) = x(:); +Evaly(:,2) = y(:); +for k=3:p + Evalx(:,k) = 2*x(:).*Evalx(:,k-1)-Evalx(:,k-2); + Evaly(:,k) = 2*y(:).*Evaly(:,k-1)-Evaly(:,k-2); +end +% coeffs to value map +% vals = zeros(nd,ncheckpts); +% for k=1:ncheckpts +% tmp1 = permute(tensorprod(Evalx(k,:),coeffs,2,1),[2 1 3]); +% vals(:,k) = squeeze(permute(tensorprod(Evaly(k,:),tmp1,2,1),[2 1 3])); +% end +vals = zeros(nd,ncheckpts); +for k=1:ncheckpts + tmp1 = permute(tensorprod(Evaly(k,:),coeffs,2,1),[2 1 3]); + vals(:,k) = squeeze(permute(tensorprod(Evalx(k,:),tmp1,2,1),[2 1 3])); +end + +end \ No newline at end of file diff --git a/@treefun2/treefun2.m b/@treefun2/treefun2.m index f5c53ae..5683885 100644 --- a/@treefun2/treefun2.m +++ b/@treefun2/treefun2.m @@ -141,6 +141,7 @@ % Now do level restriction if ( opts.balance ) f = balance(f); + % f = balancef(f); else % Do a cumulative sum in reverse to correct the heights for k = length(f.id):-1:1 @@ -228,6 +229,7 @@ vals = coeffs2vals(coeffs); vals = coeffs2refvals(coeffs); refvals = chebvals2refvals(chebvals); + checkvals = coeffs2checkvals(coeffs,x,y); end From 3f6bd3ae5cfb23e38bed107b3bcb05e10b8a809d Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Sat, 4 May 2024 21:39:20 -0400 Subject: [PATCH 12/15] treefun3 lr tree, fortran way... also prototype col,row,dep for morton way... --- @treefun3/balancef.m | 824 ++++++++++++++++++++++++++++++++++++++++ @treefun3/coeffs2vals.m | 23 ++ @treefun3/plot.m | 5 + @treefun3/refineBox.m | 32 +- @treefun3/treefun3.m | 59 ++- 5 files changed, 924 insertions(+), 19 deletions(-) create mode 100644 @treefun3/balancef.m create mode 100644 @treefun3/coeffs2vals.m diff --git a/@treefun3/balancef.m b/@treefun3/balancef.m new file mode 100644 index 0000000..9c86b8c --- /dev/null +++ b/@treefun3/balancef.m @@ -0,0 +1,824 @@ +function g = balancef(f) +% fortran vol_tree_fix_lr +% assume square boxes? therefore scale is computed from dom(1:2) +% nd needs to be fixed later... +% flatNeighbors and leafNeighbors also need to be updated in the future +% + +nd = 1; % +dpars = zeros(1000,1); +ipars = zeros(100,1); +zpars = zeros(10,1); + +if ( isempty(f) ) + g = f; + return +end + +% dada +dom = f.domain(:,1)'; +scale = (dom(2)-dom(1))/2; +norder = f.n; +ndim = numel(dom(1:end/2)); + +% convert to vol_tree_build output format +iper = 0; +npbox = norder^ndim; +grid = zeros(ndim,norder^2); +nbmax = 2^ndim*f.id(end); % is 2^ndim enough, since some box needs to be refined more than once... +nlmax = f.height(1); % is this ok? +centers0 = 1/2*(f.domain(1:2:end,:)+f.domain(2:2:end,:)); +centers = zeros(ndim,nbmax); centers(:,1:size(centers0,2)) = centers0; +nlevels = f.height(1); +nboxes = f.id(end); +boxsize = 2*scale./2.^(0:nlmax); +laddr = zeros(2,nlmax+1); +for ilev=0:nlmax + laddr(1,ilev+1) = sum(f.level0); +ichild = zeros(2^ndim,nbmax); ichild(:,1:nboxes) = f.children; ichild(ichild==0) = -1; % -1 convention in fortran tree + +% this does not belong to fortran tree, but needed for treefun plot +coeffs = cell(1,nbmax); coeffs(1:nboxes) = f.coeffs(1:nboxes); + +% call computecoll to get vol_tree_fix_lr coll info +nnbors0 = zeros(nboxes,1); nbors0 = zeros(3^ndim,nboxes); +[nnbors0,nbors0] = ... + computecoll(ndim,nlevels,nboxes,laddr,boxsize,... + centers(:,1:nboxes),iparent(1:nboxes),nchild(1:nboxes),ichild(:,1:nboxes),iper,... + nnbors0,nbors0); +nnbors = zeros(nbmax,1); nbors = zeros(3^ndim,nbmax); +nnbors(1:nboxes) = nnbors0; nbors(:,1:nboxes) = nbors0; + +% now fix lr (with coeffs, which is different from) +[centers,nboxes,laddr,ilevel,iparent,nchild,ichild,nnbors,nbors,coeffs] = ... + vol_tree_fix_lr_treefun(ndim,iper,nd,dpars,zpars,ipars,... + norder,npbox,grid,centers,nlevels,nboxes,boxsize,... + nbmax,nlmax,laddr,ilevel,iparent,nchild,ichild,nnbors,nbors,... + coeffs); + +centers = centers(:,1:nboxes); +ilevel = ilevel(1:nboxes); +iparent = iparent(1:nboxes); +nchild = nchild(1:nboxes); +ichild = ichild(:,1:nboxes); +nnbors = nnbors(1:nboxes); +nbors = nbors(:,1:nboxes); +coeffs = coeffs(1:nboxes); + +% convert back to treefun format, try to follow treefun naming convention +g = f; +scl = boxsize(ilevel+1); % x,y,z all the same +g.domain = zeros(2*ndim,nboxes); +g.domain(1:2:end,:) = centers - 1/2*scl; +g.domain(2:2:end,:) = centers + 1/2*scl; +g.level = ilevel(:)'; +g.id = 1:nboxes; +g.parent = iparent(:)'; +g.children = ichild; +g.height = zeros(1,nboxes); +g.height(nchild>0) = 1; +for k = length(g.id):-1:1 + if ( ~(g.height(k)==0) ) + g.height(k) = 1 + max(g.height(g.children(:,k))); + end +end +% redo some stuff +g.col = uint64(zeros(1,nboxes)); +g.row = uint64(zeros(1,nboxes)); +g.dep = uint64(zeros(1,nboxes)); +% g.morton = uint64(zeros(1,nboxes)); +mc = 2^ndim; +for ilev = 0:nlevels-1 + for ibox = laddr(1,ilev+1):laddr(2,ilev+1) + if nchild(ibox) > 0 + if ndim == 3 % column row stuff + j = 1; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox); + g.row(jbox) = 2*g.row(ibox); + g.dep(jbox) = 2*g.dep(ibox); + % g.morton(jbox) = cartesian2morton(g.col(jbox), g.row(jbox)); + j = 2; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox) + 1; + g.row(jbox) = 2*g.row(ibox); + g.dep(jbox) = 2*g.dep(ibox); + % g.morton(jbox) = cartesian2morton(g.col(jbox), g.row(jbox)); + j = 3; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox); + g.row(jbox) = 2*g.row(ibox) + 1; + g.dep(jbox) = 2*g.dep(ibox); + % g.morton(jbox) = cartesian2morton(g.col(jbox), g.row(jbox)); + j = 4; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox) + 1; + g.row(jbox) = 2*g.row(ibox) + 1; + g.dep(jbox) = 2*g.dep(ibox); + % g.morton(jbox) = cartesian2morton(g.col(jbox), g.row(jbox)); + j = 5; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox); + g.row(jbox) = 2*g.row(ibox); + g.dep(jbox) = 2*g.dep(ibox) + 1; + + j = 6; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox) + 1; + g.row(jbox) = 2*g.row(ibox); + g.dep(jbox) = 2*g.dep(ibox) + 1; + + j = 7; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox); + g.row(jbox) = 2*g.row(ibox) + 1; + g.dep(jbox) = 2*g.dep(ibox) + 1; + + j = 8; + jbox = g.children(j,ibox); + g.col(jbox) = 2*g.col(ibox) + 1; + g.row(jbox) = 2*g.row(ibox) + 1; + g.dep(jbox) = 2*g.dep(ibox) + 1; + + end + end + end +end +g.coeffs = coeffs; +end + +function [centers,nboxes,laddr,ilevel,iparent,nchild,ichild,nnbors,nbors,coeffs] = vol_tree_fix_lr_treefun(ndim,iperiod,nd,dpars,zpars,ipars, ... + norder,npbox,grid,centers,nlevels,nboxes,boxsize, ... + nbmax,nlmax,laddr,ilevel,iparent,nchild,ichild,nnbors,nbors, ... + coeffs) + +laddrtail=zeros(2,nlmax+1); +% boxsize(0:nlmax),laddr(2,0:nlmax),laddrtail(2,0:nlmax) +idx1 = 1; +% +bs0=boxsize(0+idx1); +% +mc=2^ndim; +mnbors=3^ndim; + +iflag = zeros(nbmax,1); iflag2 = zeros(nbmax,1); + +for i=1:nboxes + iflag(i) = 0; +end + +% Flag boxes that violate level restriction by "1" Violatioin refers to any +% box that is directly touching a box that is more than one level finer +% Method: +% 1) Carry out upward pass. For each box B, look at the colleagues of B's +% grandparent +% 2) See if any of those colleagues are childless and in contact with B. +% +% Note that we only need to get up to level two, as we will not find a +% violation at level 0 and level 1 For such boxes, we set iflag(i) = 1 +% figure(1), clf, +for ilev=nlevels:-1:2 + distest = 1.05d0*(boxsize(ilev-1+idx1) + boxsize(ilev-2+idx1))/2.0d0; + for ibox = laddr(1,ilev+idx1):laddr(2,ilev+idx1) % look at current box + idad = iparent(ibox); % its parent box + igranddad = iparent(idad); % its grand parent box + + for i=1:nnbors(igranddad) % look at neighbor of grand parent + jbox = nbors(i,igranddad); + if((nchild(jbox)==0) && (iflag(jbox)==0)) % if neighbor of grand parent has no child, and has not been flagged + ict = 0; % count overlapping dimension between ibox & its grand parent's neighbor + for k=1:ndim + dis = centers(k,jbox) - centers(k,idad); + if (iperiod==1) + dp1=dis+bs0; + dm1=dis-bs0; + if (abs(dis).gt.abs(dp1)), dis=dp1; end + if (abs(dis).gt.abs(dm1)), dis=dm1; end + end + if(abs(dis)<=distest), ict = ict + 1; end + end + if(ict==ndim) + iflag(jbox) = 1; + % figure(1), + % plotibox(centers(:,ibox),boxsize(ilev-1+idx1)/2); + % plotibox(centers(:,jbox),4*boxsize(ilev-1+idx1)/2); + % axis equal + end + end + end + end +end + +% Find all boxes that need to be given a flag+ A flag+ box will be denoted +% by setting iflag(box) = 2 This refers to any box that is not already +% flagged and is bigger than and is contacting a flagged box or another box +% that has already been given a flag +. It is found by performing an upward +% pass and looking at the flagged box's parents colleagues and a flag+ +% box's parents colleagues and seeing if they are childless and present the +% case where a bigger box is contacting a flagged or flag+ box. +% figure(2), clf, +%% if secondary violator, 1st send it downstairs during Step 4, then wait to be processed in Step 5 or no need to process at all, seems to be later, from the "worst-case" example +for ilev = nlevels:-1:1 + distest = 1.05d0*(boxsize(ilev+idx1) + boxsize(ilev-1+idx1))/2.0d0; + for ibox = laddr(1,ilev+idx1):laddr(2,ilev+idx1) + if((iflag(ibox)==1) || (iflag(ibox)==2)) % look at current box + idad = iparent(ibox); % its parent box + for i=1:nnbors(idad) % look at neighbor of parent + jbox = nbors(i,idad); + if((nchild(jbox)==0) && (iflag(jbox)==0)) % if neighbor of parent has no child, and has not been flagged + ict = 0; % count overlapping dimension between ibox & its parent's neighbor + for k=1:ndim + dis = centers(k,jbox) - centers(k,ibox); + if (iperiod==1) + dp1=dis+bs0; + dm1=dis-bs0; + if (abs(dis)>abs(dp1)), dis=dp1; end + if (abs(dis)>abs(dm1)), dis=dm1; end + end + if(abs(dis)<=distest), ict = ict + 1; end + end + if(ict==ndim) + iflag(jbox) = 2; + % figure(2), + % plotibox(centers(:,ibox),boxsize(ilev-1+idx1)/2); + % plotibox(centers(:,jbox),4*boxsize(ilev-1+idx1)/2); + % axis equal + end + end + end + end + end +end + +% Subdivide all flag and flag+ boxes. Flag all the children of flagged +% boxes as flag++. Flag++ boxes are denoted by setting iflag(box) = 3. The +% flag++ boxes need to be checked later to see which of them need further +% refinement. While creating new boxes, we will need to update all the tree +% structures as well. Note that all the flagged boxes live between levels 1 +% and nlevels - 2. We process the boxes via a downward pass. We first +% determine the number of boxes that are going to be subdivided at each +% level and everything else accordingly +for ilev = 0:nlevels + laddrtail(1,ilev+idx1) = 0; + laddrtail(2,ilev+idx1) = -1; +end + +nboxes0 = nboxes; +%% Frank's thesis, Appendix A, Step 4, divide all of the primary and secondary violators once. I think +% figure out iflag = 3 case +for ilev = 1:nlevels-2 + + laddrtail(1,ilev+1+idx1) = nboxes+1; + + nbloc = laddr(2,ilev+idx1)-laddr(1,ilev+idx1)+1; + + [iflag,centers,nboxes,ilevel,iparent,nchild,ichild,coeffs] = vol_tree_refine_boxes_flag_treefun(ndim,iflag,nd,npbox,... + dpars,zpars,ipars,grid,nbmax,laddr(1,ilev+idx1),nbloc, ... + centers,boxsize(ilev+1+idx1),nboxes,ilev,ilevel,iparent,nchild,ichild, ... + coeffs); + + laddrtail(2,ilev+1+idx1) = nboxes; + +end + +ishuffle1 = zeros(nboxes,1); +centerstmp = centers(:,1:nboxes); ileveltmp = ilevel(1:nboxes); iparenttmp = iparent(1:nboxes); nchildtmp = nchild(1:nboxes); ichildtmp = ichild(:,1:nboxes); iflagtmp = iflag(1:nboxes); +coeffstmp = coeffs(1:nboxes); +[centerstmp,laddr,ileveltmp,ishuffle1,iparenttmp,nchildtmp,ichildtmp,coeffstmp,iflagtmp] = ... + vol_tree_reorg_treefun(ndim,nboxes,nd,npbox,centerstmp,nlevels,laddr,... + laddrtail,ileveltmp,ishuffle1,iparenttmp,nchildtmp,ichildtmp,coeffstmp,iflagtmp); +centers(:,1:nboxes) = centerstmp; ilevel(1:nboxes) = ileveltmp; iparent(1:nboxes) = iparenttmp; nchild(1:nboxes) = nchildtmp; ichild(:,1:nboxes) = ichildtmp; iflag(1:nboxes) = iflagtmp; +coeffs(1:nboxes) = coeffstmp; + +nnborstmp = nnbors(1:nboxes); nborstmp = nbors(:,1:nboxes); +[nnborstmp,nborstmp] = computecoll(ndim,nlevels,nboxes,laddr,boxsize,... + centers(:,1:nboxes),iparent(1:nboxes),nchild(1:nboxes),... + ichild(:,1:nboxes),iperiod,nnborstmp,nborstmp); +nnbors(1:nboxes) = nnborstmp; nbors(:,1:nboxes) = nborstmp; + + +% Processing of flag and flag+ boxes is done Start processing flag++ boxes. +% We will use a similar strategy as before. We keep checking the flag++ +% boxes that require subdivision if they still violate the level +% restriction criterion, create the new boxes, append them to the end of +% the list to begin with and in the end reorganize the tree structure. We +% shall accomplish this via a downward pass as new boxes that get added in +% the downward pass will also be processed simultaneously. We shall +% additionally also need to keep on updating the colleague information as +% we proceed in the downward pass +% +% Reset the flags array to remove all the flag and flag+ cases. This is to +% ensure reusability of the subdivide_flag routine to handle the flag++ case + +for ibox=1:nboxes + if(iflag(ibox)~=3), iflag(ibox) = 0; end +end + +for ilev = 0:nlevels + laddrtail(1,ilev+idx1) = 0; + laddrtail(2,ilev+idx1) = -1; +end + +% boxsize(0:nlmax),laddr(2,0:nlmax),laddrtail(2,0:nlmax) +%% Frank's thesis, Appendix A, Step 5, at each level, for a descendant of a primary violator, test whether it is still in violation +for ilev = 2:nlevels-2 + + % Step 1 + iflagtmp = iflag(1:nboxes); + iflagtmp = vol_updateflags(ndim,iperiod,ilev,nboxes,nlevels, ... + laddr,nchild(1:nboxes),ichild(:,1:nboxes),nnbors(1:nboxes),nbors(:,1:nboxes),centers(:,1:nboxes),boxsize,iflagtmp); + iflag(1:nboxes) = iflagtmp; + + iflagtmp = iflag(1:nboxes); + iflagtmp = vol_updateflags(ndim,iperiod,ilev,nboxes,nlevels, ... + laddrtail,nchild(1:nboxes),ichild(:,1:nboxes),nnbors(1:nboxes),nbors(:,1:nboxes),centers(:,1:nboxes),boxsize,iflagtmp); + iflag(1:nboxes) = iflagtmp; + + % Step 2 + laddrtail(1,ilev+1+idx1) = nboxes + 1; + + nbloc = laddr(2,ilev+idx1)-laddr(1,ilev+idx1)+1; + [iflag,centers,nboxes,ilevel,iparent,nchild,ichild,coeffs] = vol_tree_refine_boxes_flag_treefun(ndim,iflag,nd,npbox, ... + dpars,zpars,ipars,grid,nbmax,laddr(1,ilev+idx1),nbloc, ... + centers,boxsize(ilev+1+idx1),nboxes,ilev,ilevel,iparent,nchild,ichild, ... + coeffs); + + nbloc = laddrtail(2,ilev+idx1)-laddrtail(1,ilev+idx1)+1; + [iflag,centers,nboxes,ilevel,iparent,nchild,ichild,coeffs] = vol_tree_refine_boxes_flag_treefun(ndim,iflag,nd,npbox, ... + dpars,zpars,ipars,grid,nbmax,laddrtail(1,ilev+idx1),nbloc, ... + centers,boxsize(ilev+1+idx1),nboxes,ilev,ilevel,iparent,nchild,ichild, ... + coeffs); + laddrtail(2,ilev+1+idx1) = nboxes; + + % Step 3 + for ibox = laddrtail(1,ilev+1+idx1):laddrtail(2,ilev+1+idx1) + nnbors(ibox) = 0; + idad = iparent(ibox); + for i=1:nnbors(idad) + jbox = nbors(i,idad); + for j=1:mc + kbox = ichild(j,jbox); + if(kbox>0) + ifnbor=1; + for k=1:ndim + dis=centers(k,kbox)-centers(k,ibox); + if (iperiod==1) + dp1=dis+bs0; + dm1=dis-bs0; + if (abs(dis)>abs(dp1)), dis=dp1; end + if (abs(dis)>abs(dm1)), dis=dm1; end + end + if((abs(dis)>1.05*boxsize(ilev+1+idx1))) + ifnbor=0; + break + end + end + if (ifnbor==1) + nnbors(ibox) = nnbors(ibox)+1; + nbors(nnbors(ibox),ibox) = kbox; + end + end + end + end + % End of computing colleagues of box i + end + +end + +ishuffle2 = zeros(nboxes,1); +centerstmp = centers(:,1:nboxes); ileveltmp = ilevel(1:nboxes); iparenttmp = iparent(1:nboxes); nchildtmp = nchild(1:nboxes); ichildtmp = ichild(:,1:nboxes); iflagtmp = iflag(1:nboxes); +coeffstmp = coeffs(1:nboxes); +[centerstmp,laddr,ileveltmp,ishuffle2,iparenttmp,nchildtmp,ichildtmp,coeffstmp,iflagtmp] = ... + vol_tree_reorg_treefun(ndim,nboxes,nd,npbox,centerstmp,nlevels,laddr,... + laddrtail,ileveltmp,ishuffle2,iparenttmp,nchildtmp,ichildtmp,coeffstmp,iflagtmp); +centers(:,1:nboxes) = centerstmp; ilevel(1:nboxes) = ileveltmp; iparent(1:nboxes) = iparenttmp; nchild(1:nboxes) = nchildtmp; ichild(:,1:nboxes) = ichildtmp; iflag(1:nboxes) = iflagtmp; +coeffs(1:nboxes) = coeffstmp; + +nnborstmp = nnbors(1:nboxes); nborstmp = nbors(:,1:nboxes); +[nnborstmp,nborstmp] = computecoll(ndim,nlevels,nboxes,laddr,boxsize,... + centers(:,1:nboxes),iparent(1:nboxes),nchild(1:nboxes),... + ichild(:,1:nboxes),iperiod,nnborstmp,nborstmp); +nnbors(1:nboxes) = nnborstmp; nbors(:,1:nboxes) = nborstmp; + +end + +function [iflag,centers,nbctr,ilevel,iparent,nchild,ichild,coeffs] = vol_tree_refine_boxes_flag_treefun(ndim,iflag,nd,npbox,dpars,zpars,ipars,grid,nboxes,ifirstbox,nbloc,centers,bs,nbctr,nlctr,ilevel,iparent,nchild,ichild,coeffs) + +% myfun = @(x,y,z) sin(x).*cos(y).*sin(z)+exp(x.*y.*z); +% myvals = myfun(2*(xx0-1/2),2*(yy0-1/2),2*(zz0-1/2)); +% mycoeffs = treefun3.vals2coeffs(myvals); +% mycvals1_2 = myfun(cxx1,cyy1,czz1); +% mycvals1 = reshape(treefun3.coeffs2checkvals(mycoeffs,cxx1(:),cyy1(:),czz1(:)),[norder norder norder]); +% mycvals2 = reshape(treefun3.coeffs2checkvals(mycoeffs,cxx2(:),cyy2(:),czz2(:)),[norder norder norder]); +% mycvals3 = reshape(treefun3.coeffs2checkvals(mycoeffs,cxx3(:),cyy3(:),czz3(:)),[norder norder norder]); +% mycvals4 = reshape(treefun3.coeffs2checkvals(mycoeffs,cxx4(:),cyy4(:),cyy4(:)),[norder norder norder]); +% mycvals5 = reshape(treefun3.coeffs2checkvals(mycoeffs,cxx5(:),cyy5(:),czz5(:)),[norder norder norder]); +% mycvals6 = reshape(treefun3.coeffs2checkvals(mycoeffs,cxx6(:),cyy6(:),czz6(:)),[norder norder norder]); +% mycvals7 = reshape(treefun3.coeffs2checkvals(mycoeffs,cxx7(:),cyy7(:),czz7(:)),[norder norder norder]); +% mycvals8 = reshape(treefun3.coeffs2checkvals(mycoeffs,cxx8(:),cyy8(:),cyy8(:)),[norder norder norder]); +% +% figure(2), clf, scatter3(2*(xx0(:)-1/2),2*(yy0(:)-1/2),2*(zz0(:)-1/2),[],log10(abs(vals(:)))) +% hold on, scatter3(cxx1(:),cyy1(:),czz1(:),[],log10(abs(cvals1(:)))) +% hold on, scatter3(cxx2(:),cyy2(:),czz2(:),[],log10(abs(cvals2(:)))) +% hold on, scatter3(cxx3(:),cyy3(:),czz3(:),[],log10(abs(cvals3(:)))) +% hold on, scatter3(cxx4(:),cyy4(:),czz4(:),[],log10(abs(cvals4(:)))) +% hold on, scatter3(cxx5(:),cyy5(:),czz5(:),[],log10(abs(cvals5(:)))) +% hold on, scatter3(cxx6(:),cyy6(:),czz6(:),[],log10(abs(cvals6(:)))) +% hold on, scatter3(cxx7(:),cyy7(:),czz7(:),[],log10(abs(cvals7(:)))) +% hold on, scatter3(cxx8(:),cyy8(:),czz8(:),[],log10(abs(cvals8(:)))) + +% additional +persistent xx0 yy0 xxx0 yyy0 zz0 zzz0 nstored +norder=round((npbox)^(1/ndim)); +n=norder; + +if ndim == 3 + nalias = n; + nrefpts = n; % Sample at equispaced points to test error + + if ( isempty(xx0) || isempty(xxx0) || n ~= nstored ) + nstored = n; + x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; + [xx0, yy0, zz0] = ndgrid(x0); + [xxx0, yyy0, zzz0] = ndgrid(linspace(0, 1, nrefpts)); + end + sclx = 2*bs; + scly = 2*bs; + sclz = 2*bs; +end + +% +isgn=zeros(ndim,2^ndim); +isgn=get_child_box_sign(ndim,isgn); + +mc=2^ndim; + +ilastbox = ifirstbox+nbloc-1; + +bsh = bs/2; + +isum=zeros(nbloc,1); + +isum = cumsum(iflag(ifirstbox:(ifirstbox+nbloc-1))>0); % there are iflag = 2 entries + +for i = 1:nbloc + ibox = ifirstbox + i-1; + if(iflag(ibox)>0) + nbl = nbctr + (isum(i)-1)*mc; + nchild(ibox) = mc; + % additional + if ndim == 3 + xx = sclx*xx0 + (centers(1,ibox)-1/2*sclx); + yy = scly*yy0 + (centers(2,ibox)-1/2*scly); + zz = sclz*zz0 + (centers(3,ibox)-1/2*sclz); + vals = treefun3.coeffs2vals(coeffs{ibox}); + % split into eight child boxes + dom = [-1 1 -1 1 -1 1]; + xmid = mean(dom(1:2)); + ymid = mean(dom(3:4)); + zmid = mean(dom(5:6)); + j=1; + jbox = nbl+j; + cdom1 = [dom(1) xmid dom(3) ymid dom(5) zmid]; + csclx1 = diff(cdom1(1:2)); + cscly1 = diff(cdom1(3:4)); + csclz1 = diff(cdom1(5:6)); + cxx1 = csclx1*xx0 + cdom1(1); + cyy1 = cscly1*yy0 + cdom1(3); + czz1 = csclz1*zz0 + cdom1(5); + cvals1 = reshape(treefun3.coeffs2checkvals(coeffs{ibox},cxx1(:),cyy1(:),czz1(:)),[norder norder norder]); + ccoeffs1 = treefun3.vals2coeffs(cvals1); + coeffs{jbox} = ccoeffs1; + j=2; + jbox = nbl+j; + cdom2 = [xmid dom(2) dom(3) ymid dom(5) zmid]; + csclx2 = diff(cdom2(1:2)); + cscly2 = diff(cdom2(3:4)); + csclz2 = diff(cdom2(5:6)); + cxx2 = csclx2*xx0 + cdom2(1); + cyy2 = cscly2*yy0 + cdom2(3); + czz2 = csclz2*zz0 + cdom2(5); + cvals2 = reshape(treefun3.coeffs2checkvals(coeffs{ibox},cxx2(:),cyy2(:),czz2(:)),[norder norder norder]); + ccoeffs2 = treefun3.vals2coeffs(cvals2); + coeffs{jbox} = ccoeffs2; + j=3; + jbox = nbl+j; + cdom3 = [dom(1) xmid ymid dom(4) dom(5) zmid]; + csclx3 = diff(cdom3(1:2)); + cscly3 = diff(cdom3(3:4)); + csclz3 = diff(cdom3(5:6)); + cxx3 = csclx3*xx0 + cdom3(1); + cyy3 = cscly3*yy0 + cdom3(3); + czz3 = csclz3*zz0 + cdom3(5); + cvals3 = reshape(treefun3.coeffs2checkvals(coeffs{ibox},cxx3(:),cyy3(:),czz3(:)),[norder norder norder]); + ccoeffs3 = treefun3.vals2coeffs(cvals3); + coeffs{jbox} = ccoeffs3; + j=4; + jbox = nbl+j; + cdom4 = [xmid dom(2) ymid dom(4) dom(5) zmid]; + csclx4 = diff(cdom4(1:2)); + cscly4 = diff(cdom4(3:4)); + csclz4 = diff(cdom4(5:6)); + cxx4 = csclx4*xx0 + cdom4(1); + cyy4 = cscly4*yy0 + cdom4(3); + czz4 = csclz4*zz0 + cdom4(5); + cvals4 = reshape(treefun3.coeffs2checkvals(coeffs{ibox},cxx4(:),cyy4(:),czz4(:)),[norder norder norder]); + ccoeffs4 = treefun3.vals2coeffs(cvals4); + coeffs{jbox} = ccoeffs4; + j=5; + jbox = nbl+j; + cdom5 = [dom(1) xmid dom(3) ymid zmid dom(6)]; + csclx5 = diff(cdom5(1:2)); + cscly5 = diff(cdom5(3:4)); + csclz5 = diff(cdom5(5:6)); + cxx5 = csclx5*xx0 + cdom5(1); + cyy5 = cscly5*yy0 + cdom5(3); + czz5 = csclz5*zz0 + cdom5(5); + cvals5 = reshape(treefun3.coeffs2checkvals(coeffs{ibox},cxx5(:),cyy5(:),czz5(:)),[norder norder norder]); + ccoeffs5 = treefun3.vals2coeffs(cvals5); + coeffs{jbox} = ccoeffs5; + j=6; + jbox = nbl+j; + cdom6 = [xmid dom(2) dom(3) ymid zmid dom(6)]; + csclx6 = diff(cdom6(1:2)); + cscly6 = diff(cdom6(3:4)); + csclz6 = diff(cdom6(5:6)); + cxx6 = csclx6*xx0 + cdom6(1); + cyy6 = cscly6*yy0 + cdom6(3); + czz6 = csclz6*zz0 + cdom6(5); + cvals6 = reshape(treefun3.coeffs2checkvals(coeffs{ibox},cxx6(:),cyy6(:),czz6(:)),[norder norder norder]); + ccoeffs6 = treefun3.vals2coeffs(cvals6); + coeffs{jbox} = ccoeffs6; + j=7; + jbox = nbl+j; + cdom7 = [dom(1) xmid ymid dom(4) zmid dom(6)]; + csclx7 = diff(cdom7(1:2)); + cscly7 = diff(cdom7(3:4)); + csclz7 = diff(cdom7(5:6)); + cxx7 = csclx7*xx0 + cdom7(1); + cyy7 = cscly7*yy0 + cdom7(3); + czz7 = csclz7*zz0 + cdom7(5); + cvals7 = reshape(treefun3.coeffs2checkvals(coeffs{ibox},cxx7(:),cyy7(:),czz7(:)),[norder norder norder]); + ccoeffs7 = treefun3.vals2coeffs(cvals7); + coeffs{jbox} = ccoeffs7; + j=8; + jbox = nbl+j; + cdom8 = [xmid dom(2) ymid dom(4) zmid dom(6)]; + csclx8 = diff(cdom8(1:2)); + cscly8 = diff(cdom8(3:4)); + csclz8 = diff(cdom8(5:6)); + cxx8 = csclx8*xx0 + cdom8(1); + cyy8 = cscly8*yy0 + cdom8(3); + czz8 = csclz8*zz0 + cdom8(5); + cvals8 = reshape(treefun3.coeffs2checkvals(coeffs{ibox},cxx8(:),cyy8(:),czz8(:)),[norder norder norder]); + ccoeffs8 = treefun3.vals2coeffs(cvals8); + coeffs{jbox} = ccoeffs8; + % + coeffs{ibox} = []; + end + for j=1:mc + jbox = nbl+j; + for k=1:ndim + centers(k,jbox) = centers(k,ibox)+isgn(k,j)*bsh; + end + iparent(jbox) = ibox; + nchild(jbox) = 0; + for l=1:mc + ichild(l,jbox) = -1; + end + ichild(j,ibox) = jbox; + ilevel(jbox) = nlctr+1; + if(iflag(ibox)==1), iflag(jbox) = 3; end + if(iflag(ibox)==2), iflag(jbox) = 0; end + end + end +end + +if(nbloc>0), nbctr = nbctr + isum(nbloc)*mc; end + +end + +function iflag = vol_updateflags(ndim,iperiod,curlev,nboxes,nlevels,laddr,nchild,ichild,nnbors,nbors,centers,boxsize,iflag) +% +% + +idx1 = 1; +bs0=boxsize(0+idx1); + +mc=2^ndim; +distest = 1.05d0*(boxsize(curlev+idx1) + boxsize(curlev+1+idx1))/2.0d0; + +for ibox = laddr(1,curlev+idx1):laddr(2,curlev+idx1) + if(iflag(ibox)==3) + iflag(ibox) = 0; + for i=1:nnbors(ibox) + jbox = nbors(i,ibox); + + for j=1:mc + kbox = ichild(j,jbox); + if(kbox>0) + if(nchild(kbox)>0) + ict = 0; + for k=1:ndim + dis = centers(k,kbox) - centers(k,ibox); + if (iperiod==1) + dp1=dis+bs0; + dm1=dis-bs0; + if (abs(dis)>abs(dp1)), dis=dp1; end + if (abs(dis)>abs(dm1)), dis=dm1; end + end + if(abs(dis)<=distest), ict = ict + 1; end + end + if(ict==ndim) + iflag(ibox) = 1; + break; + end + end + end + end + if iflag(ibox) == 1 + break; + end + end + % 1111 continue + end +end + +end + +function [nnbors,nbors] = computecoll(ndim,nlevels,nboxes,laddr,boxsize,centers,iparent,nchild,ichild,iperiod,nnbors,nbors) + +% laddr(2,0:nlevels) +% boxsize(0:nlevels) + +idx1 = 1; + +bs0=boxsize(0+idx1); + +mc= 2^ndim; +mnbors=3^ndim; + +for i=1:nboxes + nnbors(i) = 0; + for j=1:mnbors + nbors(j,i) = -1; + end +end + +nnbors(1) = 1; +nbors(1,1) = 1; +for ilev = 1:nlevels + ifirstbox = laddr(1,ilev+idx1); + ilastbox = laddr(2,ilev+idx1); + + for ibox = ifirstbox:ilastbox + dad = iparent(ibox); + for i=1:nnbors(dad) + jbox = nbors(i,dad); + for j=1:mc + kbox = ichild(j,jbox); + if(kbox>0) + ifnbor=1; + for k=1:ndim + dis=abs(centers(k,kbox)-centers(k,ibox)); + if (iperiod==1) + dp1=bs0-dis; + if (dp11.05*boxsize(ilev+idx1)) + ifnbor=0; + break + end + end + + if(ifnbor==1) + nnbors(ibox) = nnbors(ibox)+1; + nbors(nnbors(ibox),ibox) = kbox; + end + end + end + end + end +end + +end + +function [centers,laddr,ilevel,ishuffle,iparent,nchild,ichild,coeffs,iflag] = vol_tree_reorg_treefun(ndim,nboxes,nd,npbox,centers,nlevels,laddr,laddrtail,ilevel,ishuffle,iparent,nchild,ichild,coeffs,iflag) +% +% + +% laddrtail(2,0:nlevels) +% laddr(2,0:nlevels) +% tladdr(2,0:nlevels) + +tladdr = zeros(2,nlevels+1); + +idx1 = 1; + +mc=2^ndim; + +tilevel=zeros(nboxes,1); tiparent=zeros(nboxes,1); tnchild=zeros(nboxes,1); +tichild=zeros(mc,nboxes); tiflag=zeros(nboxes,1); iboxtocurbox=zeros(nboxes,1); +tcenters=zeros(ndim,nboxes); + +for ilev = 0:nlevels + tladdr(1,ilev+idx1) = laddr(1,ilev+idx1); + tladdr(2,ilev+idx1) = laddr(2,ilev+idx1); +end + +tcenters = centers; +tilevel = ilevel; +tiparent = iparent; +tnchild = nchild; +tichild = ichild; +tcoeffs = coeffs; + +for ibox=1:nboxes + tiflag(ibox) = iflag(ibox); +end + +for ilev = 0:1 + for ibox = laddr(1,ilev+idx1):laddr(2,ilev+idx1) + iboxtocurbox(ibox) = ibox; + end +end + +ilevptr=zeros(nlevels+1,1); ilevptr2=zeros(nlevels,1); + +ilevptr(2) = laddr(1,2+idx1); + +for ilev=2:nlevels + nblev = laddr(2,ilev+idx1)-laddr(1,ilev+idx1)+1; % original num of boxes at ilev + ilevptr2(ilev) = ilevptr(ilev) + nblev; + nblev = laddrtail(2,ilev+idx1)-laddrtail(1,ilev+idx1)+1; % additional number of boxes at ilev due to refinement + ilevptr(ilev+1) = ilevptr2(ilev) + nblev; +end + +curbox = laddr(1,2+idx1); +ishuffle = zeros(nboxes,1); +for ilev=2:nlevels + laddr(1,ilev+idx1) = curbox; + for ibox = tladdr(1,ilev+idx1):tladdr(2,ilev+idx1) + ilevel(curbox) = tilevel(ibox); + nchild(curbox) = tnchild(ibox); + ishuffle(ibox) = curbox; % ibox info copied to curbox + for k=1:ndim + centers(k,curbox) = tcenters(k,ibox); + end + coeffs{curbox} = tcoeffs{ibox}; + iflag(curbox) = tiflag(ibox); + iboxtocurbox(ibox) = curbox; + + curbox = curbox + 1; + end + for ibox = laddrtail(1,ilev+idx1):laddrtail(2,ilev+idx1) + ilevel(curbox) = tilevel(ibox); + ishuffle(ibox) = curbox; % ibox info copied to curbox + for k=1:ndim + centers(k,curbox) = tcenters(k,ibox); + end + coeffs{curbox} = tcoeffs{ibox}; + nchild(curbox) = tnchild(ibox); + iflag(curbox) = tiflag(ibox); + iboxtocurbox(ibox) = curbox; + + curbox = curbox + 1; + end + laddr(2,ilev+idx1) = curbox-1; +end + +for ibox=1:nboxes + if(tiparent(ibox)==-1), iparent(iboxtocurbox(ibox)) = -1; end + if(tiparent(ibox)>0) + iparent(iboxtocurbox(ibox)) = iboxtocurbox(tiparent(ibox)); end + for i=1:mc + if(tichild(i,ibox)==-1), ichild(i,iboxtocurbox(ibox)) = -1; end + if(tichild(i,ibox)>0) + ichild(i,iboxtocurbox(ibox)) = iboxtocurbox(tichild(i,ibox)); end + end +end + +end + +function isgn = get_child_box_sign(ndim,isgn) + +mc = 2^ndim; +for j=1:ndim + isgn(j,1)=-1; +end + +for j=1:ndim + for i=1:2^(j-1):mc + if (i>1), isgn(j,i)=-isgn(j,i-2^(j-1)); end + for k=1:2^(j-1)-1 + isgn(j,i+k)=isgn(j,i); + end + end +end + +end \ No newline at end of file diff --git a/@treefun3/coeffs2vals.m b/@treefun3/coeffs2vals.m new file mode 100644 index 0000000..b399bee --- /dev/null +++ b/@treefun3/coeffs2vals.m @@ -0,0 +1,23 @@ +function vals = coeffs2vals(coeffs) +% +% + +persistent Eval pstored +[p,~,~,nd] = size(coeffs); + +if ( isempty(Eval) || p ~= pstored ) + pstored = p; + Eval = ones(p, p); + x = linspace(-1, 1, p).'; + x = (1-cos(pi*(2*(1:p)'-1)/(2*p)))/2; + Eval(:,2) = x; + for k=3:p + Eval(:,k) = 2*x.*Eval(:,k-1)-Eval(:,k-2); + end +end + +tmp1 = permute(tensorprod(Eval,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(Eval,tmp1,2,1),[2 3 1 4]); +vals = squeeze(permute(tensorprod(Eval,tmp2,2,1),[2 3 1 4])); + +end \ No newline at end of file diff --git a/@treefun3/plot.m b/@treefun3/plot.m index 7f6fa15..45da67e 100644 --- a/@treefun3/plot.m +++ b/@treefun3/plot.m @@ -29,6 +29,11 @@ linspace(f.domain(5), f.domain(6), nplotpts)); % v = func( xx, yy, zz); nd = numel(func); +if nd == 1 && ~iscell(func) + func1 = []; + func1{1} = func; + func = func1; +end v = zeros(size(xx)); for k = 1:nd v = v + func{k}(xx,yy,zz); diff --git a/@treefun3/refineBox.m b/@treefun3/refineBox.m index 0d8f731..7ac0e61 100644 --- a/@treefun3/refineBox.m +++ b/@treefun3/refineBox.m @@ -43,8 +43,9 @@ f.coeffs{cid1} = ccoeffs1(1:f.n,1:f.n,1:f.n,:); % to replace f.coeffs{cid1} = []; f.rint(:,cid1) = squeeze(sqrt((csclx1*cscly1*csclz1)*sum(cvals1.^2.*ww0, [1 2 3]))); f.vmax(:,cid1) = squeeze(max(abs(cvals1),[],[1 2 3])); -% f.col(cid1) = 2*f.col(id); -% f.row(cid1) = 2*f.row(id); +f.col(cid1) = 2*f.col(id); +f.row(cid1) = 2*f.row(id); +f.dep(cid1) = 2*f.dep(id); % f.morton(cid1) = cartesian2morton(f.col(cid1), f.row(cid1)); cid2 = length(f.id)+1; @@ -70,8 +71,9 @@ f.coeffs{cid2} = ccoeffs2(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid2) = squeeze(sqrt((csclx2*cscly2*csclz2)*sum(cvals2.^2.*ww0, [1 2 3]))); f.vmax(:,cid2) = squeeze(max(abs(cvals2),[],[1 2 3])); -% f.col(cid2) = 2*f.col(id) + 1; -% f.row(cid2) = 2*f.row(id); +f.col(cid2) = 2*f.col(id) + 1; +f.row(cid2) = 2*f.row(id); +f.dep(cid2) = 2*f.dep(id); % f.morton(cid2) = cartesian2morton(f.col(cid2), f.row(cid2)); cid3 = length(f.id)+1; @@ -97,8 +99,9 @@ f.coeffs{cid3} = ccoeffs3(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid3) = squeeze(sqrt((csclx3*cscly3*csclz3)*sum(cvals3.^2.*ww0, [1 2 3]))); f.vmax(:,cid3) = squeeze(max(abs(cvals3),[],[1 2 3])); -% f.col(cid3) = 2*f.col(id); -% f.row(cid3) = 2*f.row(id) + 1; +f.col(cid3) = 2*f.col(id); +f.row(cid3) = 2*f.row(id) + 1; +f.dep(cid3) = 2*f.dep(id); % f.morton(cid3) = cartesian2morton(f.col(cid3), f.row(cid3)); cid4 = length(f.id)+1; @@ -124,8 +127,9 @@ f.coeffs{cid4} = ccoeffs4(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid4) = squeeze(sqrt((csclx4*cscly4*csclz4)*sum(cvals4.^2.*ww0, [1 2 3]))); f.vmax(:,cid4) = squeeze(max(abs(cvals4),[],[1 2 3])); -% f.col(cid4) = 2*f.col(id) + 1; -% f.row(cid4) = 2*f.row(id) + 1; +f.col(cid4) = 2*f.col(id) + 1; +f.row(cid4) = 2*f.row(id) + 1; +f.dep(cid4) = 2*f.dep(id); % f.morton(cid4) = cartesian2morton(f.col(cid4), f.row(cid4)); cid5 = length(f.id)+1; @@ -151,6 +155,9 @@ f.coeffs{cid5} = ccoeffs5(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid5) = squeeze(sqrt((csclx5*cscly5*csclz5)*sum(cvals5.^2.*ww0, [1 2 3]))); f.vmax(:,cid5) = squeeze(max(abs(cvals5),[],[1 2 3])); +f.col(cid5) = 2*f.col(id); +f.row(cid5) = 2*f.row(id); +f.dep(cid5) = 2*f.dep(id) + 1; cid6 = length(f.id)+1; f.domain(:,cid6) = [xmid dom(2) dom(3) ymid zmid dom(6)]; @@ -175,6 +182,9 @@ f.coeffs{cid6} = ccoeffs6(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid6) = squeeze(sqrt((csclx6*cscly6*csclz6)*sum(cvals6.^2.*ww0, [1 2 3]))); f.vmax(:,cid6) = squeeze(max(abs(cvals6),[],[1 2 3])); +f.col(cid6) = 2*f.col(id) + 1; +f.row(cid6) = 2*f.row(id); +f.dep(cid6) = 2*f.dep(id) + 1; cid7 = length(f.id)+1; f.domain(:,cid7) = [dom(1) xmid ymid dom(4) zmid dom(6)]; @@ -199,6 +209,9 @@ f.coeffs{cid7} = ccoeffs7(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid7) = squeeze(sqrt((csclx7*cscly7*csclz7)*sum(cvals7.^2.*ww0, [1 2 3]))); f.vmax(:,cid7) = squeeze(max(abs(cvals7),[],[1 2 3])); +f.col(cid7) = 2*f.col(id); +f.row(cid7) = 2*f.row(id) + 1; +f.dep(cid7) = 2*f.dep(id) + 1; cid8 = length(f.id)+1; f.domain(:,cid8) = [xmid dom(2) ymid dom(4) zmid dom(6)]; @@ -223,6 +236,9 @@ f.coeffs{cid8} = ccoeffs8(1:f.n,1:f.n,1:f.n,:); f.rint(:,cid8) = squeeze(sqrt((csclx8*cscly8*csclz8)*sum(cvals8.^2.*ww0, [1 2 3]))); f.vmax(:,cid8) = squeeze(max(abs(cvals8),[],[1 2 3])); +f.col(cid8) = 2*f.col(id) + 1; +f.row(cid8) = 2*f.row(id) + 1; +f.dep(cid8) = 2*f.dep(id) + 1; f.children(:,id) = [cid1 cid2 cid3 cid4 cid5 cid6 cid7 cid8]; f.height(id) = 1; diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index 34eb352..1dac994 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -32,6 +32,7 @@ coeffs col row + dep morton flatNeighbors leafNeighbors @@ -163,11 +164,13 @@ f.coeffs{1} = []; f.col = uint64(0); f.row = uint64(0); + f.dep = uint64(0); % is this the correct generalization? f.coeffs{1} = coeffs(1:f.n,1:f.n,1:f.n,:); f.rint0(:,1) = zeros(nd,1); f.rint(:,1) = rint; f.vmax(:,1) = squeeze(max(abs(vals),[],[1 2 3])); + % f = buildDepthFirst(f, func); f = buildBreadthFirst(f, func, opts.tol, opts.checkpts, opts.ifcoeffs, opts.ifstorecoeffs); @@ -176,7 +179,7 @@ % Now do level restriction opts.balance = false; if ( opts.balance ) - f = balance(f); + f = balancef(f); else % Do a cumulative sum in reverse to correct the heights for k = length(f.id):-1:1 @@ -227,9 +230,12 @@ parentc = zeros(1,8*refinecnt); idc = zeros(1,8*refinecnt); levelc = zeros(1,8*refinecnt); + colc = uint64(zeros(1,8*refinecnt)); + rowc = uint64(zeros(1,8*refinecnt)); + depc = uint64(zeros(1,8*refinecnt)); for k = 1:refinecnt % 1 by 1 id = idl(k); - [domck,coeffsck,rintck,vmaxck] = refineBoxv2(f.domain(:,id), f.n, func); % just refine the box... + [domck,coeffsck,rintck,vmaxck,colck,rowck,depck] = refineBoxv2(f.domain(:,id), f.n, func, f.col(id), f.row(id), f.dep(id)); % just refine the box... f.children(:,id) = idl_start+8*(k-1)+(1:8); % start to know f.height(id) = 1; % 1 to 8 @@ -241,6 +247,9 @@ parentc(cidx) = id*ones(1,8); % these children's parent id idc(cidx) = idl_start+8*(k-1)+(1:8); % children self id levelc(cidx) = (f.level(id)+1)*ones(1,8); + colc(cidx) = colck; + rowc(cidx) = rowck; + depc(cidx) = depck; end childrenc = zeros(8,8*refinecnt); % don't know yet, a bunch of 0s to be concatenate to the end of f.children, will know when next level heightc = zeros(1,8*refinecnt); % tmp leaf box @@ -254,6 +263,9 @@ f.level = cat(2,f.level,levelc); f.children = cat(2,f.children,childrenc); f.height = cat(2,f.height,heightc); + f.col = cat(2,f.col,colc); + f.row = cat(2,f.row,rowc); + f.dep = cat(2,f.dep,depc); % update rint, - num of idl parents contribution + 8*num of idl children contribution rint = sqrt(rint.^2 - sum(f.rint(:,idl).^2,2) + sum(f.rint(:,(idl_start+1):(idl_start+8*refinecnt)).^2,2)); @@ -420,7 +432,7 @@ end -function [domain,coeffs,rint,vmax] = refineBoxv2(dom, n, func) +function [domain,coeffs,rint,vmax,ccol,crow,cdep] = refineBoxv2(dom, n, func, col, row, dep) % if nargin==0, test_refineBox3dv2; return; end @@ -447,6 +459,12 @@ coeffs = cell(1,8); nd = numel(func); +% morton related +ccol = uint64(zeros(1,8)); +crow = uint64(zeros(1,8)); +cdep = uint64(zeros(1,8)); + + cdom1 = [dom(1) xmid dom(3) ymid dom(5) zmid]; csclx1 = diff(cdom1(1:2)); cscly1 = diff(cdom1(3:4)); @@ -460,8 +478,9 @@ end cvals1 = cat(4,cvals1{:}); ccoeffs1 = treefun3.vals2coeffs(cvals1); -% f.col(cid1) = 2*f.col(id); -% f.row(cid1) = 2*f.row(id); +ccol(1) = 2*col; +crow(1) = 2*row; +cdep(1) = 2*dep; % f.morton(cid1) = cartesian2morton(f.col(cid1), f.row(cid1)); domain(:,1) = cdom1; coeffs{1} = ccoeffs1(1:n,1:n,1:n,:); % to replace f.coeffs{cid1} = []; @@ -479,8 +498,9 @@ end cvals2 = cat(4,cvals2{:}); ccoeffs2 = treefun3.vals2coeffs(cvals2); -% f.col(cid2) = 2*f.col(id) + 1; -% f.row(cid2) = 2*f.row(id); +ccol(2) = 2*col + 1; +crow(2) = 2*row; +cdep(2) = 2*dep; % f.morton(cid2) = cartesian2morton(f.col(cid2), f.row(cid2)); domain(:,2) = cdom2; coeffs{2} = ccoeffs2(1:n,1:n,1:n,:); @@ -498,8 +518,9 @@ end cvals3 = cat(4,cvals3{:}); ccoeffs3 = treefun3.vals2coeffs(cvals3); -% f.col(cid3) = 2*f.col(id); -% f.row(cid3) = 2*f.row(id) + 1; +ccol(3) = 2*col; +crow(3) = 2*row + 1; +cdep(3) = 2*dep; % f.morton(cid3) = cartesian2morton(f.col(cid3), f.row(cid3)); domain(:,3) = cdom3; coeffs{3} = ccoeffs3(1:n,1:n,1:n,:); @@ -517,8 +538,9 @@ end cvals4 = cat(4,cvals4{:}); ccoeffs4 = treefun3.vals2coeffs(cvals4); -% f.col(cid4) = 2*f.col(id) + 1; -% f.row(cid4) = 2*f.row(id) + 1; +ccol(4) = 2*col + 1; +crow(4) = 2*row + 1; +cdep(4) = 2*dep; % f.morton(cid4) = cartesian2morton(f.col(cid4), f.row(cid4)); domain(:,4) = cdom4; coeffs{4} = ccoeffs4(1:n,1:n,1:n,:); @@ -536,6 +558,10 @@ end cvals5 = cat(4,cvals5{:}); ccoeffs5 = treefun3.vals2coeffs(cvals5); +ccol(5) = 2*col; +crow(5) = 2*row; +cdep(5) = 2*dep + 1; +% domain(:,5) = cdom5; coeffs{5} = ccoeffs5(1:n,1:n,1:n,:); @@ -552,6 +578,10 @@ end cvals6 = cat(4,cvals6{:}); ccoeffs6 = treefun3.vals2coeffs(cvals6); +ccol(6) = 2*col + 1; +crow(6) = 2*row; +cdep(6) = 2*dep + 1; +% domain(:,6) = cdom6; coeffs{6} = ccoeffs6(1:n,1:n,1:n,:); @@ -568,6 +598,10 @@ end cvals7 = cat(4,cvals7{:}); ccoeffs7 = treefun3.vals2coeffs(cvals7); +ccol(7) = 2*col; +crow(7) = 2*row + 1; +cdep(7) = 2*dep + 1; +% domain(:,7) = cdom7; coeffs{7} = ccoeffs7(1:n,1:n,1:n,:); @@ -584,6 +618,9 @@ end cvals8 = cat(4,cvals8{:}); ccoeffs8 = treefun3.vals2coeffs(cvals8); +ccol(8) = 2*col + 1; +crow(8) = 2*row + 1; +cdep(8) = 2*dep + 1; domain(:,8) = cdom8; coeffs{8} = ccoeffs8(1:n,1:n,1:n,:); From e4034f2f8a5994476b82513385efe4d5e974d548 Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Sun, 5 May 2024 11:28:14 -0400 Subject: [PATCH 13/15] add morton way balance.m for 3d --- @treefun3/balance.m | 307 +++++++++++++++++++++++++++++++++++++++++++ @treefun3/treefun3.m | 8 +- cartesian2morton.m | 25 +++- 3 files changed, 334 insertions(+), 6 deletions(-) create mode 100644 @treefun3/balance.m diff --git a/@treefun3/balance.m b/@treefun3/balance.m new file mode 100644 index 0000000..a080fb7 --- /dev/null +++ b/@treefun3/balance.m @@ -0,0 +1,307 @@ +function g = balance(f) +% based on @treefun2.balance +% initial attempt in BBBBBBoston + +if ( isempty(f) ) + g = f; + return +end + +% Note: We assume IDs are stored in level order + +% Unpack data because MATLAB seems to be slow for class member access... +fmorton = f.morton(:); +fcol = f.col; +frow = f.row; +fdep = f.dep; +fxy = [fcol(:) frow(:) fdep(:)]; +flevel = f.level; +fheight = f.height; +fcoeffs = f.coeffs; +fnlevels = max(fheight) + 1; +fnboxes = length(f.id); + +% Generate level indices for f +flevelIdx = zeros(fnlevels+1, 1); +idx = 1; +flevelIdx(idx) = 1; +currentLevel = 0; +for k = 2:fnboxes + if ( currentLevel ~= flevel(k) ) + idx = idx+1; + flevelIdx(idx) = k; + currentLevel = flevel(k); + end +end +flevelIdx(end) = fnboxes+1; + +% Generate a set of 2:1-balanced Morton IDs level by level from the bottom +% (leaves) to the top (root) +S = []; +T = cell(fnlevels, 1); +for l = fnlevels-1:-1:1 + idx = flevelIdx(l):flevelIdx(l+1)-1; + fmortonl = fmorton(idx); + N = unique([S ; fmortonl(fheight(idx) > 0)]); + S = mortonparent(mortoncolleagues(N, l)); + [child1, child2, child3, child4, child5, child6, child7, child8] = mortonchildren(N); + T{l+1} = unique([child1 ; child2 ; child3 ; child4 ; ... + child5 ; child6 ; child7 ; child8]); + + % fxyl = fxy(idx,:); + % N = unique([S ; fxyl(fheight(idx)>0, :)], 'rows', 'stable'); + % S = xyparent(xycolleagues(N, l)); + % [childxy1, childxy2, childxy3, childxy4] = xychildren(N); + % %childxy = [childxy1 childxy2 childxy3 childxy4].'; + % cxy = zeros(4*size(childxy1,1), 2, 'uint64'); + % cxy(1:4:end,:) = childxy1; + % cxy(2:4:end,:) = childxy2; + % cxy(3:4:end,:) = childxy3; + % cxy(4:4:end,:) = childxy4; + % T{l+1} = unique(cxy, 'rows', 'stable'); + % T{l+1} = mortonsort(T{l+1}); +end +T{1} = uint64(0); +%T{1} = uint64([0 0]); + +% Generate level indices for g +gnlevels = fnlevels; +gnboxes = 0; +glevelIdx = zeros(gnlevels+1, 1); +glevelIdx(1) = 1; +for l = 1:gnlevels + gnboxes = gnboxes + size(T{l}, 1); + glevelIdx(l+1) = gnboxes + 1; +end + +% First, make the tree from the Morton IDs +gmorton = cell2mat(T).'; +[gcol, grow, gdep] = morton2cartesian(gmorton); +%gmorton = []; +%xy = cell2mat(T); +%gcol = xy(:,1).'; +%grow = xy(:,2).'; +glevel = zeros(1, gnboxes); +gheight = zeros(1, gnboxes); +gparent = zeros(1, gnboxes); +gchildren = zeros(8, gnboxes); +gcoeffs = cell(1, gnboxes); + +for l = gnlevels:-1:2 + id = glevelIdx(l); + jparent = 1; + ps = mortonparent(T{l}); + %pxys = xyparent(T{l}); + for k = 1:8:size(T{l},1) + ids = id:id+7; + glevel(ids) = l-1; + p = ps(k); + %pxy = pxys(k,:); + while ( T{l-1}(jparent) ~= p && jparent <= length(T{l-1}) ) + %while ( ~all(T{l-1}(jparent,:) == pxy) && jparent <= size(T{l-1}, 1) ) + jparent = jparent + 1; + end + if ( jparent > size(T{l-1}, 1) ) + error('Couldn''t find parent node.'); + end + pid = glevelIdx(l-1) + jparent - 1; + gparent(ids) = pid; + gchildren(:,pid) = ids; + gheight(pid) = 1 + max(gheight(ids)); + id = id + 8; + end +end + +dom = f.domain(:,1); +scl = 1./2.^glevel; +offx = scl*(dom(2)-dom(1)); +offy = scl*(dom(4)-dom(3)); +offz = scl*(dom(6)-dom(5)); +xo = offx.*double(gcol); +yo = offy.*double(grow); +zo = offz.*double(gdep); +gdomain = [dom(1) + xo; dom(1)+offx + xo; ... + dom(3) + yo; dom(3)+offy + yo; ... + dom(5) + zo; dom(5)+offz + zo]; + +% Finally, fill in the coefficients +gid = 1; +for fid = 1:fnboxes + if ( fheight(fid) == 0 ) + fm = fmorton(fid); + %fc = fcol(fid); + %fr = frow(fid); + fl = flevel(fid); + while ( ~(gmorton(gid)==fm && glevel(gid)==fl) && gid <= gnboxes ) + %while ( ~(gcol(gid)==fc && grow(gid)==fr && glevel(gid)==fl) && gid <= gnboxes ) + gid = gid + 1; + end + if ( gid > gnboxes ) + error('Couldn''t find corresponding tree node.'); + end + gcoeffs{gid} = fcoeffs{fid}; + end +end + +% Propagate the coefficients to the leaves, since they may be stored in +% intermediate nodes of g (which used to be leaves of f) +%% this needs to be done for 3d +if 0 +for l = 1:gnlevels-1 + for gid = glevelIdx(l):glevelIdx(l+1)-1 + if ( gheight(gid) > 0 && ~isempty(gcoeffs{gid}) ) + [LL, LR, UL, UR] = coeffs2children(gcoeffs{gid}); + gcoeffs{gchildren(1,gid)} = LL; + gcoeffs{gchildren(2,gid)} = LR; + gcoeffs{gchildren(3,gid)} = UL; + gcoeffs{gchildren(4,gid)} = UR; + end + end +end +end + +% Make a new treefun2 +g = treefun3(); +g.n = f.n; +g.domain = gdomain; +g.level = glevel; +g.height = gheight; +g.id = 1:gnboxes; +g.parent = gparent; +g.children = gchildren; +g.coeffs = gcoeffs; +g.col = gcol; +g.row = grow; +g.morton = gmorton; + +end + +%% Column/row routines +function nbrxy = xycolleagues(xy, level) + x = xy(:,1); + y = xy(:,2); + z = xy(:,3); + %bnd = 2^(level-1)-1; + bnd = bitshift(uint64(1), level-1) - 1; + % Order is important here: + %nbrxy = max(uint64(0), min(bnd, [x-1 y ; x+1 y ; x y-1 ; x y+1 ; x-1 y-1 ; x+1 y-1 ; x-1 y+1 ; x+1 y+1])); + nbrxy = max(uint64(0), min(bnd, [[x-1 y-1 z-1; x y-1 z-1; x+1 y-1 z-1; x-1 y z-1; x y z-1; x+1 y z-1; x-1 y+1 z-1; x y+1 z-1; x+1 y+1 z-1]; ... + [x-1 y-1 z ; x y-1 z ; x+1 y-1 z ; x-1 y z ; x+1 y z ; x-1 y+1 z ; x y+1 z ; x+1 y+1 z ]; ... + [x-1 y-1 z+1; x y-1 z+1; x+1 y-1 z+1; x-1 y z+1; x y z+1; x+1 y z+1; x-1 y+1 z+1; x y+1 z+1; x+1 y+1 z+1]])); + %nbrxy = max(uint64(0), min(bnd, [x-1 y-1; x y-1; x+1 y-1 ; x-1 y ; x+1 y ; x-1 y+1 ; x y+1 ; x+1 y+1])); +end + +function pxy = xyparent(xy) + pxy = bitshift(xy, -1); +end + +function [cxy1, cxy2, cxy3, cxy4, cxy5, cxy6, cxy7, cxy8] = xychildren(pxy) + cxy1 = bitshift(pxy, 2); + cxy2 = cxy1 + uint64([1 0 0]); + cxy3 = cxy1 + uint64([0 1 0]); + cxy4 = cxy1 + uint64([1 1 0]); + cxy5 = cxy1 + uint64([0 0 1]); + cxy6 = cxy1 + uint64([1 0 1]); + cxy7 = cxy1 + uint64([0 1 1]); + cxy8 = cxy1 + uint64([1 1 1]); +end + + +%% Morton routines +function [xy, idx] = mortonsort(xy) + m = xy2morton(xy); + [m, idx] = sort(m); + xy = morton2xy(m); +end + +function morton = xy2morton(xy) + partxy = Part1By2_64(xy); + morton = bitshift(partxy(:,3), 2) + bitshift(partxy(:,2), 1) + partxy(:,1); +end + +function xy = morton2xy(morton) + xy = Compact1By2_64([morton bitshift(morton, -1) bitshift(morton, -2)]); +end + +function coll = mortoncolleagues(m, level) + %bnd = 2^(level-1)-1; + bnd = bitshift(uint64(1), level-1) - 1; + %[x, y] = morton2cartesian(m); + %nbrx = max(uint64(0), min(bnd, [x-1 ; x+1 ; x ; x ; x-1 ; x+1 ; x-1 ; x+1])); + %nbry = max(uint64(0), min(bnd, [y ; y ; y-1 ; y+1 ; y-1 ; y-1 ; y+1 ; y+1])); + %coll = cartesian2morton(nbrx, nbry); + xy = morton2xy(m); + x = xy(:,1); + y = xy(:,2); + z = xy(:,3); + nbrxy = min(bnd, [[x-1 y-1 z-1; x y-1 z-1; x+1 y-1 z-1; x-1 y z-1; x y z-1; x+1 y z-1; x-1 y+1 z-1; x y+1 z-1; x+1 y+1 z-1]; ... + [x-1 y-1 z ; x y-1 z ; x+1 y-1 z ; x-1 y z ; x+1 y z ; x-1 y+1 z ; x y+1 z ; x+1 y+1 z ]; ... + [x-1 y-1 z+1; x y-1 z+1; x+1 y-1 z+1; x-1 y z+1; x y z+1; x+1 y z+1; x-1 y+1 z+1; x y+1 z+1; x+1 y+1 z+1]]); + coll = xy2morton(nbrxy); +end + +function p = mortonparent(c) + p = bitshift(c, -3); +end + +function [c1, c2, c3, c4, c5, c6, c7, c8] = mortonchildren(p) + c1 = bitshift(p, 3); + c2 = c1 + 1; + c3 = c1 + 2; + c4 = c1 + 3; + c5 = c1 + 4; + c6 = c1 + 5; + c7 = c1 + 6; + c8 = c1 + 7; +end + +function [x, y, z] = morton2cartesian(morton) + x = Compact1By2_64(morton); + y = Compact1By2_64(bitshift(morton, -1)); + z = Compact1By2_64(bitshift(morton, -2)); +end + +function morton = cartesian2morton(x, y) + morton = bitshift(Part1By2_64(y), 1) + Part1By2_64(x); +end + +function x = Part1By1(x) +% "Insert" a 0 bit after each of the 16 low bits of x + x = bitand(x, 0x0000ffff); + x = bitand(bitxor(x, bitshift(x, 8)), 0x00ff00ff); + x = bitand(bitxor(x, bitshift(x, 4)), 0x0f0f0f0f); + x = bitand(bitxor(x, bitshift(x, 2)), 0x33333333); + x = bitand(bitxor(x, bitshift(x, 1)), 0x55555555); +end + +function x = Compact1By1(x) +% Inverse of Part1By1 - "delete" all odd-indexed bits + x = bitand(x, 0x55555555); + x = bitand(bitxor(x, bitshift(x, -1)), 0x33333333); + x = bitand(bitxor(x, bitshift(x, -2)), 0x0f0f0f0f); + x = bitand(bitxor(x, bitshift(x, -4)), 0x00ff00ff); + x = bitand(bitxor(x, bitshift(x, -8)), 0x0000ffff); +end + +function x = Part1By2_64(x) +% "Insert" two 0 bits after each of the 16 low bits of x +% https://github.com/trevorprater/pymorton/blob/f248683c19d90e904193c58bbd03e77bc2c43768/pymorton/pymorton.py#L20 + +x = bitand(x, uint64(0x1fffff)); +x = bitand(bitxor(x, bitshift(x, 32)), uint64(0x1f00000000ffff)); +x = bitand(bitxor(x, bitshift(x, 16)), uint64(0x1f0000ff0000ff)); +x = bitand(bitxor(x, bitshift(x, 8)), uint64(0x100f00f00f00f00f)); +x = bitand(bitxor(x, bitshift(x, 4)), uint64(0x10c30c30c30c30c3)); +x = bitand(bitxor(x, bitshift(x, 2)), uint64(0x1249249249249249)); + +end + +function x = Compact1By2_64(x) +% Inverse of Part1By2 - "delete" all odd-indexed bits + x = bitand(x, uint64(0x1249249249249249)); + x = bitand(bitxor(x, bitshift(x, -2)), uint64(0x10c30c30c30c30c3)); + x = bitand(bitxor(x, bitshift(x, -4)), uint64(0x100f00f00f00f00f)); + x = bitand(bitxor(x, bitshift(x, -8)), uint64(0x1f0000ff0000ff)); + x = bitand(bitxor(x, bitshift(x, -16)), uint64(0x1f00000000ffff)); + x = bitand(bitxor(x, bitshift(x, -32)), uint64(0x1fffff)); +end \ No newline at end of file diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index 1dac994..4424631 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -52,7 +52,7 @@ dom = [-1 1 -1 1 -1 1]; opts = struct(); - opts.balance = false; % not yet + opts.balance = false; % fortran way exists, morton way intial attempt in BBBBBBBoston opts.neighbors = false; opts.tol = 1e-12; opts.checkpts = []; @@ -174,12 +174,12 @@ % f = buildDepthFirst(f, func); f = buildBreadthFirst(f, func, opts.tol, opts.checkpts, opts.ifcoeffs, opts.ifstorecoeffs); - % f.morton = cartesian2morton(f.col, f.row); + f.morton = cartesian2morton(f.col, f.row, f.dep); % Now do level restriction - opts.balance = false; if ( opts.balance ) - f = balancef(f); + f = balance(f); + % f = balancef(f); else % Do a cumulative sum in reverse to correct the heights for k = length(f.id):-1:1 diff --git a/cartesian2morton.m b/cartesian2morton.m index fad4e4f..3959abe 100644 --- a/cartesian2morton.m +++ b/cartesian2morton.m @@ -1,6 +1,12 @@ -function morton = cartesian2morton(x, y) +function morton = cartesian2morton(x, y, z) -morton = bitshift(Part1By1_64(y), 1) + Part1By1_64(x); +if nargin == 2 + morton = bitshift(Part1By1_64(y), 1) + Part1By1_64(x); +end + +if nargin == 3 + morton = bitshift(Part1By2_64(z), 2) + bitshift(Part1By2_64(y), 1) + Part1By2_64(x); +end end @@ -15,3 +21,18 @@ x = bitand(bitxor(x, bitshift(x, 1)), 0x5555555555555555); end + +function x = Part1By2_64(x) +% "Insert" two 0 bits after each of the 16 low bits of x +% https://github.com/trevorprater/pymorton/blob/f248683c19d90e904193c58bbd03e77bc2c43768/pymorton/pymorton.py#L20 + +x = bitand(x, uint64(0x1fffff)); +x = bitand(bitxor(x, bitshift(x, 32)), uint64(0x1f00000000ffff)); +x = bitand(bitxor(x, bitshift(x, 16)), uint64(0x1f0000ff0000ff)); +x = bitand(bitxor(x, bitshift(x, 8)), uint64(0x100f00f00f00f00f)); +x = bitand(bitxor(x, bitshift(x, 4)), uint64(0x10c30c30c30c30c3)); +x = bitand(bitxor(x, bitshift(x, 2)), uint64(0x1249249249249249)); + +end + + From fa38253442ccb53742dffb0e0bfdbd5385b9431d Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Tue, 7 May 2024 09:42:28 -0400 Subject: [PATCH 14/15] coeffs2children --- @treefun3/balance.m | 17 +++--- @treefun3/private/coeffs2children.m | 89 +++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 @treefun3/private/coeffs2children.m diff --git a/@treefun3/balance.m b/@treefun3/balance.m index a080fb7..9967a1c 100644 --- a/@treefun3/balance.m +++ b/@treefun3/balance.m @@ -145,20 +145,21 @@ % Propagate the coefficients to the leaves, since they may be stored in % intermediate nodes of g (which used to be leaves of f) -%% this needs to be done for 3d -if 0 for l = 1:gnlevels-1 for gid = glevelIdx(l):glevelIdx(l+1)-1 if ( gheight(gid) > 0 && ~isempty(gcoeffs{gid}) ) - [LL, LR, UL, UR] = coeffs2children(gcoeffs{gid}); - gcoeffs{gchildren(1,gid)} = LL; - gcoeffs{gchildren(2,gid)} = LR; - gcoeffs{gchildren(3,gid)} = UL; - gcoeffs{gchildren(4,gid)} = UR; + [LLD, LRD, ULD, URD, LLT, LRT, ULT, URT] = coeffs2children(gcoeffs{gid}); + gcoeffs{gchildren(1,gid)} = LLD; + gcoeffs{gchildren(2,gid)} = LRD; + gcoeffs{gchildren(3,gid)} = ULD; + gcoeffs{gchildren(4,gid)} = URD; + gcoeffs{gchildren(5,gid)} = LLT; + gcoeffs{gchildren(6,gid)} = LRT; + gcoeffs{gchildren(7,gid)} = ULT; + gcoeffs{gchildren(8,gid)} = URT; end end end -end % Make a new treefun2 g = treefun3(); diff --git a/@treefun3/private/coeffs2children.m b/@treefun3/private/coeffs2children.m new file mode 100644 index 0000000..c0db47d --- /dev/null +++ b/@treefun3/private/coeffs2children.m @@ -0,0 +1,89 @@ +function [LLD, LRD, ULD, URD, LLT, LRT, ULT, URT] = coeffs2children3d(coeffs) +%COEFFS2CHILDREN Convert 3D Chebyshev coefficients on a parent to 2D +% Chebyshev coefficients on its four children. +% follow Z curve, double check with ngrid + +persistent EvalL EvalR Fp pstored +p = size(coeffs, 1); + +if ( isempty(EvalL) || isempty(EvalR) || isempty(Fp) || p ~= pstored ) + pstored = p; + x = -cos(pi*(2*(1:p)'-1)/(2*p)); + xL = 1/2*x-1/2; + xR = 1/2*x+1/2; + EvalL = ones(p, p); EvalR = ones(p, p); + EvalL(:,2) = xL; EvalR(:,2) = xR; + for k=3:p + EvalL(:,k) = 2*xL.*EvalL(:,k-1)-EvalL(:,k-2); + EvalR(:,k) = 2*xR.*EvalR(:,k-1)-EvalR(:,k-2); + end + Fp = 2*cos(pi*((1:p)-1)'*(2*(p:-1:1)-1)/(2*p))/p; + Fp(1,:) = 1/2*Fp(1,:); +end + +% Lower left down +tmp1 = permute(tensorprod(EvalL,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(EvalL,tmp1,2,1),[2 3 1 4]); +valsLLD = squeeze(permute(tensorprod(EvalL,tmp2,2,1),[2 3 1 4])); +tmp1hat = permute(tensorprod(Fp,valsLLD,2,1),[2 3 1 4]); +tmp2hat = permute(tensorprod(Fp,tmp1hat,2,1),[2 3 1 4]); +LLD = permute(tensorprod(Fp,tmp2hat,2,1),[2 3 1 4]); +% Lower right down +% [xx0LRD, yy0LRD, zz0LRD] = ndgrid(xR,xL,xL); +tmp1 = permute(tensorprod(EvalR,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(EvalL,tmp1,2,1),[2 3 1 4]); +valsLRD = squeeze(permute(tensorprod(EvalL,tmp2,2,1),[2 3 1 4])); +tmp1hat = permute(tensorprod(Fp,valsLRD,2,1),[2 3 1 4]); +tmp2hat = permute(tensorprod(Fp,tmp1hat,2,1),[2 3 1 4]); +LRD = permute(tensorprod(Fp,tmp2hat,2,1),[2 3 1 4]); +% Upper left down +% [xx0ULD, yy0ULD, zz0ULD] = ndgrid(xL,xR,xL); +tmp1 = permute(tensorprod(EvalL,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(EvalR,tmp1,2,1),[2 3 1 4]); +valsULD = squeeze(permute(tensorprod(EvalL,tmp2,2,1),[2 3 1 4])); +tmp1hat = permute(tensorprod(Fp,valsULD,2,1),[2 3 1 4]); +tmp2hat = permute(tensorprod(Fp,tmp1hat,2,1),[2 3 1 4]); +ULD = permute(tensorprod(Fp,tmp2hat,2,1),[2 3 1 4]); +% Upper right down +% [xx0URD, yy0URD, zz0URD] = ndgrid(xR,xR,xL); +tmp1 = permute(tensorprod(EvalR,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(EvalR,tmp1,2,1),[2 3 1 4]); +valsURD = squeeze(permute(tensorprod(EvalL,tmp2,2,1),[2 3 1 4])); +tmp1hat = permute(tensorprod(Fp,valsURD,2,1),[2 3 1 4]); +tmp2hat = permute(tensorprod(Fp,tmp1hat,2,1),[2 3 1 4]); +URD = permute(tensorprod(Fp,tmp2hat,2,1),[2 3 1 4]); + +% Lower left top +% [xx0LLT, yy0LLT, zz0LLT] = ndgrid(xL,xL,xR); +tmp1 = permute(tensorprod(EvalL,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(EvalL,tmp1,2,1),[2 3 1 4]); +valsLLT = squeeze(permute(tensorprod(EvalR,tmp2,2,1),[2 3 1 4])); +tmp1hat = permute(tensorprod(Fp,valsLLT,2,1),[2 3 1 4]); +tmp2hat = permute(tensorprod(Fp,tmp1hat,2,1),[2 3 1 4]); +LLT = permute(tensorprod(Fp,tmp2hat,2,1),[2 3 1 4]); +% Lower right top +% [xx0LRT, yy0LRT, zz0LRT] = ndgrid(xR,xL,xR); +tmp1 = permute(tensorprod(EvalR,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(EvalL,tmp1,2,1),[2 3 1 4]); +valsLRT = squeeze(permute(tensorprod(EvalR,tmp2,2,1),[2 3 1 4])); +tmp1hat = permute(tensorprod(Fp,valsLRT,2,1),[2 3 1 4]); +tmp2hat = permute(tensorprod(Fp,tmp1hat,2,1),[2 3 1 4]); +LRT = permute(tensorprod(Fp,tmp2hat,2,1),[2 3 1 4]); +% Upper left top +% [xx0ULT, yy0ULT, zz0ULT] = ndgrid(xL,xR,xR); +tmp1 = permute(tensorprod(EvalL,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(EvalR,tmp1,2,1),[2 3 1 4]); +valsULT = squeeze(permute(tensorprod(EvalR,tmp2,2,1),[2 3 1 4])); +tmp1hat = permute(tensorprod(Fp,valsULT,2,1),[2 3 1 4]); +tmp2hat = permute(tensorprod(Fp,tmp1hat,2,1),[2 3 1 4]); +ULT = permute(tensorprod(Fp,tmp2hat,2,1),[2 3 1 4]); +% Upper right top +% [xx0URT, yy0URT, zz0URT] = ndgrid(xR,xR,xR); +tmp1 = permute(tensorprod(EvalR,coeffs,2,1),[2 3 1 4]); +tmp2 = permute(tensorprod(EvalR,tmp1,2,1),[2 3 1 4]); +valsURT = squeeze(permute(tensorprod(EvalR,tmp2,2,1),[2 3 1 4])); +tmp1hat = permute(tensorprod(Fp,valsURT,2,1),[2 3 1 4]); +tmp2hat = permute(tensorprod(Fp,tmp1hat,2,1),[2 3 1 4]); +URT = permute(tensorprod(Fp,tmp2hat,2,1),[2 3 1 4]); + +end From 6e106d734ad05bee2b47e3498ee11de870f4a516 Mon Sep 17 00:00:00 2001 From: Hai Zhu Date: Mon, 29 Jul 2024 17:21:33 -0400 Subject: [PATCH 15/15] make changes to treefun3 func input... --- @treefun3/plot.m | 20 +++++--- @treefun3/treefun3.m | 111 +++++++++++++++++++++++++++++++------------ 2 files changed, 93 insertions(+), 38 deletions(-) diff --git a/@treefun3/plot.m b/@treefun3/plot.m index 45da67e..407c04f 100644 --- a/@treefun3/plot.m +++ b/@treefun3/plot.m @@ -28,16 +28,22 @@ linspace(f.domain(3), f.domain(4), nplotpts), ... linspace(f.domain(5), f.domain(6), nplotpts)); % v = func( xx, yy, zz); -nd = numel(func); -if nd == 1 && ~iscell(func) - func1 = []; - func1{1} = func; - func = func1; -end +tmpval = func(0,0,0); +nd = numel(tmpval); +% nd = numel(func); +% if nd == 1 && ~iscell(func) +% func1 = []; +% func1{1} = func; +% func = func1; +% end v = zeros(size(xx)); +tmpvals = func(xx,yy,zz); for k = 1:nd - v = v + func{k}(xx,yy,zz); + v = v + tmpvals(:,:,:,k); end +% for k = 1:nd +% v = v + func{k}(xx,yy,zz); +% end if isreal(v) [row,col,tube] = ind2sub(size(v), find(v(:) == max(v(:)), 1, 'last')); else diff --git a/@treefun3/treefun3.m b/@treefun3/treefun3.m index 4424631..55d0994 100644 --- a/@treefun3/treefun3.m +++ b/@treefun3/treefun3.m @@ -106,6 +106,9 @@ if ( isfield(opts1, 'checkpts') ) opts.checkpts = opts1.checkpts; end + if ( isfield(opts1, 'ifcoeffs') ) + opts.ifcoeffs = opts1.ifcoeffs; + end elseif ( nargin == 9 ) % TREEFUN3(DOMAIN, LEVEL, HEIGHT, ID, PARENT, CHILDREN, % COEFFS, COL, ROW) @@ -127,12 +130,13 @@ % initialize vals, rint, coeffs... wrap this up? nalias = f.n; - nd = numel(func); - if nd == 1 && ~iscell(func) - func1 = []; - func1{1} = func; - func = func1; - end + tmpval = func(0,0,0); + nd = numel(tmpval); + % if nd == 1 && ~iscell(func) + % func1 = []; + % func1{1} = func; + % func = func1; + % end x0 = (1-cos(pi*(2*(1:nalias)'-1)/(2*nalias)))/2; [xx0, yy0, zz0] = ndgrid(x0); l = floor(nalias/2)+1; @@ -147,9 +151,13 @@ zz = sclz*zz0 + dom(5); ww0 = wx0.*wy0.*wz0; vals = cell(1,nd); + tmpvals = func(xx,yy,zz); for k = 1:nd - vals{k} = func{k}(xx,yy,zz); + vals{k} = tmpvals(:,:,:,k); end + % for k = 1:nd + % vals{k} = func{k}(xx,yy,zz); + % end vals = cat(4,vals{:}); coeffs = treefun3.vals2coeffs(vals); rint = max(squeeze(sqrt((sclx*scly*sclz)*sum(vals.^2.*ww0, [1 2 3]))),1e-16); % initialze l2 @@ -360,18 +368,24 @@ if ~ifcoeffs resolved = 1; - xxx = sclx*xxx0 + dom(1); - yyy = scly*yyy0 + dom(3); - zzz = sclz*zzz0 + dom(5); - vals = cell(1,nd); - for k = 1:nd - vals{k} = f{k}(xxx,yyy,zzz); - end - F = cat(4,vals{:}); - G = treefun3.coeffs2refvals(coeffs); % structured grid - for k = 1:nd - erra = sqrt(sum(squeeze(G(:,:,:,k) - F(:,:,:,k)).^2.*www0,'all')); - resolved = resolved && ( erra < tol * sqrt(1/(sclx*scly*sclz)) * rint(k) ); + if (sclx>tol) || (scly>tol) || (sclz>tol) % is this ok? + xxx = sclx*xxx0 + dom(1); + yyy = scly*yyy0 + dom(3); + zzz = sclz*zzz0 + dom(5); + vals = cell(1,nd); + tmpvals = f(xxx,yyy,zzz); + for k = 1:nd + vals{k} = tmpvals(:,:,:,k); + end + % for k = 1:nd + % vals{k} = f{k}(xxx,yyy,zzz); + % end + F = cat(4,vals{:}); + G = treefun3.coeffs2refvals(coeffs); % structured grid + for k = 1:nd + erra = sqrt(sum(squeeze(G(:,:,:,k) - F(:,:,:,k)).^2.*www0,'all')); + resolved = resolved && ( erra < tol * sqrt(1/(sclx*scly*sclz)) * rint(k) ); + end end elseif ifcoeffs @@ -418,9 +432,10 @@ zzz>=-1 & zzz<=1); if ( any(in) ) F = zeros(nd,sum(in)); - for k = 1:nd - F(k,:) = f{k}(checkpts(1,in)',checkpts(2,in)',checkpts(3,in)')'; % nd x n checkpts - end + F = reshape(squeeze(f(checkpts(1,in)',checkpts(2,in)',checkpts(3,in)'))',[nd sum(in)]); + % for k = 1:nd + % F(k,:) = f{k}(checkpts(1,in)',checkpts(2,in)',checkpts(3,in)')'; % nd x n checkpts + % end G = treefun3.coeffs2checkvals(coeffs,xxx(in),yyy(in),zzz(in)); err_checkvals = max(abs(F - G),[],2); for k = 1:nd @@ -457,7 +472,9 @@ % domain and coeffs domain = zeros(6,8); coeffs = cell(1,8); -nd = numel(func); +% nd = numel(func); +tmpval = func(0,0,0); +nd = numel(tmpval); % morton related ccol = uint64(zeros(1,8)); @@ -473,9 +490,13 @@ cyy1 = cscly1*yy0 + cdom1(3); czz1 = csclz1*zz0 + cdom1(5); cvals1 = cell(1,nd); +tmpvals1 = func(cxx1,cyy1,czz1); for k = 1:nd - cvals1{k} = func{k}(cxx1,cyy1,czz1); + cvals1{k} = tmpvals1(:,:,:,k); end +% for k = 1:nd +% cvals1{k} = func{k}(cxx1,cyy1,czz1); +% end cvals1 = cat(4,cvals1{:}); ccoeffs1 = treefun3.vals2coeffs(cvals1); ccol(1) = 2*col; @@ -493,9 +514,13 @@ cyy2 = cscly2*yy0 + cdom2(3); czz2 = csclz2*zz0 + cdom2(5); cvals2 = cell(1,nd); +tmpvals2 = func(cxx2,cyy2,czz2); for k = 1:nd - cvals2{k} = func{k}(cxx2,cyy2,czz2); + cvals2{k} = tmpvals2(:,:,:,k); end +% for k = 1:nd +% cvals2{k} = func{k}(cxx2,cyy2,czz2); +% end cvals2 = cat(4,cvals2{:}); ccoeffs2 = treefun3.vals2coeffs(cvals2); ccol(2) = 2*col + 1; @@ -513,9 +538,13 @@ cyy3 = cscly3*yy0 + cdom3(3); czz3 = csclz3*zz0 + cdom3(5); cvals3 = cell(1,nd); +tmpvals3 = func(cxx3,cyy3,czz3); for k = 1:nd - cvals3{k} = func{k}(cxx3,cyy3,czz3); + cvals3{k} = tmpvals3(:,:,:,k); end +% for k = 1:nd +% cvals3{k} = func{k}(cxx3,cyy3,czz3); +% end cvals3 = cat(4,cvals3{:}); ccoeffs3 = treefun3.vals2coeffs(cvals3); ccol(3) = 2*col; @@ -533,9 +562,13 @@ cyy4 = cscly4*yy0 + cdom4(3); czz4 = csclz4*zz0 + cdom4(5); cvals4 = cell(1,nd); +tmpvals4 = func(cxx4,cyy4,czz4); for k = 1:nd - cvals4{k} = func{k}(cxx4,cyy4,czz4); + cvals4{k} = tmpvals4(:,:,:,k); end +% for k = 1:nd +% cvals4{k} = func{k}(cxx4,cyy4,czz4); +% end cvals4 = cat(4,cvals4{:}); ccoeffs4 = treefun3.vals2coeffs(cvals4); ccol(4) = 2*col + 1; @@ -553,9 +586,13 @@ cyy5 = cscly5*yy0 + cdom5(3); czz5 = csclz5*zz0 + cdom5(5); cvals5 = cell(1,nd); +tmpvals5 = func(cxx5,cyy5,czz5); for k = 1:nd - cvals5{k} = func{k}(cxx5,cyy5,czz5); + cvals5{k} = tmpvals5(:,:,:,k); end +% for k = 1:nd +% cvals5{k} = func{k}(cxx5,cyy5,czz5); +% end cvals5 = cat(4,cvals5{:}); ccoeffs5 = treefun3.vals2coeffs(cvals5); ccol(5) = 2*col; @@ -573,9 +610,13 @@ cyy6 = cscly6*yy0 + cdom6(3); czz6 = csclz6*zz0 + cdom6(5); cvals6 = cell(1,nd); +tmpvals6 = func(cxx6,cyy6,czz6); for k = 1:nd - cvals6{k} = func{k}(cxx6,cyy6,czz6); + cvals6{k} = tmpvals6(:,:,:,k); end +% for k = 1:nd +% cvals6{k} = func{k}(cxx6,cyy6,czz6); +% end cvals6 = cat(4,cvals6{:}); ccoeffs6 = treefun3.vals2coeffs(cvals6); ccol(6) = 2*col + 1; @@ -593,9 +634,13 @@ cyy7 = cscly7*yy0 + cdom7(3); czz7 = csclz7*zz0 + cdom7(5); cvals7 = cell(1,nd); +tmpvals7 = func(cxx7,cyy7,czz7); for k = 1:nd - cvals7{k} = func{k}(cxx7,cyy7,czz7); + cvals7{k} = tmpvals7(:,:,:,k); end +% for k = 1:nd +% cvals7{k} = func{k}(cxx7,cyy7,czz7); +% end cvals7 = cat(4,cvals7{:}); ccoeffs7 = treefun3.vals2coeffs(cvals7); ccol(7) = 2*col; @@ -613,9 +658,13 @@ cyy8 = cscly8*yy0 + cdom8(3); czz8 = csclz8*zz0 + cdom8(5); cvals8 = cell(1,nd); +tmpvals8 = func(cxx8,cyy8,czz8); for k = 1:nd - cvals8{k} = func{k}(cxx8,cyy8,czz8); + cvals8{k} = tmpvals8(:,:,:,k); end +% for k = 1:nd +% cvals8{k} = func{k}(cxx8,cyy8,czz8); +% end cvals8 = cat(4,cvals8{:}); ccoeffs8 = treefun3.vals2coeffs(cvals8); ccol(8) = 2*col + 1;