Changeset 27900


Ignore:
Timestamp:
09/06/23 20:20:00 (19 months ago)
Author:
musselman
Message:

Added functionality for arrays of objects like outputdefinition.defintions to all files.

Location:
issm/trunk-jpl/src/m/netcdf
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • issm/trunk-jpl/src/m/netcdf/read_netCDF.m

    r27891 r27900  
    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-jpl/src/m/netcdf/read_netCDF.py

    r27891 r27900  
    1010from taoinversion import taoinversion
    1111from collections import OrderedDict
     12import sys
     13from massfluxatgate import massfluxatgate
     14
    1215
    1316
     
    2528def read_netCDF(filename, verbose = False):
    2629    if verbose:
    27         print('NetCDF42C v1.1.13')
    28 
    29     # check if path exists
    30     if path.exists(filename):
     30        print('NetCDF42C v1.2.0')
     31
     32    '''
     33    filename = path and name to save file under
     34    verbose = T/F = show or muted log statements. Naturally muted
     35    '''
     36
     37    # this is a precaution so that data is not lost
     38    try:
     39        # check if path exists
     40        if path.exists(filename):
     41            if verbose:
     42                print('Opening {} for reading'.format(filename))
     43            else: pass
     44   
     45            # open the given netCDF4 file
     46            NCData = Dataset(filename, 'r')
     47            # remove masks from numpy arrays for easy conversion
     48            NCData.set_auto_mask(False)
     49        else:
     50            return 'The file you entered does not exist or cannot be found in the current directory'
     51       
     52        # in order to handle some subclasses in the results class, we have to utilize this band-aid
     53        # there will likely be more band-aids added unless a class name library is created with all class names that might be added to a md
     54        try:
     55            # if results has meaningful data, save the name of the subclass and class instance
     56            NCData.groups['results']
     57            make_results_subclasses(NCData, verbose)
     58        except:
     59            pass
     60   
     61        # similarly, we need to check and see if we have an m1qn3inversion class instance
     62        try:
     63            NCData.groups['inversion']
     64            check_inversion_class(NCData, verbose)
     65        except:
     66            pass
     67       
     68        # walk through each group looking for subgroups and variables
     69        for group in NCData.groups.keys():
     70            if 'debris' in group:
     71                pass
     72            else:
     73                # have to send a custom name to this function: filename.groups['group']
     74                name = "NCData.groups['" + str(group) + "']"
     75                walk_nested_groups(name, NCData, verbose)
     76       
    3177        if verbose:
    32             print('Opening {} for reading'.format(filename))
    33         else: pass
    34 
    35         # open the given netCDF4 file
    36         NCData = Dataset(filename, 'r')
    37         # remove masks from numpy arrays for easy conversion
    38         NCData.set_auto_mask(False)
    39     else:
    40         print('The file you entered does not exist or cannot be found in the current directory')
    41         return print()
    42    
    43     # continuation of band-aid for results class
    44     try:
    45         NCData.groups['results']
    46         make_results_subclasses(NCData, verbose)
    47     except:
    48         pass
    49 
    50     # similarly, we need to check and see if we have an m1qn3inversion class instance
    51     try:
    52         NCData.groups['inversion']
    53         check_inversion_class(NCData, verbose)
    54     except:
    55         pass
    56    
    57     # walk through each group looking for subgroups and variables
    58     for group in NCData.groups.keys():
    59         if 'debris' in group:
    60             pass
    61         else:
    62             # have to send a custom name to this function: filename.groups['group']
    63             name = "NCData.groups['" + str(group) + "']"
    64             walk_nested_groups(name, NCData, verbose)
    65    
    66     if verbose:
    67         print("Model Successfully Loaded.")
    68     return model_copy
    69 
     78            print("Model Successfully Loaded.")
     79           
     80        NCData.close()
     81       
     82        return model_copy
     83
     84    # just in case something unexpected happens
     85    except Exception as e:
     86        if 'NCData' in locals():
     87            NCData.close()
     88        raise e
    7089
    7190def make_results_subclasses(NCData, verbose = False):
     
    111130    # first, we enter the group by: filename.groups['group_name']
    112131    # second we search the current level for variables: filename.groups['group_name'].variables.keys()
    113     # at this step we check for multidimensional structure arrays and filter them out
     132    # at this step we check for multidimensional structure arrays/ arrays of objects and filter them out
    114133    # third we get nested group keys by: filename.groups['group_name'].groups.keys()
    115     # if a variables exists, copy the data to the model framework by calling copy function
    116134    # if a nested groups exist, repeat all
    117135
    118136    for variable in eval(group_location_in_file + '.variables.keys()'):
    119         if variable == 'this_is_a_nested' and 'results' in group_location_in_file:
    120             # have to do some string deconstruction to get the name of the class instance/last group from 'NetCDF.groups['group1'].groups['group1.1']'
    121             pattern = r"\['(.*?)'\]"
    122             matches = re.findall(pattern, group_location_in_file)
    123             name_of_struct = matches[-1] #eval(group_location_in_file + ".variables['solution']")
    124             copy_multidimensional_results_struct(group_location_in_file, name_of_struct, NCData)
    125             istruct = True
    126 
    127         elif variable == 'this_is_a_nested' and 'qmu' in group_location_in_file:
    128             print('encountered qmu structure that is not yet supported.')
    129             # have to do some string deconstruction to get the name of the class instance/last group from 'NetCDF.groups['group1'].groups['group1.1']'
    130             #pattern = r"\['(.*?)'\]"
    131             #matches = re.findall(pattern, group_location_in_file)
    132             #name_of_struct = matches[-1] #eval(group_location_in_file + ".variables['solution']")
    133             #name_of_struct = eval(group_location_in_file + ".variables['']")
    134             #copy_multidimensional_qmu_struct(group_location_in_file, name_of_struct)
    135             isstruct = True
    136    
    137         else:
    138             location_of_variable_in_file = group_location_in_file + ".variables['" + str(variable) + "']"
    139             # group_location_in_file is like filename.groups['group1'].groups['group1.1'].groups['group1.1.1']
    140             # Define the regex pattern to match the groups within brackets
    141             pattern = r"\['(.*?)'\]"
    142             # Use regex to find all matches and return something like 'group1.group1.1.group1.1.1 ...' where the last value is the name of the variable
    143             matches = re.findall(pattern, location_of_variable_in_file)
    144             variable_name = matches[-1]
    145             location_of_variable_in_model = '.'.join(matches[:-1])
    146             copy_variable_data_to_new_model(location_of_variable_in_file, location_of_variable_in_model, variable_name, NCData, verbose=verbose)
    147            
    148     if 'istruct' in locals():
     137        if 'is_object' not in locals():
     138            if variable == 'this_is_a_nested' and 'results' in group_location_in_file and 'qmu' not in group_location_in_file:
     139                # have to do some string deconstruction to get the name of the class instance/last group from 'NetCDF.groups['group1'].groups['group1.1']'
     140                pattern = r"\['(.*?)'\]"
     141                matches = re.findall(pattern, group_location_in_file)
     142                name_of_struct = matches[-1] #eval(group_location_in_file + ".variables['solution']")
     143                deserialize_nested_results_struct(group_location_in_file, name_of_struct, NCData)
     144                is_object = True
     145   
     146            elif variable == 'name_of_cell_array':
     147                # reconstruct an array of elements
     148                deserialize_array_of_objects(group_location_in_file, model_copy, NCData, verbose)
     149                is_object = True
     150   
     151            elif variable == 'this_is_a_nested' and 'qmu' in group_location_in_file:
     152                if verbose:
     153                    print('encountered qmu structure that is not yet supported.')
     154                else: pass
     155                   
     156                is_object = True
     157       
     158            else:
     159                location_of_variable_in_file = group_location_in_file + ".variables['" + str(variable) + "']"
     160                # group_location_in_file is like filename.groups['group1'].groups['group1.1'].groups['group1.1.1']
     161                # Define the regex pattern to match the groups within brackets
     162                pattern = r"\['(.*?)'\]"
     163                # Use regex to find all matches and return something like 'group1.group1.1.group1.1.1 ...' where the last value is the name of the variable
     164                matches = re.findall(pattern, location_of_variable_in_file)
     165                variable_name = matches[-1]
     166                location_of_variable_in_model = '.'.join(matches[:-1])
     167                deserialize_data(location_of_variable_in_file, location_of_variable_in_model, variable_name, NCData, verbose=verbose)
     168
     169    # if one of the variables above was an object, further subclasses will be taken care of when reconstructing it
     170    if 'is_object' in locals():
    149171        pass
    150172    else:
     
    162184'''
    163185
    164 def copy_multidimensional_results_struct(group_location_in_file, name_of_struct, NCData, verbose = False):
     186def deserialize_nested_results_struct(group_location_in_file, name_of_struct, NCData, verbose = False):
    165187    '''
    166188    A common multidimensional array is the 1xn md.results.TransientSolution object.
     
    202224
    203225
    204 def copy_variable_data_to_new_model(location_of_variable_in_file, location_of_variable_in_model, variable_name, NCData, verbose = False):
     226
     227def deserialize_array_of_objects(group_location_in_file, model_copy, NCData, verbose):
     228    '''
     229        The structure in netcdf for groups with the name_of_cell_array variable is like:
     230
     231        group: 2x6_cell_array_of_objects {
     232            name_of_cell_array = <name_of_cell_array>
     233
     234            group: Row_1_of_2 {
     235                group: Col_1_of_6 {
     236                    ... other groups can be here that refer to objects
     237                } // group Col_6_of_6
     238            } // group Row_1_of_2
     239
     240            group: Row_2_of_2 {
     241                group: Col_1_of_6 {
     242                    ... other groups can be here that refer to objects
     243                } // group Col_6_of_6
     244            } // group Row_2_of_2
     245        } // group 2x6_cell_array_of_objects
     246
     247        We have to navigate this structure to extract all the data and recreate the
     248        original structure when the model was saved
     249    '''
     250
     251    if verbose:
     252        print(f"Loading array of objects.")
     253
     254    # get the name_of_cell_array, rows and cols vars
     255    name_of_cell_array_varID = eval(group_location_in_file + ".variables['name_of_cell_array']")
     256    rows_varID = eval(group_location_in_file + ".variables['rows']")
     257    cols_varID = eval(group_location_in_file + ".variables['cols']")
     258
     259    name_of_cell_array = name_of_cell_array_varID[:][...].tobytes().decode()
     260    rows = rows_varID[:]
     261    cols = cols_varID[:]
     262
     263    # now we work backwards: make the array, fill it in, and assign it to the model
     264
     265    # make the array
     266    array = list()
     267
     268    subgroups = eval(group_location_in_file + ".groups") #.keys()")
     269
     270    # enter each subgroup, get the data, assign it to the corresponding index of cell array
     271    if rows > 1:
     272        # we go over rows
     273        # set index for rows
     274        row_idx = 0
     275        for row in list(subgroups):
     276            # make list for each row
     277            current_row = list()
     278            columns = subgroups[str(row)].groups.keys()
     279
     280            # set index for columns
     281            col_idx = 0
     282
     283            # iterate over columns
     284            for col in list(columns):
     285                # now get the variables
     286                current_col_vars = columns.groups[str(col)].variables
     287
     288                # check for special datastructures               
     289                if "class_is_a" in current_col_vars:
     290                    class_name = subgroups[str(col)].variables['class_is_a'][:][...].tobytes().decode()
     291                    col_data = deserialize_class_instance(class_name, columns.groups[str(col)], NCData, verbose)
     292                    is_object = True
     293                elif "this_is_a_nested" in current_col_vars:
     294                    # functionality not yet supported
     295                    print('Error: Cell Arrays of structs not yet supported!')
     296                    is_object = True
     297                else:
     298                    if 'is_object_' in locals():
     299                        pass
     300                        # already taken care of
     301                    else:
     302                        # store the variables as normal -- to be added later
     303                        print('Error: Arrays of mixed objects not yet supported!')
     304                        for var in current_col_vars:
     305                            # this is where that functionality would be handled
     306                            pass
     307                col_idx += 1
     308                # add the entry to our row list
     309                current_row.append(col_data)
     310
     311            # add the list of columns to the array
     312            array.append(current_row)
     313            row_idx += 1
     314
     315    else:
     316        # set index for columns
     317        col_idx = 0
     318
     319        # iterate over columns
     320        for col in list(subgroups):
     321            # now get the variables
     322            current_col_vars = subgroups[str(col)].variables
     323           
     324            # check for special datastructures
     325            if "class_is_a" in current_col_vars:
     326                class_name = subgroups[str(col)].variables['class_is_a'][:][...].tobytes().decode()
     327                col_data = deserialize_class_instance(class_name, subgroups[str(col)], NCData, verbose)
     328                is_object = True
     329            elif "this_is_a_nested" in current_col_vars:
     330                # functionality not yet supported
     331                print('Error: Cell Arrays of structs not yet supported!')
     332                is_object = True
     333            else:
     334                if 'is_object_' in locals():
     335                    pass
     336                    # already taken care of
     337                else:
     338                    # store the variables as normal -- to be added later
     339                    print('Error: Arrays of mixed objects not yet supported!')
     340                    for var in current_col_vars:
     341                        # this is where that functionality would be handled
     342                        pass
     343            col_idx += 1
     344            # add the list of columns to the array
     345            array.append(col_data)
     346
     347    # finally, add the attribute to the model
     348    pattern = r"\['(.*?)'\]"
     349    matches = re.findall(pattern, group_location_in_file)
     350    variable_name = matches[0]
     351    setattr(model_copy.__dict__[variable_name], name_of_cell_array, array)
     352
     353    if verbose:
     354        print(f"Successfully loaded array of objects: {name_of_cell_array} to {variable_name}")
     355
     356
     357
     358def deserialize_class_instance(class_name, group, NCData, verbose=False):
     359
     360    if verbose:
     361        print(f"Loading class: {class_name}")
     362
     363    # this function requires the class module to be imported into the namespace of this file.
     364    # we make a custom error in case the class module is not in the list of imported classes.
     365    # most ISSM classes are imported by from <name> import <name>
     366    class ModuleError(Exception):
     367        pass
     368   
     369    if class_name not in sys.modules:
     370        raise ModuleError(str('Model requires the following class to be imported from a module: ' + class_name + ". Please add the import to read_netCDF.py in order to continue."))
     371
     372    # Instantiate the class
     373    class_instance = eval(class_name + "()")
     374
     375    # Get and assign properties
     376    subgroups = list(group.groups.keys())
     377
     378    if len(subgroups) == 1:
     379        # Get properties
     380        subgroup = group[subgroups[0]]
     381        varIDs = subgroup.variables.keys()
     382        for varname in varIDs:
     383            # Variable metadata
     384            var = subgroup[varname]
     385
     386            # Data
     387            if 'char' in var.dimensions[0]:
     388                data = var[:][...].tobytes().decode()
     389            else:
     390                data = var[:]
     391
     392            # Some classes may have permissions, so we skip those
     393            try:
     394                setattr(class_instance, varname, data)
     395            except:
     396                pass
     397    else:
     398        # Not supported
     399        pass
     400
     401    if verbose:
     402        print(f"Successfully loaded class instance {class_name} to model")
     403    return class_instance
     404
     405
     406
     407def deserialize_data(location_of_variable_in_file, location_of_variable_in_model, variable_name, NCData, verbose = False):
    205408    # as simple as navigating to the location_of_variable_in_model and setting it equal to the location_of_variable_in_file
    206409    # NetCDF4 has a property called "_FillValue" that sometimes saves empty lists, so we have to catch those
     
    208411    try:
    209412        # results band-aid...
    210         #print(str(location_of_variable_in_model + '.' + variable_name))
    211413        if str(location_of_variable_in_model + '.' + variable_name) in ['results.solutionstep', 'results.solution', 'results.resultsdakota']:
    212414            pass
     
    240442                    else:
    241443                        # we have to convert numpy datatypes to native python types with .item()
    242                         setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, var_to_save.item())                       
     444                        setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, var_to_save.item())
    243445            except:
    244                     setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:]'))
     446                setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:]'))
    245447    except AttributeError:
    246         copy_variable_data_to_new_model_dict(location_of_variable_in_file, location_of_variable_in_model, NCData, verbose=verbose)
     448        deserialize_dict(location_of_variable_in_file, location_of_variable_in_model, NCData, verbose=verbose)
    247449
    248450    if verbose:
     
    250452
    251453
    252 def copy_variable_data_to_new_model_dict(location_of_variable_in_file, location_of_variable_in_model, NCData, verbose = False):
    253     # as simple as navigating to the location_of_variable_in_model and setting it equal to the location_of_variable_in_file
    254 
    255     # NetCDF4 has a property called "_FillValue" that sometimes saves empty lists, so we have to catch those
     454
     455def deserialize_dict(location_of_variable_in_file, location_of_variable_in_model, NCData, verbose = False):
    256456    FillValue = -9223372036854775806
    257457
  • issm/trunk-jpl/src/m/netcdf/write_netCDF.m

    r27891 r27900  
    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');
  • issm/trunk-jpl/src/m/netcdf/write_netCDF.py

    r27891 r27900  
    1515'''
    1616Given a md, this set of functions will perform the following:
    17     1. Enter each nested class of the md.
    18     2. View each attribute of each nested class.
    19     3. Compare state of attribute in the model to an empty model class.
    20     4. If states are identical, pass.
    21     5. Otherwise, create nested groups named after class structure.
    22     6. Create variable named after class attribute and assign value to it.
     17    1. View each attribute of each nested class.
     18    2. Compare state of attribute in the model to an empty model.
     19    3. If states are identical, pass. (except for np arrays which will always be saved)
     20    4. Otherwise, create nested groups named after class structure.
     21    5. Create variable named after class attribute and assign value to it.
    2322'''
    2423
     
    2625def write_netCDF(md, filename: str, verbose = False):
    2726    if verbose:
    28         print('Python C2NetCDF4 v1.1.14')
     27        print('Python C2NetCDF4 v1.2.0')
    2928    else: pass
    3029    '''
    31     md = model() class instance to be saved
     30    md = model class instance to be saved
    3231    filename = path and name to save file under
    33     verbose = T/F muted or show log statements. Naturally muted
     32    verbose = T/F = show or muted log statements. Naturally muted
    3433    '''
    35    
    36     # Create a NetCDF file to write to
    37     NetCDF = make_NetCDF(filename, verbose)
    38    
    39     # Create an instance of an empty md class to compare md_var against
    40     empty_model = model()
    41 
    42     # Walk through the md class and compare subclass states to empty_model
    43     walk_through_model(md, empty_model, NetCDF, verbose)
    44 
    45     # in order to handle some subclasses in the results class, we have to utilize this band-aid
    46     # there will likely be more band-aids added unless a class name library is created with all class names that might be added to a md
     34    # this is a precaution so that data is not lost
    4735    try:
    48         # if results has meaningful data, save the name of the subclass and class instance
    49         NetCDF.groups['results']
    50         results_subclasses_bandaid(md, NetCDF, verbose)
    51         # otherwise, ignore
    52     except KeyError:
    53         pass
     36        # Create a NCData file to write to
     37        NCData = create_NetCDF(filename, verbose)
    5438       
    55     NetCDF.close()
    56     if verbose:
    57         print('Model successfully saved as NetCDF4')
    58     else: pass
    59    
    60 
    61 def results_subclasses_bandaid(md, NetCDF, verbose = False):
     39        # Create an instance of an empty md class to compare md_var against
     40        empty_model = model()
     41   
     42        # Walk through the md class and compare subclass states to empty_model
     43        walk_through_model(md, empty_model, NCData, verbose)
     44   
     45        # in order to handle some subclasses in the results class, we have to utilize this band-aid
     46        # there will likely be more band-aids added unless a class name library is created with all class names that might be added to a md
     47        try:
     48            # if results has meaningful data, save the name of the subclass and class instance
     49            NCData.groups['results']
     50            results_subclasses_bandaid(md, NCData, verbose)
     51            # otherwise, ignore
     52        except KeyError:
     53            pass
     54           
     55        NCData.close()
     56        if verbose:
     57            print('Model successfully saved as NetCDF4')
     58        else: pass
     59
     60    # just in case something unexpected happens
     61    except Exception as e:
     62        if 'NCData' in locals():
     63            NCData.close()
     64        raise e
     65   
     66
     67def results_subclasses_bandaid(md, NCData, verbose = False):
    6268    # since the results class may have nested classes within it, we need to record the name of the
    6369    # nested class instance variable as it appears in the md that we're trying to save
    6470    quality_control = []
    6571
    66     # we save lists of instances to the netcdf
     72    # we save lists of instances to the NCData
    6773    solutions = []
    6874    solutionsteps = []
     
    7278        if verbose:
    7379            print(class_instance_name)
    74         # for each class instance in results, see which class its from and record that info in the netcdf to recreate structure later
     80        # for each class instance in results, see which class its from and record that info in the NCData to recreate structure later
    7581        # check to see if there is a solutionstep class instance
    7682        if isinstance(md.results.__dict__[class_instance_name],solutionstep):
     
    8995
    9096    if solutionsteps != []:
    91         write_string_to_netcdf(variable_name=str('solutionstep'), address_of_child=solutionsteps, group=NetCDF.groups['results'], list=True, NetCDF=NetCDF, verbose=verbose)
     97        serialize_string(variable_name=str('solutionstep'), address_of_child=solutionsteps, group=NCData.groups['results'], list=True, NCData=NCData, verbose=verbose)
    9298
    9399    if solutions != []:
    94         write_string_to_netcdf(variable_name=str('solution'), address_of_child=solutions, group=NetCDF.groups['results'], list=True, NetCDF=NetCDF, verbose=verbose)
     100        serialize_string(variable_name=str('solution'), address_of_child=solutions, group=NCData.groups['results'], list=True, NCData=NCData, verbose=verbose)
    95101
    96102    if resultsdakotas != []:
    97         write_string_to_netcdf(variable_name=str('resultsdakota'), address_of_child=resultsdakotas, group=NetCDF.groups['results'], list=True, NetCDF=NetCDF, verbose=verbose)
     103        serialize_string(variable_name=str('resultsdakota'), address_of_child=resultsdakotas, group=NCData.groups['results'], list=True, NCData=NCData, verbose=verbose)
    98104
    99105   
     
    107113
    108114
    109 def make_NetCDF(filename: str, verbose = False):
     115def create_NetCDF(filename: str, verbose = False):
    110116    # If file already exists delete / rename it
    111117    if os.path.exists(filename):
     
    122128    else:
    123129        # Otherwise create the file and define it globally so other functions can call it
    124         NetCDF = Dataset(filename, 'w', format='NETCDF4')
    125         NetCDF.history = 'Created ' + time.ctime(time.time())
    126         NetCDF.createDimension('Unlim', None)  # unlimited dimension
    127         NetCDF.createDimension('float', 1)     # single integer dimension
    128         NetCDF.createDimension('int', 1)       # single float dimension
     130        NCData = Dataset(filename, 'w', format='NETCDF4')
     131        NCData.history = 'Created ' + time.ctime(time.time())
     132        NCData.createDimension('Unlim', None)  # unlimited dimension
     133        NCData.createDimension('float', 1)     # single integer dimension
     134        NCData.createDimension('int', 1)       # single float dimension
    129135   
    130136    if verbose:
    131137        print('Successfully created ' + filename)
    132138
    133     return NetCDF
    134 
    135 
    136 def walk_through_model(md, empty_model, NetCDF, verbose= False):
    137     # Iterate over first layer of md_var attributes and assume this first layer is only classes
     139    return NCData
     140
     141
     142def walk_through_model(md, empty_model, NCData, verbose= False):
     143    # Iterate over first layer of md attributes and assume this first layer is only classes
    138144    for group in md.__dict__.keys():
    139145        address = md.__dict__[group]
    140146        empty_address = empty_model.__dict__[group]
    141         # we need to record the layers of the md so we can save them to the netcdf file
     147        # we need to record the layers of the md so we can save them to the NCData file
    142148        layers = [group]
    143149
    144150        # Recursively walk through subclasses
    145         walk_through_subclasses(address, empty_address, layers, NetCDF, empty_model, verbose)       
    146 
    147 
    148 def walk_through_subclasses(address, empty_address, layers: list, NetCDF, empty_model, verbose = False):
     151        walk_through_subclasses(address, empty_address, layers, NCData, empty_model, verbose)       
     152
     153
     154def walk_through_subclasses(address, empty_address, layers: list, NCData, empty_model, verbose = False):
    149155    # See if we have an object with keys or a not
    150156    try:
     
    155161    if is_object:
    156162        # enter the subclass, see if it has nested classes and/or attributes
    157         # then compare attributes between mds and write to netCDF if they differ
     163        # then compare attributes between mds and write to NCData if they differ
    158164        # if subclass found, walk through it and repeat
    159165        for child in address.__dict__.keys():
     
    165171            address_of_child = address.__dict__[child]
    166172           
    167             # if the current object is a results.<solution> object and has the steps attr it needs special treatment
     173            # if the current object is a results.<solution> object and has nonzero steps attr it needs special treatment
    168174            if isinstance(address_of_child, solution) and len(address_of_child.steps) != 0:
    169                 create_group(address_of_child, current_layer, is_struct = True, NetCDF=NetCDF, verbose = verbose)
     175                create_group(address_of_child, current_layer, is_struct = True, is_special_list = False,  NCData=NCData, verbose = verbose)
     176
     177            # if the current object is a list of objects (currently only filters for lists/arrays of classes)
     178            elif isinstance(address_of_child, list) and len(address_of_child) > 0 and hasattr(address_of_child[0], '__dict__'):
     179                create_group(address_of_child, current_layer, is_struct = False, is_special_list = True, NCData=NCData, verbose = verbose)
    170180
    171181            # if the variable is an array, assume it has relevant data (this is because the next line cannot evaluate "==" with an array)
    172182            elif isinstance(address_of_child, np.ndarray):
    173                 create_group(address_of_child, current_layer, is_struct = False, NetCDF=NetCDF, verbose = verbose)
     183                create_group(address_of_child, current_layer, is_struct = False, is_special_list = False,  NCData=NCData, verbose = verbose)
    174184           
    175             # see if the child exists in the empty md. If not, record it in the netcdf
     185            # see if the child exists in the empty md. If not, record it in the NCData
    176186            else:
    177187                try:
     
    181191                    # if the attributes are identical we don't need to save anything
    182192                    if address_of_child == address_of_child_in_empty_class:
    183                         walk_through_subclasses(address_of_child, address_of_child_in_empty_class, current_layer, NetCDF, empty_model, verbose)
    184    
    185                     # If it has been modified, record it in the NetCDF file
     193                        walk_through_subclasses(address_of_child, address_of_child_in_empty_class, current_layer, NCData, empty_model, verbose)
     194   
     195                    # If it has been modified, record it in the NCData file
    186196                    else:
    187                         create_group(address_of_child, current_layer, is_struct = False, NetCDF=NetCDF, verbose = verbose)
    188                         walk_through_subclasses(address_of_child, address_of_child_in_empty_class, current_layer, NetCDF, empty_model, verbose)
    189    
    190                 except KeyError: # record in netcdf and continue to walk thru md
    191                     walk_through_subclasses(address_of_child, empty_address, current_layer, NetCDF, empty_model, verbose)
    192                     create_group(address_of_child, current_layer, is_struct = False, NetCDF=NetCDF, verbose = verbose)
     197                        create_group(address_of_child, current_layer, is_struct = False, is_special_list = False,  NCData=NCData, verbose = verbose)
     198                        walk_through_subclasses(address_of_child, address_of_child_in_empty_class, current_layer, NCData, empty_model, verbose)
     199   
     200                except KeyError: # record in NCData and continue to walk thru md
     201                    walk_through_subclasses(address_of_child, empty_address, current_layer, NCData, empty_model, verbose)
     202                    create_group(address_of_child, current_layer, is_struct = False, is_special_list = False,  NCData=NCData, verbose = verbose)
    193203    else: pass
    194204
    195205
    196 def create_group(address_of_child, layers, is_struct = False, NetCDF=None, verbose = False):
     206def create_group(address_of_child, layers, is_struct = False, is_special_list = False,  NCData=None, verbose = False):
    197207
    198208    # Handle the first layer of the group(s)
    199209    group_name = layers[0]
     210   
     211    # try to make a group unless the group is already made
    200212    try:
    201         group = NetCDF.createGroup(str(group_name))
     213        group = NCData.createGroup(str(group_name))
    202214    except:
    203         group = NetCDF.groups[str(group_name)]
     215        group = NCData.groups[str(group_name)]
    204216
    205217    # need to check if inversion or m1qn3inversion class
    206218    if group_name == 'inversion':
    207         check_inversion_class(address_of_child, NetCDF, verbose)
     219        check_inversion_class(address_of_child, NCData, verbose)
    208220    else: pass
    209221
    210     # if the data is nested, create nested groups to match class structure
     222    # if the data is nested in md, create nested groups to match class structure
    211223    if len(layers) > 2:
    212224        for name in layers[1:-1]:
     
    214226                group = group.createGroup(str(name))
    215227            except:
    216                 group = NetCDF.groups[str(name)]
     228                group = NCData.groups[str(name)]
    217229    else: pass
    218230
     
    220232    if is_struct:
    221233        parent_struct_name = layers[-1]
    222         copy_nested_results_struct(parent_struct_name, address_of_child, group, NetCDF, verbose)
     234        serialize_nested_results_struct(parent_struct_name, address_of_child, group, NCData, verbose)
     235
     236    elif is_special_list:
     237        list_name = layers[-1]
     238        serialize_array_of_objects(list_name, address_of_child, group, NCData, verbose)
    223239   
    224240    else:
    225241        variable_name = layers[-1]
    226         create_var(variable_name, address_of_child, group, NetCDF, verbose)
     242        serialize_var(variable_name, address_of_child, group, NCData, verbose)
    227243           
    228244
     
    242258
    243259@singleton
    244 def check_inversion_class(address_of_child, NetCDF, verbose = False):
     260def check_inversion_class(address_of_child, NCData, verbose = False):
    245261    # need to make sure that we have the right inversion class: inversion, m1qn3inversion, taoinversion
    246262    if isinstance(address_of_child, m1qn3inversion):
    247         write_string_to_netcdf(variable_name=str('inversion_class_name'), address_of_child=str('m1qn3inversion'), group=NetCDF.groups['inversion'], NetCDF=NetCDF, verbose = verbose)
     263        serialize_string(variable_name=str('inversion_class_name'), address_of_child=str('m1qn3inversion'), group=NCData.groups['inversion'], NCData=NCData, verbose = verbose)
    248264        if verbose:
    249265            print('Successfully saved inversion class instance ' + 'm1qn3inversion')
    250266    elif isinstance(address_of_child, taoinversion):
    251         write_string_to_netcdf(variable_name=str('inversion_class_name'), address_of_child=str('taoinversion'), group=NetCDF.groups['inversion'], NetCDF=NetCDF, verbose = verbose)
     267        serialize_string(variable_name=str('inversion_class_name'), address_of_child=str('taoinversion'), group=NCData.groups['inversion'], NCData=NCData, verbose = verbose)
    252268        if verbose:
    253269            print('Successfully saved inversion class instance ' + 'taoinversion')
    254270    else:
    255         write_string_to_netcdf(variable_name=str('inversion_class_name'), address_of_child=str('inversion'), group=NetCDF.groups['inversion'], NetCDF=NetCDF, verbose = verbose)
     271        serialize_string(variable_name=str('inversion_class_name'), address_of_child=str('inversion'), group=NCData.groups['inversion'], NCData=NCData, verbose = verbose)
    256272        if verbose:
    257273            print('Successfully saved inversion class instance ' + 'inversion')
    258274
    259275
    260 def copy_nested_results_struct(parent_struct_name, address_of_struct, group, NetCDF, verbose = False):
     276def serialize_nested_results_struct(parent_struct_name, address_of_struct, group, NCData, verbose = False):
    261277    '''
    262         This function takes a solution class instance and saves the solutionstep instances from <solution>.steps to the netcdf.
    263 
    264         To do this, we get the number of dimensions (substructs) of the parent struct.
     278        This function takes a results.solution class instance and saves the solutionstep instances from <solution>.steps to the NCData.
     279
     280        To do this, we get the number of dimensions (substructs) of the parent struct (list).
    265281        Next, we iterate through each substruct and record the data.
    266282        For each substruct, we create a subgroup of the main struct.
     
    268284    '''
    269285    if verbose:
    270         print("Beginning transfer of nested MATLAB struct to the NetCDF")
     286        print("Beginning transfer of nested MATLAB struct to the NCData")
    271287   
    272288    # make a new subgroup to contain all the others:
     
    274290
    275291    # make sure other systems can flag the nested struct type
    276     write_string_to_netcdf('this_is_a_nested', 'struct', group, list=False, NetCDF=NetCDF, verbose = verbose)
     292    serialize_string('this_is_a_nested', 'struct', group, list=False, NCData=NCData, verbose = verbose)
    277293
    278294    # other systems know the name of the parent struct because it's covered by the results/qmu functions above
     
    290306        for variable in substruct_fields:
    291307            address_of_child = current_substruct.__dict__[variable]
    292             create_var(variable, address_of_child, subgroup, NetCDF, verbose = verbose)
     308            serialize_var(variable, address_of_child, subgroup, NCData, verbose = verbose)
    293309   
    294310    if verbose:
    295         print(f'Successfully transferred struct {parent_struct_name} to the NetCDF\n')
    296    
     311        print(f'Successfully transferred struct {parent_struct_name} to the NCData\n')
     312
     313
     314
     315
     316def serialize_array_of_objects(list_name, address_of_child, group, NCData, verbose):
     317    if verbose:
     318        print(f"Serializing array of objects.")
     319   
     320    # Get the dimensions of the cell array
     321    if len(np.shape(address_of_child)) > 1:
     322        rows, cols = np.shape(address_of_child)
     323    else: rows, cols = 1, np.shape(address_of_child)[0]
     324
     325    # Make subgroup to represent the array
     326    name_of_subgroup = f"{str(rows)}x{str(cols)}_cell_array_of_objects"
     327    subgroup = group.createGroup(name_of_subgroup)
     328
     329    # Save the name of the cell array
     330    serialize_string('name_of_cell_array', list_name, subgroup, NCData, verbose)
     331
     332    # Save the dimensions of the cell array
     333    rowsID = subgroup.createVariable('rows', int, ('int',))
     334    colsID = subgroup.createVariable('cols', int, ('int',))
     335    rowsID[:] = rows
     336    colsID[:] = cols
     337
     338
     339    # If this is a multidimensional cell array, iterate over rows here and cols in serialize_objects
     340    if rows > 1:
     341        for row in range(rows):
     342            # Make a subgroup for each row
     343            name_of_subgroup = f"Row_{row+1}_of_{rows}"
     344            subgroup = group.createGroup(name_of_subgroup)
     345            serialize_objects(address_of_child, subgroup, NCData, cols, verbose)
     346    else:
     347        serialize_objects(address_of_child, subgroup, NCData, cols, verbose)
    297348       
    298 def create_var(variable_name, address_of_child, group, NetCDF, verbose = False):
     349    if verbose:
     350        print(f"Successfully serialized array of objects: {list_name}")
     351
     352
     353def serialize_objects(address_of_child, group, NCData, cols, verbose):
     354    for col in range(cols):
     355        # Make subgroup to contain each col of array
     356        name_of_subgroup = f'Col_{col+1}_of_{cols}'
     357        subgroup = group.createGroup(name_of_subgroup)
     358
     359        # index the current item
     360        variable = address_of_child[col]
     361
     362        # Get the kind of object we're working with:
     363        # see if it's a solution instance
     364        if isinstance(variable, solution) and len(variable.steps) != 0:
     365            pass
     366            # this needs more work...
     367       
     368        # see if it's a general class -- assume ISSM classes all have __dict__
     369        elif hasattr(variable, '__dict__'):
     370            # Handle class instances
     371            serialize_class_instance(variable, subgroup, NCData, verbose)
     372        else:
     373            print('ERROR: Cell arrays of mixed types are not yet supported in read_NCData!')
     374            print('Deserialization will not be able to complete!')
     375            # Handle regular data structures that are already supported
     376            serialize_var(variable_name, variable, subgroup, NCData, verbose)
     377
     378
     379def serialize_class_instance(instance, group, NCData, verbose):
     380    # get parent class name:
     381    name = instance.__class__.__name__
     382
     383    # save the name of the class
     384    serialize_string(variable_name='class_is_a', address_of_child=name, group=group, NCData=NCData, verbose = verbose)
     385
     386    # make subgroup to contain attributes
     387    name_of_subgroup = 'Properties_of_' + name
     388    subgroup = group.createGroup(name_of_subgroup)
     389
     390    # get attributes
     391    keys = instance.__dict__.keys()
     392
     393    for name in keys:
     394        serialize_var(name, instance.__dict__[name], subgroup, NCData, verbose)
     395   
     396
     397
     398       
     399def serialize_var(variable_name, address_of_child, group, NCData, verbose = False):
    299400    # There are lots of different variable types that we need to handle from the md class
    300401   
    301402    # This first conditional statement will catch numpy arrays of any dimension and save them
    302403    if isinstance(address_of_child, np.ndarray):
    303         write_numpy_array_to_netcdf(variable_name, address_of_child, group, NetCDF, verbose=verbose)
     404        serialize_numpy_array(variable_name, address_of_child, group, NCData, verbose=verbose)
    304405   
    305406    # check if it's an int
     
    315416    # or a string
    316417    elif isinstance(address_of_child, str):
    317         write_string_to_netcdf(variable_name, address_of_child, group, NetCDF, verbose=verbose)
     418        serialize_string(variable_name, address_of_child, group, NCData, verbose=verbose)
    318419
    319420    #or a bool
    320421    elif isinstance(address_of_child, bool) or isinstance(address_of_child, np.bool_):
    321         # netcdf4 can't handle bool types like True/False so we convert all to int 1/0 and add an attribute named units with value 'bool'
     422        # NetCDF can't handle bool types like True/False so we convert all to int 1/0 and add an attribute named units with value 'bool'
    322423        variable = group.createVariable(variable_name, int, ('int',))
    323424        variable[:] = int(address_of_child)
     
    331432    elif isinstance(address_of_child,list) and isinstance(address_of_child[0],str):
    332433        for string in address_of_child:
    333             write_string_to_netcdf(variable_name, string, group, list=True, NetCDF=NetCDF, verbose=verbose)
     434            serialize_string(variable_name, string, group, list=True, NCData=NCData, verbose=verbose)
    334435
    335436    # or a regular list
     
    352453   
    353454    if verbose:
    354         print(f'Successfully transferred data from {variable_name} to the NetCDF')
    355    
    356 
    357 def write_string_to_netcdf(variable_name, address_of_child, group, list=False, NetCDF=None, verbose = False):
    358     # netcdf and strings dont get along.. we have to do it 'custom':
     455        print(f'Successfully transferred data from {variable_name} to the NCData')
     456   
     457
     458def serialize_string(variable_name, address_of_child, group, list=False, NCData=None, verbose = False):
     459    # NCData and strings dont get along.. we have to do it 'custom':
    359460    # if we hand it an address we need to do it this way:
    360     if list == True:
    361         """
    362         Save a list of strings to a NetCDF file.
    363    
     461    if list:
     462        """   
    364463        Convert a list of strings to a numpy.char_array with utf-8 encoded elements
    365         and size rows x cols with each row the same # of cols and save to NetCDF
     464        and size rows x cols with each row the same # of cols and save to NCData
    366465        as char array.
    367466        """
     
    399498                arr[i] = new_list[i]
    400499   
    401             # save array to netcdf file
     500            # save array to NCData file
    402501            string_var[:] = arr
    403502
     
    431530
    432531
    433 def write_numpy_array_to_netcdf(variable_name, address_of_child, group, NetCDF, verbose = False):
    434     # to make a nested array in netCDF, we have to get the dimensions of the array,
    435     # create corresponding dimensions in the netCDF file, then we can make a variable
    436     # in the netCDF with dimensions identical to those in the original array
     532def serialize_numpy_array(variable_name, address_of_child, group, NCData, verbose = False):
     533    # to make a nested array in NCData, we have to get the dimensions of the array,
     534    # create corresponding dimensions in the NCData file, then we can make a variable
     535    # in the NCData with dimensions identical to those in the original array
    437536   
    438537    # start by getting the data type at the lowest level in the array:
Note: See TracChangeset for help on using the changeset viewer.