BenWalleshauser / Predicting-SST-w-.-Coupled-RCs

MIT License
3 stars 0 forks source link

Identification of neighbors #3

Closed renierts closed 2 years ago

renierts commented 2 years ago

Hello,

thanks for sharing the code for your paper. I have a question about the computation of the neighborhood of a patch. To debug this, I created the following matrix containing all of the indices (regardless the current point is land or water):

1 2 3 4 5 6 7 ... 120
121 122 123 124 125 126 127 ... 240
241 242 243 244 245 246 247 ... 360
361 362 363 364 365 366 367 ... 480
481 482 483 484 485 486 487 ... 600
601 602 603 604 605 606 607 ... 720
721 722 723 724 725 726 727 ... 840
841 842 843 844 845 846 847 ... 960
... ... ... ... ... ... ... ... ...
28681 28682 28683 28684 28685 28686 28687 ... 28800

I can pad this matrix in order to contain surrounding neighbors:

28800 28681 28682 28683 28684 28685 28686 28687 ... 28800 28681
120 1 2 3 4 5 6 7 ... 120 1
240 121 122 123 124 125 126 127 ... 240 121
360 241 242 243 244 245 246 247 ... 360 241
480 361 362 363 364 365 366 367 ... 480 361
600 481 482 483 484 485 486 487 ... 600 481
720 601 602 603 604 605 606 607 ... 720 601
840 721 722 723 724 725 726 727 ... 840 721
960 841 842 843 844 845 846 847 ... 960 841
... ... ... ... ... ... ... ... ... ... ...
28800 28681 28682 28683 28684 28685 28686 28687 ... 28800 28681
120 1 2 3 4 5 6 7 ... 120 0

Now, I would like to follow you and extract 4x4 patches together with the neigbors. This breaks down to extract a 6x6 submatrix, shift by 4 and extract the next 6x6 matrix. The first patch with its neighbors would look like the following:

28680 28681 28682 28683 28684 28685
120 1 2 3 4 5
240 121 122 123 124 125
360 241 242 243 244 245
480 361 362 363 364 365
600 481 482 483 484 485
However, this is different from what you obtain:
28680 28681 28682 28683 28684 28685
1 2 3 4 5
121 122 123 124 125
241 242 243 244 245
361 362 363 364 365
481 482 483 484 485

Two issues are:

  1. Only 35 values are present - I would have expected 36.
  2. The four values below the Table are not correct.

Could you please help me to identify potential mistakes in my way of thinking?

BenWalleshauser commented 2 years ago

Hello,

Thank you for reaching out and voicing your concerns regarding the code! The current difficulty you are experiencing is due to how neighbors are found for indices at the top of the map.

Following the 120x240 matrix used in the paper, let's say we want to find the neighboring indices for the first pack: Slide1

We can first find the neighbors directly surrounding the pack (please note the indices in the East): Slide2

Now there is a challenge to find the neighbors for the indices on the top of the map, as on a globe they would all converge at the pole. But it would not be computationally efficient to include all of the points on the top of the map (total size of input for RC would be >240). Therefore the Neighbors.m file currently will select the corresponding index that is symmetric about the prime meridian (note the same computations were neglected in the Neighbors.m file for the South Pole due to the used dataset being entirely land/ice there). Slide3

Using a combination of the FindPackIndices.m and Neighbors.m file this is also what is produced (for a total of 34 indices).

clear
clc

nBox = 120;   %Number of latitudes 
mBox = 240;   %Number of longitudes

%Number of rows and columns per RC (can change)
nPack = 4;
mPack = 4;
NumPacks = (nBox*mBox)/(nPack*mPack);    %Number of packs, each pack will have its own RC
NaNset = 250;

%function PackInd = FindPackIndices(Avgs,nBox,mBox,nPack,mPack,NaNset)

%Returns the matrix indices that are in each pack. Each column of indices
%contains the points within a pack.

if nBox/nPack ~= ceil(nBox/nPack) | mBox/mPack ~= ceil(mBox/mPack)
    error('Number of Boxes needs to be divisible by num of RCs (Packs)')
end

%Finding the indices of the boxes per pack/RC
PackInd = zeros(nPack*mPack,NumPacks);
d = 1;
c = 0;
b = 0;
e = 0;
mult = 0;       
mult2 = 0;      
for i = 1:NumPacks 
        for j = 1:mPack
            for k = 1:nPack
                %Cycling through each pack:
                PackInd(b+1,i) = (j-1)*nBox + k + mult2*nPack + mult*nBox*mPack;
                b = b+1;
                %if SST_total(1,PackInd(b,i)) == NaNset   %This just disregards points on land from the creation of the pack                              
                     %PackInd(b,i) = 0;             
                %end
            end

            if b >= nPack*mPack    
                mult2 = mult2+1;        
                b = 0;
            end
        end       
        c = c + 1;         
        if c >= nBox/nPack          
            mult = mult+1;     
            c = 0;
            mult2 = 0;          
        end

end

%end

%% Finding Neighbors
NeighborInd = zeros((nPack+2)*(mPack+2),NumPacks);

%Finding of neighboring indices
for p = 1:NumPacks   %creating RC for each group

    iPackInd = nonzeros(PackInd(:,p));                      %The indices that are going to be predicted with the given RC                     
    CloudInd = zeros(8,length(iPackInd));                   %The indices that surround the pack

    %Finding the indices that are going to be considered in RC
    for z = 1:length(iPackInd)
        nbInds = Neighbors(iPackInd(z),nBox,mBox);          %The neighboring indices for a given point
        %for i = 1:length(nbInds)                            %Getting rid of neighbor if it is on land
            %if SST_total(1,nbInds(i)) == NaNset             
                %nbInds(i) = 0;              
            %end
        %end
        CloudInd(1:length(nbInds),z) = nbInds;              %All of the neighboring indices
    end

    CloudInd = reshape(CloudInd,[],1);
    TotalInd = [iPackInd; CloudInd];            
    TotalInd = unique(TotalInd,'stable');                   %Getting rid of repeat Neighbors while preserving order of indices
    TotalInd = TotalInd(TotalInd~=0);                       %Total Indices being condsidered in the given RC (nonzero)
    NeighborInd(1:length(TotalInd),p) = TotalInd;
end

PackOne = sort(nonzeros(NeighborInd(:,1)));                 %Total indices in first pack

%% Neighbors function as in current code

function [neighbors] = Neighbors(ind, nBox, mBox)
%Returns the neighboring indices surrounding a point on the globe. Disregards the
%neighbors that wrap around on the bottom of the map as they are all land
%values (due to Antarctica). 

k = 0; %index of neighbors, increases as conditions met

%% Top, Bottom, Left, and Right
%Neighbor beneath
Bottom = 0;
if ind/nBox ~= ceil(ind/nBox)   %ie not at bottom of map
    k = k+1;
    Bottom = 1;
    neighbors(k) = ind+1;
end

%Neighbor above
Top = 0;
if (ind-1)/nBox ~= ceil((ind-1)/nBox) & ind ~= 1 %If point is not on top of the map
    k = k+1;
    Top = 1;
    neighbors(k) = ind-1;
end
TopMap = 0;
if mod(ind-1,nBox) == 0   %If point is on top of the map
    k = k+1;
    TopMap = 1;
    myCol = (ind-1)/nBox+1;
    neighborCol = 240-myCol+1;
    neighbors(k) = (neighborCol-1)*nBox+1;
end

%Neighbor right
if ind <= nBox*mBox-nBox  %If point is not on the right edge of map
    k = k+1;
    neighbors(k) = ind + nBox;
end
if ind > nBox*mBox-nBox   %If point is on the right edge of map
    k = k+1;
    neighbors(k) = ind - (nBox*mBox-nBox);
end

%Neighbor left
if ind >= nBox+1       %If point is not on the left edge of the map
    k = k+1;
    neighbors(k) = ind - nBox;
end
if ind < nBox+1        %If point is on the left edge of the map
    k = k+1;
    neighbors(k) = ind + (nBox*mBox-nBox);
end

%% Corners

%Top Left
if Top == 1 %If not on top edge
    k = k+1;
    neighbors(k) = ind-nBox-1;
    if neighbors(k) < 1 %If on left edge therefore the neighbor wraps around
        neighbors(k) = neighbors(k) + nBox*mBox;
    end
end
if TopMap == 1;   %If on top edge
    neighborCol = 240-myCol+1+1;
    if neighborCol ~= mBox+1        %Would be it's own neighbor if condition true
        k = k+1;
        neighbors(k) = (neighborCol-1)*nBox+1;
    end
end

%Top Right
if Top == 1      %If not on top edge
    k = k+1;
    neighbors(k) = ind+nBox-1;
    if neighbors(k) > nBox*mBox %If on right edge therefore the neighbor wraps around
        neighbors(k) = neighbors(k) - nBox*mBox;
    end
end
if TopMap == 1;           %If on top edge
    neighborCol = 240-myCol+1-1;
    if neighborCol ~= 0        %Would be it's own neighbor if condition true
        k = k+1;
        neighbors(k) = (neighborCol-1)*nBox+1;
    end
end

%Bottom Right
if Bottom == 1 
    k = k+1;
    neighbors(k) = ind+nBox+1;
    if neighbors(k) > nBox*mBox %If on right edge therefore the neighbor wraps around
        neighbors(k) = neighbors(k) - nBox*mBox;
    end
end

%Bottom Left
if Bottom == 1 
    k = k+1;
    neighbors(k) = ind-nBox+1;
    if neighbors(k) < 1 %If on left edge therefore the neighbor wraps around
        neighbors(k) = neighbors(k) + nBox*mBox;
    end
end

end

If you'd like to disregard the neighbors found across the pole, please use this revised version of Neighbors.m:

function [neighbors] = Neighbors(ind, nBox, mBox)
%Returns the neighboring indices surrounding a point on the globe. Disregards the
%neighbors that wrap around on the top and bottom of the map.

k = 0; %index of neighbors, increases as conditions met

%% Top, Bottom, Left, and Right
%Neighbor beneath
Bottom = 0;
if ind/nBox ~= ceil(ind/nBox)   %ie not at bottom of map
    k = k+1;
    Bottom = 1;
    neighbors(k) = ind+1;
end

%Neighbor above
Top = 0;
if (ind-1)/nBox ~= ceil((ind-1)/nBox) & ind ~= 1 %If point is not on top of the map
    k = k+1;
    Top = 1;
    neighbors(k) = ind-1;
end

%Neighbor right
if ind <= nBox*mBox-nBox  %If point is not on the right edge of map
    k = k+1;
    neighbors(k) = ind + nBox;
end
if ind > nBox*mBox-nBox   %If point is on the right edge of map
    k = k+1;
    neighbors(k) = ind - (nBox*mBox-nBox);
end

%Neighbor left
if ind >= nBox+1       %If point is not on the left edge of the map
    k = k+1;
    neighbors(k) = ind - nBox;
end
if ind < nBox+1        %If point is on the left edge of the map
    k = k+1;
    neighbors(k) = ind + (nBox*mBox-nBox);
end

%% Corners

%Top Left
if Top == 1 %If not on top edge
    k = k+1;
    neighbors(k) = ind-nBox-1;
    if neighbors(k) < 1 %If on left edge therefore the neighbor wraps around
        neighbors(k) = neighbors(k) + nBox*mBox;
    end
end

%Top Right
if Top == 1      %If not on top edge
    k = k+1;
    neighbors(k) = ind+nBox-1;
    if neighbors(k) > nBox*mBox %If on right edge therefore the neighbor wraps around
        neighbors(k) = neighbors(k) - nBox*mBox;
    end
end

%Bottom Right
if Bottom == 1 
    k = k+1;
    neighbors(k) = ind+nBox+1;
    if neighbors(k) > nBox*mBox %If on right edge therefore the neighbor wraps around
        neighbors(k) = neighbors(k) - nBox*mBox;
    end
end

%Bottom Left
if Bottom == 1 
    k = k+1;
    neighbors(k) = ind-nBox+1;
    if neighbors(k) < 1 %If on left edge therefore the neighbor wraps around
        neighbors(k) = neighbors(k) + nBox*mBox;
    end
end

end

Please let me know if you still experience issues!

Best regards, Ben W.

renierts commented 2 years ago

Hello,

thanks for pointing this out. I will shortly try the solution.

Best regards, Peter S.

renierts commented 2 years ago

Everything workd fine. Thanks for the explanation!

BenWalleshauser commented 2 years ago

Great - I am glad to have helped!