


Creates a nesting structure array for direct/indirect or weighted nesting
function Nested = find_nesting_region(conf, Mobj)
DESCRIPTION:
Uses the Mesh object Mobj and the conf variable to search for the nodes
and elements originating from the open boundaries into de domain
interior for a specified number of levels.
Optionally specify nesting type:
1/2: DIRECT/INDIRECT nesting:
- Full variables/no surface elevation respectively.
3: RELAXATION nesting:
- Nesting with a relaxation method.
INPUT:
conf = struct whose field names are the model configuration options
as set by the user. These are generally assigned at the top of a
make_input.m type of script. Minimum fields are:
- Nested_type: Array of nesting types, one for each open boundary.
1 or 2 are direct nesting, 3 is weighted. If we're doing
type 3, we can specify the number of levels of nested
boundaries to use. The minimum valid value is 1. For
Indirect or direct nesting use 1
- levels: number of levels of nodes over which to relax the
boundary (if Nested_type is 3).
- power = determines drop of weights from 1. 0 is linear,
1-4 is 1/conf.levels.^conf.power.
Mobj = Mesh object with the following fields:
- tri: Triangulation table as pupulated by read_sms_grid
- x: Node x coordinates (cartesian)
- y: Node y coordinates (cartesian)
- xc: element x coordinates (cartesian)
- yc: Element y coordinates (cartesian)
- nObcNodes: number of nodes as defined by SMS and read
through read_sms_grid
- read_obc_nodes = nodes indices at boundaries as read in
read_sms_grid.
- obc_type = Type of OBC as defined in mod_obcs.F [ values
of 1-10] and parsed to Mobj from conf by add_obc_nodes_list
- obc_nodes: matrix with node indices. Each row is a given
open boundary.
- nObs: number of open boundaries.
OUTPUT:
Nested = Mesh object with all the same fields as Mobj, plus the
following added and modified fields:
- read_obc_nodes: new nested boundary node IDs
- read_obc_elems: new nested boundary element IDs
- nObs: number of open boundaries (number of levels * number
- nObcNodes: number of nodes in each nested level
of original open boundaries)
- obc_type: the type for each nested boundary
- obc_nodes: the array-based nested boundary node IDs (for
backwards compatibility)
- weight_node: weights for each nested node boundary level
- weight_cell: weights for each nested element boundary level
EXAMPLE USAGE:
conf.Nested_type = type of nesting [1, 2 == direct nesting, 3 == weighted]
conf.levels = number of boundary bands to use for weighted option
conf.power = determines drop of weights from 1 [0 is linear, anything
else is 1/conf.levels.^conf.power]
Mobj.tri = Triangulation table as pupulated by read_sms_grid
Mobj.x = Nodes x coordinate [we should make it possible to use lon instead]
Mobj.y = Nodes y coordinate [we should make it possible to use lat instead]
Mobj.nObcNodes = number of nodes as defined by SMS and read through read_sms_grid
Mobj.read_obc_nodes = nodes indices at boundaries as read in read_sms_grid
Mobj.obc_type = Type of OBC as defined in mod_obcs.F [ values of 1-10]
and parsed to Mobj from conf by add_obc_nodes_list
Mobj.xc = Nodes x coordinate [we should make it possible to use lonc instead]
Mobj.yc = Elements y coordinate [we should make it possible to use latc instead]
Mobj.obc_nodes = matrix with node indices. Each row is a boundary level.
Mobj.nObs = total number of open boundary levels (I think this is set
in setup_metrics.m).
the global variable ftbverbose shows information at run time as well as
generating a figure with the location of the nesting nodes and elements
Nested = find_nesting_region(conf,Mobj)
Nested.nObcNodes = number of nodes in new nesting zone
Nested.read_obc_nodes = nodes indices in nesting zone
Nested.obc_type = Type of OBC as defined in mod_obcs.F [ values of 1-10]
Nested.obc_nodes = matrix with node indices. Each row is a boundary level.
Nested.nObs = total number of open boundary levels
Nested.weight_node = weights for nodes if using weighted type [0-1] see
FVCOM manual for further info (Chapter 6 section 4 in version 3.2)
Nested.weight_cell = weights for elements if using weighted type [0-1]

0001 function Nested = find_nesting_region(conf, Mobj) 0002 % Creates a nesting structure array for direct/indirect or weighted nesting 0003 % 0004 % function Nested = find_nesting_region(conf, Mobj) 0005 % 0006 % DESCRIPTION: 0007 % Uses the Mesh object Mobj and the conf variable to search for the nodes 0008 % and elements originating from the open boundaries into de domain 0009 % interior for a specified number of levels. 0010 % 0011 % Optionally specify nesting type: 0012 % 1/2: DIRECT/INDIRECT nesting: 0013 % - Full variables/no surface elevation respectively. 0014 % 3: RELAXATION nesting: 0015 % - Nesting with a relaxation method. 0016 % 0017 % INPUT: 0018 % conf = struct whose field names are the model configuration options 0019 % as set by the user. These are generally assigned at the top of a 0020 % make_input.m type of script. Minimum fields are: 0021 % - Nested_type: Array of nesting types, one for each open boundary. 0022 % 1 or 2 are direct nesting, 3 is weighted. If we're doing 0023 % type 3, we can specify the number of levels of nested 0024 % boundaries to use. The minimum valid value is 1. For 0025 % Indirect or direct nesting use 1 0026 % - levels: number of levels of nodes over which to relax the 0027 % boundary (if Nested_type is 3). 0028 % - power = determines drop of weights from 1. 0 is linear, 0029 % 1-4 is 1/conf.levels.^conf.power. 0030 % Mobj = Mesh object with the following fields: 0031 % - tri: Triangulation table as pupulated by read_sms_grid 0032 % - x: Node x coordinates (cartesian) 0033 % - y: Node y coordinates (cartesian) 0034 % - xc: element x coordinates (cartesian) 0035 % - yc: Element y coordinates (cartesian) 0036 % - nObcNodes: number of nodes as defined by SMS and read 0037 % through read_sms_grid 0038 % - read_obc_nodes = nodes indices at boundaries as read in 0039 % read_sms_grid. 0040 % - obc_type = Type of OBC as defined in mod_obcs.F [ values 0041 % of 1-10] and parsed to Mobj from conf by add_obc_nodes_list 0042 % - obc_nodes: matrix with node indices. Each row is a given 0043 % open boundary. 0044 % - nObs: number of open boundaries. 0045 % 0046 % OUTPUT: 0047 % Nested = Mesh object with all the same fields as Mobj, plus the 0048 % following added and modified fields: 0049 % - read_obc_nodes: new nested boundary node IDs 0050 % - read_obc_elems: new nested boundary element IDs 0051 % - nObs: number of open boundaries (number of levels * number 0052 % - nObcNodes: number of nodes in each nested level 0053 % of original open boundaries) 0054 % - obc_type: the type for each nested boundary 0055 % - obc_nodes: the array-based nested boundary node IDs (for 0056 % backwards compatibility) 0057 % - weight_node: weights for each nested node boundary level 0058 % - weight_cell: weights for each nested element boundary level 0059 % 0060 % EXAMPLE USAGE: 0061 % conf.Nested_type = type of nesting [1, 2 == direct nesting, 3 == weighted] 0062 % conf.levels = number of boundary bands to use for weighted option 0063 % conf.power = determines drop of weights from 1 [0 is linear, anything 0064 % else is 1/conf.levels.^conf.power] 0065 % Mobj.tri = Triangulation table as pupulated by read_sms_grid 0066 % Mobj.x = Nodes x coordinate [we should make it possible to use lon instead] 0067 % Mobj.y = Nodes y coordinate [we should make it possible to use lat instead] 0068 % Mobj.nObcNodes = number of nodes as defined by SMS and read through read_sms_grid 0069 % Mobj.read_obc_nodes = nodes indices at boundaries as read in read_sms_grid 0070 % Mobj.obc_type = Type of OBC as defined in mod_obcs.F [ values of 1-10] 0071 % and parsed to Mobj from conf by add_obc_nodes_list 0072 % Mobj.xc = Nodes x coordinate [we should make it possible to use lonc instead] 0073 % Mobj.yc = Elements y coordinate [we should make it possible to use latc instead] 0074 % Mobj.obc_nodes = matrix with node indices. Each row is a boundary level. 0075 % Mobj.nObs = total number of open boundary levels (I think this is set 0076 % in setup_metrics.m). 0077 % 0078 % the global variable ftbverbose shows information at run time as well as 0079 % generating a figure with the location of the nesting nodes and elements 0080 % 0081 % Nested = find_nesting_region(conf,Mobj) 0082 % 0083 % Nested.nObcNodes = number of nodes in new nesting zone 0084 % Nested.read_obc_nodes = nodes indices in nesting zone 0085 % Nested.obc_type = Type of OBC as defined in mod_obcs.F [ values of 1-10] 0086 % Nested.obc_nodes = matrix with node indices. Each row is a boundary level. 0087 % Nested.nObs = total number of open boundary levels 0088 % Nested.weight_node = weights for nodes if using weighted type [0-1] see 0089 % FVCOM manual for further info (Chapter 6 section 4 in version 3.2) 0090 % Nested.weight_cell = weights for elements if using weighted type [0-1] 0091 0092 % Author(s): 0093 % Ricardo Torres (Plymouth Marine Laboratory) 0094 % Pierre Cazenave (Plymouth Marine Laboratory) 0095 % Darren Price (CH2MHill) 0096 % Hakeem Johnson (CH2MHill) 0097 % 0098 % Revision history: 0099 % 2015-11-01 First version based on Hakeem and Darren code as provided to 0100 % Torres by Pierre. 0101 % 2016-01-19 Updated to a stand alone function and general tidy up. 0102 % 2016-12-14 Updated the help. Also disabled the plot to ease automated 0103 % runs. 0104 % 2016-12-22 Fairly major rewrite to make things clearer and less prone 0105 % to subtle bugs. 0106 % 2017-02-16 Fix for direct nesting (no weights needed). 0107 % 2017-02-20 Fix non-critical bug which added empty cells for the nest 0108 % elements. Also make the node and element weight values match one another. 0109 % 0110 %========================================================================== 0111 0112 [~, subname] = fileparts(mfilename('fullpath')); 0113 0114 assert(isscalar(conf.power), 'conf.power should be scalar, applying to all open boundaries'); 0115 0116 global ftbverbose 0117 if isempty(ftbverbose) 0118 ftbverbose=false; 0119 end 0120 if ftbverbose 0121 fprintf('\nbegin : %s\n', subname) 0122 end 0123 0124 TR = triangulation(Mobj.tri, [Mobj.x, Mobj.y]); 0125 0126 Nested = Mobj; 0127 Nested.nObs = 0; % number of nodal levels is incremented for each level. 0128 0129 % Make cell arrays to store the element IDs for each nested level as well 0130 % as for the weights on the nodes and elements. 0131 Nested.read_obc_nodes = cell(0); 0132 Nested.read_obc_elems = cell(0); 0133 if any(conf.Nested_type == 3) 0134 Nested.weight_cell = cell(0); 0135 Nested.weight_node = cell(0); 0136 end 0137 0138 % if ftbverbose 0139 % figure(1) 0140 % clf 0141 % triplot(Nested.tri, Nested.x, Nested.y) 0142 % axis('equal', 'tight') 0143 % hold on 0144 % end 0145 0146 % Indices for the output cell arrays which are incremented for each nest 0147 % level and each open boundary. 0148 cumulative_node_idx = 1; 0149 cumulative_elem_idx = 1; 0150 for obc_idx = 1:Mobj.nObs 0151 % Generate the weights for the elements and nodes. 0152 if conf.Nested_type(obc_idx) == 3 0153 if conf.power == 0 0154 weights_nodes = fliplr((1:conf.levels(obc_idx) + 1)./(conf.levels(obc_idx) + 1)); 0155 else 0156 weights_nodes = 1:conf.levels(obc_idx) + 1; 0157 weights_nodes = 1./weights_nodes.^conf.power; 0158 end 0159 % Use the same weights as the nodes, but drop the last level 0160 % (elements is always 1 smaller). 0161 weights_elems = weights_nodes(1:end - 1); 0162 end 0163 0164 % Save the original open boundary nodes into the nested struct (Nested). 0165 Nested.read_obc_nodes{cumulative_node_idx} = Mobj.read_obc_nodes{obc_idx}; 0166 0167 % Given the current open boundary, find the elements connected to it 0168 % and give them some weights. 0169 ti = vertexAttachments(TR, double(Mobj.read_obc_nodes{obc_idx})'); 0170 Nested.read_obc_elems{cumulative_elem_idx} = unique([ti{:}]); 0171 0172 % Save the weights into the nested struct (Nested). 0173 if conf.Nested_type(obc_idx) ~= 1 0174 Nested.weight_node{cumulative_node_idx} = repmat(weights_nodes(1), 1, length(Nested.read_obc_nodes{cumulative_node_idx})); 0175 Nested.weight_cell{cumulative_elem_idx} = repmat(weights_elems(1), 1, length(Nested.read_obc_elems{cumulative_elem_idx})); 0176 end 0177 0178 % Also save the type of open boundary we've got and update the open 0179 % boundary counter and number of open boundary nodes. 0180 Nested.nObcNodes(cumulative_node_idx) = length(Nested.read_obc_nodes{cumulative_node_idx}); 0181 Nested.nObs = Nested.nObs + 1; 0182 Nested.obc_type(cumulative_node_idx) = conf.Nested_type(obc_idx); 0183 0184 if ftbverbose && conf.Nested_type(obc_idx) ~= 1 0185 % figure(1) 0186 % scatter(Nested.x(Nested.read_obc_nodes{cumulative_node_idx}), Nested.y(Nested.read_obc_nodes{cumulative_node_idx}), 20, Nested.weight_node{cumulative_node_idx}, 'filled') 0187 % scatter(Nested.xc(Nested.read_obc_elems{cumulative_elem_idx}), Nested.yc(Nested.read_obc_elems{cumulative_elem_idx}), 20, Nested.weight_cell{cumulative_elem_idx}, 'filled') 0188 fprintf('Original open boundary %d\n', obc_idx) 0189 end 0190 0191 % Now we have the original open boundary and the elements connected to 0192 % it we can move through the levels specified in conf.levels(obc_idx) 0193 % and repeat the process. Bump the cumulative counters accordingly. 0194 cumulative_node_idx = cumulative_node_idx + 1; 0195 cumulative_elem_idx = cumulative_elem_idx + 1; 0196 0197 for lev = 1:conf.levels(obc_idx) 0198 % Find the nodes and elements for this level and assign their 0199 % weights. Use the most recent data in Nested.read_obc_nodes as the 0200 % anchor from which to work. 0201 Nested.read_obc_nodes{cumulative_node_idx} = int32(setdiff(unique(Nested.tri(Nested.read_obc_elems{cumulative_elem_idx - 1}, :)), ... 0202 [Nested.read_obc_nodes{1:cumulative_node_idx - 1}]))'; 0203 ti = vertexAttachments(TR, double(Nested.read_obc_nodes{cumulative_node_idx})'); 0204 Nested.nObs = Nested.nObs + 1; 0205 Nested.obc_type(cumulative_node_idx) = conf.Nested_type(obc_idx); 0206 Nested.nObcNodes(cumulative_node_idx) = length(Nested.read_obc_nodes{cumulative_node_idx}); 0207 0208 if conf.Nested_type(obc_idx) ~= 1 0209 Nested.weight_node{cumulative_node_idx} = repmat(weights_nodes(lev + 1), 1, length(Nested.read_obc_nodes{cumulative_node_idx})); 0210 end 0211 if lev ~= conf.levels(obc_idx) 0212 Nested.read_obc_elems{cumulative_elem_idx} = setdiff(unique([ti{:}]), [Nested.read_obc_elems{:}]); 0213 if conf.Nested_type(obc_idx) ~= 1 0214 Nested.weight_cell{cumulative_elem_idx} = repmat(weights_elems(lev + 1), 1, length(Nested.read_obc_elems{cumulative_elem_idx})); 0215 end 0216 end 0217 0218 if ftbverbose && conf.Nested_type(obc_idx) ~= 1 0219 % figure(1) 0220 % scatter(Nested.x(Nested.read_obc_nodes{cumulative_node_idx}), Nested.y(Nested.read_obc_nodes{cumulative_node_idx}), 20, Nested.weight_node{cumulative_node_idx}, 'filled') 0221 % if lev ~= conf.levels(obc_idx) 0222 % scatter(Nested.xc(Nested.read_obc_elems{cumulative_elem_idx}), Nested.yc(Nested.read_obc_elems{cumulative_elem_idx}), 20, Nested.weight_cell{cumulative_elem_idx}, 'filled') 0223 % end 0224 fprintf('Nested level %d\n', lev) 0225 end 0226 0227 % Bump the node and element cumulative counters so the next loop 0228 % dumps everything into the right position in the cell arrays. 0229 cumulative_node_idx = cumulative_node_idx + 1; 0230 if lev ~= conf.levels(obc_idx) 0231 cumulative_elem_idx = cumulative_elem_idx + 1; 0232 end 0233 end 0234 % Check if all possible elements have been correctly identified 0235 % colapse all nodes into an array and extract all elements connected to 0236 % those nodes 0237 % find the elements that are attached to 3 nodes in the nesting region 0238 % 0239 candidate= find(all(ismember(Mobj.tri,[Nested.read_obc_nodes{end}]),2)); 0240 % test if it is different from existing elements 0241 [truecandidate]=setdiff(candidate,[Nested.read_obc_elems{:}]); 0242 % add to existing list. Catenate will ignore an empty result from 0243 % setdiff 0244 if ~isempty(truecandidate) 0245 for dd=1:length(truecandidate) 0246 Nested.read_obc_elems{end} = cat(2,Nested.read_obc_elems{end},truecandidate(dd)); 0247 if conf.Nested_type(obc_idx) ~= 1 0248 Nested.weight_cell{end} = cat(2,Nested.weight_cell{end},Nested.weight_cell{end}(end)); 0249 end 0250 end 0251 end 0252 if ftbverbose 0253 fprintf('\n') 0254 end 0255 end 0256 0257 % Update the clunky obc_nodes array with the new node IDs from 0258 % Nested.read_obc_nodes. 0259 for nidx = 1:length(Nested.read_obc_nodes) 0260 Nested.obc_nodes(nidx, 1:length(Nested.read_obc_nodes{nidx})) = Nested.read_obc_nodes{nidx}; 0261 end 0262 0263 if ftbverbose 0264 % figure(1) 0265 % colorbar 0266 % title('Nest weights') 0267 fprintf('end : %s \n', subname) 0268 end