source: issm/trunk/src/m/contrib/musselman/read_netCDF.m@ 27889

Last change on this file since 27889 was 27889, checked in by musselman, 19 months ago

Removed global vars from all files. Updated read log statements to use "load" instead of "save"

File size: 13.3 KB
Line 
1%{
2Given a NetCDF4 file, this set of functions will perform the following:
3 1. Enter each group of the file.
4 2. For each variable in each group, update an empty model with the variable's data
5 3. Enter nested groups and repeat
6
7
8If the model you saved has subclass instances that are not in the standard model() class
9you can:
10 1. Copy lines 30-35, set the "results" string to the name of the subclass instance,
11 2. Copy and modify the make_results_subclasses() function to create the new subclass
12 instances you need.
13From there, the rest of this script will automatically create the new subclass
14instance in the model you're writing to and store the data from the netcdf file there.
15%}
16
17
18function model_copy = read_netCDF(filename, varargin)
19 if nargin > 1
20 verbose = true;
21 else
22 verbose = false;
23 end
24
25 if verbose
26 fprintf('NetCDF42C v1.1.14\n');
27 end
28 % make a model framework to fill that is in the scope of this file
29 model_copy = model();
30
31 % Check if path exists
32 if exist(filename, 'file')
33 if verbose
34 fprintf('Opening %s for reading\n', filename);
35 end
36
37 % Open the given netCDF4 file
38 NCData = netcdf.open(filename, 'NOWRITE');
39 % Remove masks from netCDF data for easy conversion: NOT WORKING
40 %netcdf.setMask(NCData, 'NC_NOFILL');
41
42 % see if results is in there, if it is we have to instantiate some classes
43 try
44 results_group_id = netcdf.inqNcid(NCData, "results");
45 model_copy = make_results_subclasses(model_copy, NCData, verbose);
46 catch
47 end % 'results' group doesn't exist
48
49 % see if inversion is in there, if it is we may have to instantiate some classes
50 try
51 inversion_group_id = netcdf.inqNcid(NCData, "inversion");
52 model_copy = check_inversion_class(model_copy, NCData, verbose);
53 catch
54 end % 'inversion' group doesn't exist
55
56 % loop over first layer of groups in netcdf file
57 for group = netcdf.inqGrps(NCData)
58 group_id = netcdf.inqNcid(NCData, netcdf.inqGrpName(group));
59 %disp(netcdf.inqGrpNameFull(group_id))
60 % hand off first level to recursive search
61 model_copy = walk_nested_groups(group_id, model_copy, NCData, verbose);
62 end
63
64 % Close the netCDF file
65 netcdf.close(NCData);
66 if verbose
67 disp('Model Successfully Copied')
68 end
69 else
70 fprintf('File %s does not exist.\n', filename);
71 end
72end
73
74
75function model_copy = make_results_subclasses(model_copy, NCData, verbose)
76 resultsGroup = netcdf.inqNcid(NCData, "results");
77 variables = netcdf.inqVarIDs(resultsGroup);
78 for name = variables
79 class_instance = netcdf.inqVar(resultsGroup, name);
80 class_instance_names_raw = netcdf.getVar(resultsGroup, name, 'char').';
81 class_instance_names = cellstr(class_instance_names_raw);
82 for index = 1:numel(class_instance_names)
83 class_instance_name = class_instance_names{index};
84 model_copy.results = setfield(model_copy.results, class_instance_name, struct());
85 end
86 %model_copy.results = setfield(model_copy.results, class_instance, class_instance_name);
87 end
88 model_copy = model_copy;
89 if verbose
90 disp('Successfully recreated results structs:')
91 for fieldname = string(fieldnames(model_copy.results))
92 disp(fieldname)
93 end
94 end
95end
96
97
98function model_copy = check_inversion_class(model_copy, NCData, verbose)
99 % get the name of the inversion class: either inversion or m1qn3inversion or taoinversion
100 inversionGroup = netcdf.inqNcid(NCData, "inversion");
101 varid = netcdf.inqVarID(inversionGroup, 'inversion_class_name');
102 inversion_class = convertCharsToStrings(netcdf.getVar(inversionGroup, varid,'char'));
103 if strcmp(inversion_class, 'm1qn3inversion')
104 model_copy.inversion = m1qn3inversion();
105 if verbose
106 disp('Successfully created inversion class instance: m1qn3inversion')
107 end
108 elseif strcmp(inversion_class, 'taoinversion')
109 model_copy.inversion = taoinversion();
110 if verbose
111 disp('Successfully created inversion class instance: taoinversion')
112 end
113 else
114 if verbose
115 disp('No inversion class was found')
116 end
117 end
118 model_copy = model_copy;
119end
120
121
122function model_copy = walk_nested_groups(group_location_in_file, model_copy, NCData, verbose)
123 % we search the current group level for variables by getting this struct
124 variables = netcdf.inqVarIDs(group_location_in_file);
125
126 % from the variables struct get the info related to the variables
127 for variable = variables
128 [varname, xtype, dimids, numatts] = netcdf.inqVar(group_location_in_file, variable);
129
130 % keep an eye out for nested structs:
131 if strcmp(varname, 'this_is_a_nested')
132 is_nested = true;
133 model_copy = copy_nested_struct(group_location_in_file, model_copy, NCData, verbose);
134 elseif strcmp(varname, 'solution')
135 % band-aid pass..
136 else
137 model_copy = copy_variable_data_to_new_model(group_location_in_file, varname, xtype, model_copy, NCData, verbose);
138 end
139 end
140
141 % try to find groups in current level, if it doesn't work it's because there is nothing there
142 %try
143 % if it's a nested struct the function copy_nested_struct has already been called
144 if logical(exist('is_nested', 'var'))
145 % do nothing
146 else
147 % search for nested groups in the current level to feed back to this function
148 groups = netcdf.inqGrps(group_location_in_file);
149 if not(isempty(groups))
150 for group = groups
151 group_id = netcdf.inqNcid(group_location_in_file, netcdf.inqGrpName(group));
152 %disp(netcdf.inqGrpNameFull(group_id))
153 model_copy = walk_nested_groups(group, model_copy, NCData, verbose);
154 end
155 end
156 end
157 %catch % no nested groups here
158 %end
159end
160
161
162
163function model_copy = copy_nested_struct(group_location_in_file, model_copy, NCData, verbose)
164 %{
165 A common multidimensional struct array is the 1xn md.results.TransientSolution struct.
166 The process to recreate is as follows:
167 1. Get the name of the struct from group name
168 2. Get the fieldnames from the subgroups
169 3. Recreate the struct with fieldnames
170 4. Populate the fields with their respective values
171 %}
172
173 % step 1
174 name_of_struct = netcdf.inqGrpName(group_location_in_file);
175
176 % step 2
177 subgroups = netcdf.inqGrps(group_location_in_file); % numerical cell array with ID's of subgroups
178 % get single subgroup's data
179 single_subgroup_ID = subgroups(1);
180 subgroup_varids = netcdf.inqVarIDs(single_subgroup_ID);
181 fieldnames = {};
182 for variable = subgroup_varids
183 [varname, xtype, dimids, numatts] = netcdf.inqVar(single_subgroup_ID, variable);
184 fieldnames{end+1} = varname;
185 end
186
187 % step 3
188 address_in_model_raw = split(netcdf.inqGrpNameFull(group_location_in_file), '/');
189 address_in_model = address_in_model_raw{2};
190
191 % we cannot assign a variable to represent this object as MATLAB treats all variables as copies
192 % and not pointers to the same memory address
193 % this means that if address_in_model has more than 1 layer, we need to modify the code. For now,
194 % we just hope this will do. An example of a no-solution would be model().abc.def.ghi.field
195
196 model_copy.(address_in_model).(name_of_struct) = struct();
197 % for every fieldname in the subgroup, create an empty field
198 for fieldname = string(fieldnames)
199 model_copy.(address_in_model).(name_of_struct).(fieldname) = {};
200 end
201
202 % use repmat to make the struct array multidimensional along the fields axis
203 number_of_dimensions = numel(subgroups);
204 model_copy.(address_in_model).(name_of_struct) = repmat(model_copy.(address_in_model).(name_of_struct), 1, number_of_dimensions);
205
206 % step 4
207 % for every layer of the multidimensional struct array, populate the fields
208 for current_layer = 1:number_of_dimensions
209 % choose subgroup
210 current_layer_subgroup_ID = subgroups(current_layer);
211 % get all vars
212 current_layer_subgroup_varids = netcdf.inqVarIDs(current_layer_subgroup_ID);
213 % get individual vars and set fields at layer current_layer
214 for varid = current_layer_subgroup_varids
215 [varname, xtype, dimids, numatts] = netcdf.inqVar(current_layer_subgroup_ID, varid);
216 data = netcdf.getVar(current_layer_subgroup_ID, varid);
217
218 % netcdf uses Row Major Order but MATLAB uses Column Major Order so we need to transpose all arrays w/ more than 1 dim
219 if all(size(data)~=1) || xtype == 2
220 data = data.';
221 end
222
223 % set the field
224 model_copy.(address_in_model).(name_of_struct)(current_layer).(varname) = data;
225 %address_to_struct_in_model = setfield(address_to_struct_in_model(current_layer), varname, data)
226 end
227 model_copy.(address_in_model).(name_of_struct)(current_layer);
228 if verbose
229 fprintf("Successfully loaded layer %s to multidimension struct array\n", num2str(current_layer))
230 end
231 end
232 model_copy = model_copy;
233 if verbose
234 fprintf('Successfully recreated multidimensional structure array %s in md.%s\n', name_of_struct, address_in_model)
235 end
236end
237
238
239
240
241%{
242Since there are two types of objects that MATLAB uses (classes and structs), we have to check
243which object we're working with before we can set any fields/attributes of it. After this is completed,
244we can write the data to that location in the model.
245%}
246
247function model_copy = copy_variable_data_to_new_model(group_location_in_file, varname, xtype, model_copy, NCData, verbose)
248 %disp(varname)
249 % this is an inversion band-aid
250 if strcmp(varname, 'inversion_class_name') || strcmp(varname, 'name_of_struct') || strcmp(varname, 'solution')
251 % we don't need this
252 else
253 % putting try/catch here so that any errors generated while copying data are logged and not lost by the try/catch in walk_nested_groups function
254 try
255 %disp(netcdf.inqGrpNameFull(group_location_in_file))
256 %disp(class(netcdf.inqGrpNameFull(group_location_in_file)))
257 address_to_attr = strrep(netcdf.inqGrpNameFull(group_location_in_file), '/', '.');
258 varid = netcdf.inqVarID(group_location_in_file, varname);
259 data = netcdf.getVar(group_location_in_file, varid);
260
261
262 % if we have an empty string
263 if xtype == 2 && isempty(all(data))
264 data = cell(char());
265 % if we have an empty cell-char array
266 elseif numel(data) == 1 && xtype == 3 && data == -32767
267 data = cell(char());
268 elseif isempty(all(data))
269 data = []
270 end
271 % band-aid for some cell-char-arrays:
272 if xtype == 2 && strcmp(data, 'default')
273 data = {'default'};
274 end
275
276 % netcdf uses Row Major Order but MATLAB uses Column Major Order so we need to transpose all arrays w/ more than 1 dim
277 if all(size(data)~=1) || xtype == 2
278 data = data.';
279 end
280
281 % if we have a list of strings
282 if xtype == 2
283 try
284 if strcmp(netcdf.getAtt(group_location_in_file, varid, "type_is"), 'cell_array_of_strings')
285 data = cellstr(data);
286 end
287 catch
288 % no attr found so we pass
289 end
290 end
291
292 % the issm c compiler does not work with int64 datatypes, so we need to convert those to int16
293 % reference this (very hard to find) link for netcdf4 datatypes: https://docs.unidata.ucar.edu/netcdf-c/current/netcdf_8h_source.html
294 %xtype
295 if xtype == 10
296 arg_to_eval = ['model_copy', address_to_attr, '.', varname, ' = ' , 'double(data);'];
297 eval(arg_to_eval);
298 %disp('Loaded int64 as int16')
299 else
300 arg_to_eval = ['model_copy', address_to_attr, '.', varname, ' = data;'];
301 eval(arg_to_eval);
302 end
303
304 if verbose
305 full_addy = netcdf.inqGrpNameFull(group_location_in_file);
306 %disp(xtype)
307 %class(data)
308 fprintf('Successfully loaded %s to %s\n', varname, full_addy);
309 end
310
311 catch ME %ME is an MException struct
312 % Some error occurred if you get here.
313 fprintf(1,'There was an error with %s! \n', varname)
314 errorMessage = sprintf('Error in function %s() at line %d.\n\nError Message:\n%s', ME.stack.name, ME.stack.line, ME.message);
315 fprintf(1, '%s\n', errorMessage);
316 uiwait(warndlg(errorMessage));
317 %line = ME.stack.line
318 %fprintf(1,'There was an error with %s! \n', varname)
319 %fprintf('The message was:\n%s\n',ME.message);
320 %fprintf(1,'The identifier was:\n%s\n',ME.identifier);
321
322 % more error handling...
323 end
324 end
325 model_copy = model_copy;
326end
Note: See TracBrowser for help on using the repository browser.