Home > fvcom_prepro > find_nesting_region.m

find_nesting_region

PURPOSE ^

Creates a nesting structure array for direct/indirect or weighted nesting

SYNOPSIS ^

function Nested = find_nesting_region(conf, Mobj)

DESCRIPTION ^

 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]

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

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

Generated on Wed 20-Feb-2019 16:06:01 by m2html © 2005