Changeset 27894


Ignore:
Timestamp:
09/01/23 16:40:56 (19 months ago)
Author:
musselman
Message:

Added functionality for cell arrays of objects like definitions in outputdefinition

Location:
issm/trunk/src/m/contrib/musselman
Files:
2 edited

Legend:

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

    r27889 r27894  
    130130        % keep an eye out for nested structs:
    131131        if strcmp(varname, 'this_is_a_nested')
    132             is_nested = true;
     132            is_object = true;
    133133            model_copy = copy_nested_struct(group_location_in_file, model_copy, NCData, verbose);
     134        elseif strcmp(varname, 'name_of_cell_array')
     135            is_object = true;
     136            model_copy = copy_cell_array_of_objects(variables, group_location_in_file, model_copy, NCData, verbose);
    134137        elseif strcmp(varname, 'solution')
    135138            % band-aid pass..
    136139        else
    137             model_copy = copy_variable_data_to_new_model(group_location_in_file, varname, xtype, model_copy, NCData, verbose);
     140            if logical(exist('is_object', 'var'))
     141                % already handled
     142            else
     143                model_copy = copy_variable_data_to_new_model(group_location_in_file, varname, xtype, model_copy, NCData, verbose);
     144            end
    138145        end
    139146    end
     
    142149    %try
    143150    % if it's a nested struct the function copy_nested_struct has already been called
    144     if logical(exist('is_nested', 'var'))
     151    if logical(exist('is_object', 'var'))
    145152        % do nothing
    146153    else
     
    159166end
    160167
     168
     169% to read cell arrays with objects:
     170function model_copy = copy_cell_array_of_objects(variables, group_location_in_file, model_copy, NCData, verbose);
     171    %{
     172        The structure in netcdf for groups with the name_of_cell_array variable is like:
     173
     174        group: 2x6_cell_array_of_objects {
     175            name_of_cell_array = <name_of_cell_array>
     176
     177            group: Row_1_of_2 {
     178                group: Col_1_of_6 {
     179                    ... other groups can be here that refer to objects
     180                } // group Col_6_of_6
     181            } // group Row_1_of_2
     182
     183            group: Row_2_of_2 {
     184                group: Col_1_of_6 {
     185                    ... other groups can be here that refer to objects
     186                } // group Col_6_of_6
     187            } // group Row_2_of_2
     188        } // group 2x6_cell_array_of_objects
     189
     190        We have to navigate this structure to extract all the data and recreate the
     191        original structure when the model was saved
     192    %}
     193
     194    % get the name_of_cell_array, rows and cols vars
     195    name_of_cell_array_varID = netcdf.inqVarID(group_location_in_file, 'name_of_cell_array');
     196    rows_varID = netcdf.inqVarID(group_location_in_file, 'rows');
     197    cols_varID = netcdf.inqVarID(group_location_in_file, 'cols');
     198
     199    name_of_cell_array = netcdf.getVar(group_location_in_file, name_of_cell_array_varID).'; % transpose
     200    rows = netcdf.getVar(group_location_in_file, rows_varID);
     201    cols = netcdf.getVar(group_location_in_file, cols_varID);
     202
     203    % now we work backwards: make the cell array, fill it in, and assign it to the model
     204
     205    % make the cell array
     206    cell_array_placeholder = cell(rows, cols);
     207
     208    % get subgroups which are elements of the cell array
     209    subgroups = netcdf.inqGrps(group_location_in_file); % numerical cell array with ID's of subgroups
     210
     211    % enter each subgroup, get the data, assign it to the corresponding index of cell array
     212    if rows > 1
     213        % we go over rows
     214        % set index for cell array rows
     215        row_idx = 1;
     216        for row = subgroups
     217            % now columns
     218            columns = netcdf.inqGrps(group_location_in_file);
     219           
     220            % set index for cell array cols
     221            col_idx = 1;
     222            for column = columns
     223                % now variables
     224                current_column_varids = netcdf.inqVarIDs(column);
     225
     226                % if 'class_is_a' or 'this_is_a_nested' variables is present at this level we have to handle them accordingly
     227                try
     228                    class_is_aID = netcdf.inqVarID(column, 'class_is_a');
     229                    col_data = deserialize_class(column, NCData, verbose);
     230                    is_object = true;
     231                catch
     232                end
     233               
     234                try
     235                    this_is_a_nestedID = netcdf.inqVarID(column, 'this_is_a_nested');
     236                    % functionality not supported
     237                    disp('Error: Cell Arrays of structs not yet supported!')
     238                    % copy_nested_struct(column, model_copy, NCData, verbose)
     239                    is_object = true;
     240                catch
     241                end
     242
     243                if logical(exist('is_object', 'var'))
     244                    % already taken care of
     245                else
     246                    % store the variables as normal -- to be added later
     247                    disp('Error: Cell Arrays of mixed objects not yet supported!')
     248                    for var = current_column_varids
     249                        % not supported
     250                    end
     251                end
     252
     253                cell_array_placeholder{row_idx, col_idx} = col_data;
     254                col_idx = col_idx + 1;
     255            end
     256            row_idx = row_idx + 1;
     257        end
     258    else
     259        % set index for cell array
     260        col_idx = 1;
     261        for column = subgroups
     262            % now variables
     263            current_column_varids = netcdf.inqVarIDs(column);
     264
     265            % if 'class_is_a' or 'this_is_a_nested' variables is present at this level we have to handle them accordingly
     266            try
     267                classID = netcdf.inqVarID(column, 'class_is_a');
     268                col_data = deserialize_class(classID, column, NCData, verbose);
     269                is_object = true;
     270            catch ME
     271                rethrow(ME)
     272            end
     273           
     274            try
     275                this_is_a_nestedID = netcdf.inqVarID(column, 'this_is_a_nested');
     276                % functionality not supported
     277                disp('Error: Cell Arrays of structs not yet supported!')
     278                % col_data = copy_nested_struct(column, model_copy, NCData, verbose);
     279                is_object = true;
     280            catch
     281            end
     282            if logical(exist('is_object', 'var'))
     283                % already taken care of
     284            else
     285                % store the variables as normal -- to be added later
     286                disp('Error: Cell Arrays of mixed objects not yet supported!')
     287                for var = current_column_varids
     288                    % col_data = not supported
     289                end
     290            end
     291
     292            cell_array_placeholder{col_idx} = col_data;
     293            col_idx = col_idx + 1;
     294
     295        end
     296    end
     297   
     298
     299    % Like in copy_nested_struct, we can only handle things 1 layer deep.
     300    % assign cell array to model
     301    address_to_attr_list = split(netcdf.inqGrpNameFull(group_location_in_file), '/');
     302    address_to_attr = address_to_attr_list{2};
     303    if isprop(model_copy.(address_to_attr), name_of_cell_array);
     304        model_copy.(address_to_attr).(name_of_cell_array) = cell_array_placeholder;
     305    else
     306        model_copy = addprop(model_copy.(address_to_attr), name_of_cell_array, cell_array_placeholder);
     307    end
     308
     309    if verbose
     310        fprintf("Successfully loaded cell array %s to %s\n", name_of_cell_array,address_to_attr_list{2})
     311    end
     312end
     313
     314
     315
     316
     317function output = deserialize_class(classID, group, NCData, verbose)
     318    %{
     319        This function will recreate a class
     320    %}
     321
     322    % get the name of the class
     323    name = netcdf.getVar(group, classID).';
     324
     325    % instantiate it
     326    class_instance = eval([name, '()']);
     327
     328    % get and assign properties
     329    subgroups = netcdf.inqGrps(group); % numerical cell array with ID's of subgroups
     330
     331    if numel(subgroups) == 1
     332        % get properties
     333        varIDs = netcdf.inqVarIDs(subgroups);
     334        for varID = varIDs
     335            % var metadata
     336            [varname, xtype, dimids, numatts] = netcdf.inqVar(subgroups, varID);
     337            % data
     338            data = netcdf.getVar(subgroups, varID);
     339
     340            % netcdf uses Row Major Order but MATLAB uses Column Major Order so we need to transpose all arrays w/ more than 1 dim
     341            if all(size(data)~=1) || xtype == 2
     342                data = data.';
     343            end
     344
     345            % some classes have permissions... so we skip those
     346            try
     347                % if property already exists, assign new value
     348                if isprop(class_instance, varname)
     349                    class_instance.(varname) = data;
     350                else
     351                    addprop(class_instance, varname, data);
     352                end
     353            catch
     354            end
     355        end
     356    else
     357        % not supported
     358    end
     359    output = class_instance;
     360end
    161361
    162362
     
    192392    % and not pointers to the same memory address
    193393    % 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
     394    % we just hope this will do. An example of a no-solution would be model().abc.def.ghi.field whereas we're only assuming model().abc.field now
    195395   
    196396    model_copy.(address_in_model).(name_of_struct) = struct();
  • issm/trunk/src/m/contrib/musselman/write_netCDF.m

    r27889 r27894  
    159159    if numel(quality_control) ~= numel(class_instance_names)
    160160        disp('Error: The class instance within your model.results class is not currently supported by this application');
    161         disp(class(results_var.(class_instance_name)));
    162161    else
    163162        if verbose
     
    299298
    300299function create_group(location_of_child, list_of_layers, NetCDF, verbose)
     300    %disp(list_of_layers)
    301301    % location_of_child is an object
    302302    % list_of_layers is a list like {'inversion', 'StressbalanceSolution','cost_functions_coefficients'}
     
    324324        end
    325325    end
    326     % we may be dealing with an object
     326    % sometimes objects are passed through twice so we account for that with this try/catch
    327327    try
    328         % if this line works, we're still dealing with a struct and need lower levels
    329         if isempty(fieldnames(location_of_child))
    330             % do nothing
    331         elseif isstruct(location_of_child) && any(size(location_of_child) > 1)
    332             % we have a nested struct
    333             copy_nested_struct(variable_name, location_of_child, group, NetCDF, verbose)
     328        % we may be dealing with an object
     329        % first we screen for structs
     330        if isstruct(location_of_child) % && any(size(location_of_child) > 1) -- this is being tested
     331            % we have a struct
     332            copy_nested_struct(variable_name, location_of_child, group, NetCDF, verbose);
     333       
     334        % now for cell arrays of datastructures:
     335        elseif logical(~isstruct(location_of_child) && iscell(location_of_child) && isobject(location_of_child{1}))
     336            copy_cell_array_of_objects(variable_name, location_of_child, group, NetCDF, verbose);
     337        else
     338            if ~isobject(location_of_child) && ~isstruct(location_of_child)
     339                % we're dealing with raw data
     340                create_var(variable_name, location_of_child, group, NetCDF, verbose);
     341            end
    334342        end
    335343    catch
    336         % if that line doesn't work, it means we're dealing with raw data
    337         % not a nested struct, so we can pass
    338         create_var(variable_name, location_of_child, group, NetCDF, verbose);
    339     end
    340 end
    341 
     344        % do nothing
     345    end
     346end
     347
     348
     349
     350function copy_cell_array_of_objects(variable_name, address_of_child, group, NetCDF, verbose)
     351    % make subgroup to represent the array
     352    [rows, cols] = size(address_of_child);
     353    name_of_subgroup = [num2str(rows), 'x', num2str(cols), '_cell_array_of_objects'];
     354    subgroup = netcdf.defGrp(group, name_of_subgroup);
     355
     356    % save the name of the cell array
     357    write_string_to_netcdf('name_of_cell_array', variable_name, subgroup, NetCDF, verbose);
     358
     359    % save the dimensions of the cell array
     360    create_var('rows', rows, subgroup, NetCDF, verbose);
     361    create_var('cols', cols, subgroup, NetCDF, verbose);
     362
     363    % if this is a multidimensional cell array, iterate over rows here and cols in copy_objects
     364    if rows>1
     365        for row = 1:rows
     366            % make a subgroup for each row
     367            name_of_subgroup = ['Row_', num2str(row), '_of_', num2str(rows)];
     368            subgroup = netcdf.defGrp(group, name_of_subgroup);
     369            copy_objects(address_of_child, subgroup, NetCDF, cols, verbose);
     370        end
     371    else
     372        copy_objects(address_of_child, subgroup, NetCDF, cols, verbose);
     373    end
     374end
     375
     376
     377
     378function copy_objects(address_of_child, group, NetCDF, cols, verbose)
     379    for col = 1:cols
     380        % make subgroup to contain each col of array
     381        name_of_subgroup = ['Col_', num2str(col), '_of_', num2str(cols)];
     382        subgroup = netcdf.defGrp(group, name_of_subgroup);
     383
     384        % get the kind of object we're working with:
     385        if isstruct(address_of_child{col})
     386            % handle structs
     387            name_raw = fields(address_of_child{col});
     388            variable_name = name_raw{1};
     389            copy_nested_struct(variable_name, address_of_child, subgroup, NetCDF, verbose);
     390           
     391        elseif numel(properties(address_of_child{col})) > 0
     392            % handle class instances
     393            copy_class_instance(address_of_child{col}, subgroup, NetCDF, verbose);
     394        else
     395            disp('ERROR: Cell arrays of mixed types are not yet supported in read_netCDF!\n Deserialization will not be able to complete!')
     396            % handle regular datastructures that are already supported
     397            name_raw = fields(address_of_child);
     398            variable_name = name_raw{col};
     399            create_var(variable_name, address_of_child, subgroup, NetCDF, verbose);
     400        end
     401    end
     402end
     403
     404
     405function copy_class_instance(address_of_child, subgroup, NetCDF, verbose)
     406    % get parent class name
     407    name = class(address_of_child);
     408
     409    % save the name of the class
     410    write_string_to_netcdf('class_is_a', name, subgroup, NetCDF, verbose);
     411   
     412    % make subgroup to contain properties
     413    name_of_subgroup = ['Properties_of_', name];
     414    subgroup = netcdf.defGrp(subgroup, name_of_subgroup);
     415
     416    % get properties
     417    props = properties(address_of_child);
     418
     419    for property = 1:length(props)
     420        variable_name = props{property};
     421        create_var(variable_name, address_of_child.(variable_name), subgroup, NetCDF, verbose);
     422    end
     423
     424end
    342425
    343426
     
    345428    %{
    346429        This function takes a struct of structs and saves them to netcdf.
     430
     431        It also works with single structs.
    347432
    348433        To do this, we get the number of dimensions (substructs) of the parent struct.
     
    352437    %}
    353438
    354     if verbose
    355         disp("Beginning transfer of nested MATLAB struct to the NetCDF")
    356     end
    357439    % make a new subgroup to contain all the others:
    358440    group = netcdf.defGrp(group, parent_struct_name);
     
    386468    end
    387469    if verbose
    388         fprintf('Succesfully transferred nested MATLAB struct %s to the NetCDF\n', parent_struct_name)
     470        fprintf(["Succesfully transferred nested MATLAB struct ",  parent_struct_name, " to the NetCDF\n"])
    389471    end
    390472end
     
    583665
    584666function write_numeric_array_to_netcdf(variable_name, address_of_child, group, NetCDF, verbose)
     667
    585668    % get the dimensions we'll need
    586669    intdim = netcdf.inqDimID(NetCDF,'int');
Note: See TracChangeset for help on using the changeset viewer.