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

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

Added 'verbose' argument to all files. Argument is T/F and acts as an unmute button for the files. All files are naturally muted.

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