Ignore:
Timestamp:
08/10/23 18:34:53 (20 months ago)
Author:
musselman
Message:

Added support in matlab read/write_netCDF files for multidimensional structure arrays.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • issm/trunk/src/m/contrib/musselman/read_netCDF.m

    r27874 r27875  
    1717
    1818function model_copy = read_netCDF(filename)
    19     fprintf('NetCDF42C v1.1.13\n');
     19    fprintf('NetCDF42C v1.1.14\n');
    2020
    2121    % make a model framework to fill that is in the scope of this file
     
    109109        for variable = variables
    110110            [varname, xtype, dimids, numatts] = netcdf.inqVar(group_location_in_file, variable);
    111             copy_variable_data_to_new_model(group_location_in_file,varname, xtype);
     111
     112            % keep an eye out for nested structs:
     113            if strcmp(varname, 'this_is_a_nested')
     114                is_nested = true;
     115                copy_nested_struct(group_location_in_file)
     116            else
     117                is_nested = false;
     118                copy_variable_data_to_new_model(group_location_in_file,varname, xtype);
     119            end
    112120        end
    113121    catch ME
     
    117125    % try to find groups in current level, if it doesn't work it's because there is nothing there
    118126    try
    119         % search for nested groups in the current level to feed back to this function
    120         groups = netcdf.inqGrps(group_location_in_file);
    121         if not(isempty(groups))
    122             for group = groups
    123                 %disp('found nested group!!')
    124                 group_id = netcdf.inqNcid(group_location_in_file, netcdf.inqGrpName(group));
    125                 %disp(netcdf.inqGrpNameFull(group_id))
    126                 walk_nested_groups(group);
    127             end
    128         end
    129     catch
    130     end
    131 end
     127        % if it's not a nested struct, keep searching for subgroups
     128        if isnested
     129            % do nothing
     130        else
     131            % search for nested groups in the current level to feed back to this function
     132            groups = netcdf.inqGrps(group_location_in_file);
     133            if not(isempty(groups))
     134                for group = groups
     135                    %disp('found nested group!!')
     136                    group_id = netcdf.inqNcid(group_location_in_file, netcdf.inqGrpName(group));
     137                    %disp(netcdf.inqGrpNameFull(group_id))
     138                    walk_nested_groups(group);
     139                end
     140            end
     141        end
     142    catch % no nested groups here
     143    end
     144end
     145
     146
     147
     148function copy_nested_struct(group_location_in_file)
     149    global model_copy;
     150    global NCData;
     151    %{
     152        A common multidimensional struct array is the 1xn md.results.TransientSolution struct.
     153        The process to recreate is as follows:
     154            1. Get the name of the struct from group variable name_of_struct
     155            2. Get the fieldnames from the subgroups
     156            3. Recreate the struct with fieldnames
     157            4. Populate the fields with their respective values
     158    %}
     159
     160    % step 1
     161    varid = netcdf.inqVarID(group_location_in_file, 'name_of_struct');
     162    name_of_struct = netcdf.getVar(group_location_in_file, varid)';
     163
     164    % step 2
     165    subgroups = netcdf.inqGrps(group_location_in_file); % numerical cell array with ID's of subgroups
     166    % get single subgroup's data
     167    single_subgroup_ID = subgroups(1);
     168    subgroup_varids = netcdf.inqVarIDs(single_subgroup_ID);
     169    fieldnames = {};
     170    for variable = subgroup_varids
     171        [varname, xtype, dimids, numatts] = netcdf.inqVar(single_subgroup_ID, variable);
     172        fieldnames{end+1} = varname;
     173    end
     174
     175    % step 3
     176    address_in_model = strrep(netcdf.inqGrpNameFull(group_location_in_file), '/', '');
     177    % we cannot assign a variable to represent this object as MATLAB treats all variables as copies
     178    % and not pointers to the same memory address
     179    % this means that if address_in_model is more than 1 item, we need to modify the code. For now,
     180    % we just hope this will do
     181   
     182    model_copy.(address_in_model).(name_of_struct) = struct();
     183   
     184    % for every fieldname in the subgroup, create an empty field
     185    for fieldname = string(fieldnames)
     186        model_copy.(address_in_model).(name_of_struct).(fieldname) = {};
     187    end
     188
     189    % use repmat to make the struct array multidimensional along the fields axis
     190    number_of_dimensions = numel(subgroups);
     191    model_copy.(address_in_model).(name_of_struct) = repmat(model_copy.(address_in_model).(name_of_struct), 1, number_of_dimensions);
     192   
     193    % step 4
     194    % for every layer of the multidimensional struct array, populate the fields
     195    for current_layer = 1:number_of_dimensions
     196        % choose subgroup
     197        current_layer_subgroup_ID = subgroups(current_layer);
     198        % get all vars
     199        current_layer_subgroup_varids = netcdf.inqVarIDs(current_layer_subgroup_ID);
     200        % get individual vars and set fields at layer current_layer
     201        for varid = current_layer_subgroup_varids
     202            [varname, xtype, dimids, numatts] = netcdf.inqVar(current_layer_subgroup_ID, varid);
     203            data = netcdf.getVar(current_layer_subgroup_ID, varid);
     204
     205            % netcdf uses Row Major Order but MATLAB uses Column Major Order so we need to transpose all arrays w/ more than 1 dim
     206            if all(size(data)~=1) || xtype == 2
     207                data = data.';
     208            end
     209           
     210            % set the field
     211            model_copy.(address_in_model).(name_of_struct)(current_layer).(varname) = data;
     212            %address_to_struct_in_model = setfield(address_to_struct_in_model(current_layer), varname, data)
     213        end
     214        model_copy.(address_in_model).(name_of_struct)(current_layer);
     215        fprintf("Successfully saved layer %s to multidimension struct array\n", num2str(current_layer))
     216    end
     217    fprintf('Successfully recreated multidimensional structure array %s in md.%s\n', name_of_struct, address_in_model)
     218end
     219
     220
     221
    132222
    133223%{
     
    142232    %disp(varname)
    143233    % this is an inversion band-aid
    144     if strcmp(varname, 'inversion_class_name')
     234    if strcmp(varname, 'inversion_class_name') || strcmp(varname, 'name_of_struct') || strcmp(varname, 'solution')
    145235        % we don't need this
    146236    else
     
    149239            %disp(netcdf.inqGrpNameFull(group_location_in_file))
    150240            %disp(class(netcdf.inqGrpNameFull(group_location_in_file)))
    151             adress_to_attr = strrep(netcdf.inqGrpNameFull(group_location_in_file), '/', '.');
    152            
    153             data = netcdf.getVar(group_location_in_file, netcdf.inqVarID(group_location_in_file, varname));
    154            
    155    
    156             % matlab needs to know that ' ' = char()
     241            address_to_attr = strrep(netcdf.inqGrpNameFull(group_location_in_file), '/', '.');
     242            varid = netcdf.inqVarID(group_location_in_file, varname);
     243            data = netcdf.getVar(group_location_in_file, varid);
     244           
     245   
     246            % if we have an empty string
    157247            if xtype == 2 && isempty(all(data))
    158248                data = cell(char());
     249            % if we have an empty cell-char array
    159250            elseif numel(data) == 1 && xtype == 3 && data == -32767
    160251                data = cell(char());
    161252            end
    162             % band-aid for cell-char-arrays:
     253            % band-aid for some cell-char-arrays:
    163254            if xtype == 2 && strcmp(data, 'default')
    164255                data = {'default'};
     
    168259            if all(size(data)~=1) || xtype == 2
    169260                data = data.';
     261            end
     262
     263            % if we have a list of strings
     264            if xtype == 2
     265                try
     266                    if strcmp(netcdf.getAtt(group, varid, "type_is"), 'cell_array_of_strings')
     267                        data = cellstr(data)
     268                    end
     269                catch
     270                    % no attr found so we pass
     271                end
    170272            end
    171273           
     
    174276            %xtype
    175277            if xtype == 10
    176                 arg_to_eval = ['model_copy', adress_to_attr, '.', varname, ' = ' , 'double(data);'];
     278                arg_to_eval = ['model_copy', address_to_attr, '.', varname, ' = ' , 'double(data);'];
    177279                eval(arg_to_eval);
    178280                %disp('saved int64 as int16')
    179281            else
    180                 arg_to_eval = ['model_copy', adress_to_attr, '.', varname, ' = ' , 'data;'];
     282                arg_to_eval = ['model_copy', address_to_attr, '.', varname, ' = ' , 'data;'];
    181283                eval(arg_to_eval);
    182284            end
Note: See TracChangeset for help on using the changeset viewer.