Index: /issm/trunk/src/m/contrib/musselman/read_netCDF.m
===================================================================
--- /issm/trunk/src/m/contrib/musselman/read_netCDF.m	(revision 27893)
+++ /issm/trunk/src/m/contrib/musselman/read_netCDF.m	(revision 27894)
@@ -130,10 +130,17 @@
         % keep an eye out for nested structs:
         if strcmp(varname, 'this_is_a_nested')
-            is_nested = true;
+            is_object = true;
             model_copy = copy_nested_struct(group_location_in_file, model_copy, NCData, verbose);
+        elseif strcmp(varname, 'name_of_cell_array')
+            is_object = true;
+            model_copy = copy_cell_array_of_objects(variables, group_location_in_file, model_copy, NCData, verbose);
         elseif strcmp(varname, 'solution')
             % band-aid pass..
         else
-            model_copy = copy_variable_data_to_new_model(group_location_in_file, varname, xtype, model_copy, NCData, verbose);
+            if logical(exist('is_object', 'var'))
+                % already handled
+            else
+                model_copy = copy_variable_data_to_new_model(group_location_in_file, varname, xtype, model_copy, NCData, verbose);
+            end
         end
     end
@@ -142,5 +149,5 @@
     %try
     % if it's a nested struct the function copy_nested_struct has already been called
-    if logical(exist('is_nested', 'var'))
+    if logical(exist('is_object', 'var'))
         % do nothing
     else
@@ -159,4 +166,197 @@
 end
 
+
+% to read cell arrays with objects: 
+function model_copy = copy_cell_array_of_objects(variables, group_location_in_file, model_copy, NCData, verbose);
+    %{
+        The structure in netcdf for groups with the name_of_cell_array variable is like:
+
+        group: 2x6_cell_array_of_objects {
+            name_of_cell_array = <name_of_cell_array>
+
+            group: Row_1_of_2 {
+                group: Col_1_of_6 {
+                    ... other groups can be here that refer to objects
+                } // group Col_6_of_6
+            } // group Row_1_of_2
+
+            group: Row_2_of_2 {
+                group: Col_1_of_6 {
+                    ... other groups can be here that refer to objects
+                } // group Col_6_of_6
+            } // group Row_2_of_2
+        } // group 2x6_cell_array_of_objects
+
+        We have to navigate this structure to extract all the data and recreate the 
+        original structure when the model was saved
+    %}
+
+    % get the name_of_cell_array, rows and cols vars
+    name_of_cell_array_varID = netcdf.inqVarID(group_location_in_file, 'name_of_cell_array');
+    rows_varID = netcdf.inqVarID(group_location_in_file, 'rows');
+    cols_varID = netcdf.inqVarID(group_location_in_file, 'cols');
+
+    name_of_cell_array = netcdf.getVar(group_location_in_file, name_of_cell_array_varID).'; % transpose
+    rows = netcdf.getVar(group_location_in_file, rows_varID);
+    cols = netcdf.getVar(group_location_in_file, cols_varID);
+
+    % now we work backwards: make the cell array, fill it in, and assign it to the model
+
+    % make the cell array
+    cell_array_placeholder = cell(rows, cols);
+
+    % get subgroups which are elements of the cell array
+    subgroups = netcdf.inqGrps(group_location_in_file); % numerical cell array with ID's of subgroups
+
+    % enter each subgroup, get the data, assign it to the corresponding index of cell array
+    if rows > 1
+        % we go over rows
+        % set index for cell array rows
+        row_idx = 1;
+        for row = subgroups
+            % now columns
+            columns = netcdf.inqGrps(group_location_in_file);
+            
+            % set index for cell array cols
+            col_idx = 1;
+            for column = columns
+                % now variables
+                current_column_varids = netcdf.inqVarIDs(column);
+
+                % if 'class_is_a' or 'this_is_a_nested' variables is present at this level we have to handle them accordingly
+                try
+                    class_is_aID = netcdf.inqVarID(column, 'class_is_a');
+                    col_data = deserialize_class(column, NCData, verbose);
+                    is_object = true;
+                catch
+                end
+                
+                try
+                    this_is_a_nestedID = netcdf.inqVarID(column, 'this_is_a_nested');
+                    % functionality not supported
+                    disp('Error: Cell Arrays of structs not yet supported!')
+                    % copy_nested_struct(column, model_copy, NCData, verbose)
+                    is_object = true;
+                catch
+                end
+
+                if logical(exist('is_object', 'var'))
+                    % already taken care of
+                else
+                    % store the variables as normal -- to be added later
+                    disp('Error: Cell Arrays of mixed objects not yet supported!')
+                    for var = current_column_varids
+                        % not supported
+                    end
+                end
+
+                cell_array_placeholder{row_idx, col_idx} = col_data;
+                col_idx = col_idx + 1;
+            end
+            row_idx = row_idx + 1;
+        end 
+    else
+        % set index for cell array
+        col_idx = 1;
+        for column = subgroups
+            % now variables
+            current_column_varids = netcdf.inqVarIDs(column);
+
+            % if 'class_is_a' or 'this_is_a_nested' variables is present at this level we have to handle them accordingly
+            try
+                classID = netcdf.inqVarID(column, 'class_is_a');
+                col_data = deserialize_class(classID, column, NCData, verbose);
+                is_object = true;
+            catch ME
+                rethrow(ME)
+            end
+            
+            try
+                this_is_a_nestedID = netcdf.inqVarID(column, 'this_is_a_nested');
+                % functionality not supported
+                disp('Error: Cell Arrays of structs not yet supported!')
+                % col_data = copy_nested_struct(column, model_copy, NCData, verbose);
+                is_object = true;
+            catch
+            end
+            if logical(exist('is_object', 'var'))
+                % already taken care of
+            else
+                % store the variables as normal -- to be added later
+                disp('Error: Cell Arrays of mixed objects not yet supported!')
+                for var = current_column_varids
+                    % col_data = not supported
+                end
+            end
+
+            cell_array_placeholder{col_idx} = col_data;
+            col_idx = col_idx + 1;
+
+        end 
+    end
+   
+
+    % Like in copy_nested_struct, we can only handle things 1 layer deep.
+    % assign cell array to model
+    address_to_attr_list = split(netcdf.inqGrpNameFull(group_location_in_file), '/');
+    address_to_attr = address_to_attr_list{2};
+    if isprop(model_copy.(address_to_attr), name_of_cell_array);
+        model_copy.(address_to_attr).(name_of_cell_array) = cell_array_placeholder;
+    else
+        model_copy = addprop(model_copy.(address_to_attr), name_of_cell_array, cell_array_placeholder);
+    end
+
+    if verbose
+        fprintf("Successfully loaded cell array %s to %s\n", name_of_cell_array,address_to_attr_list{2})
+    end
+end
+
+
+
+
+function output = deserialize_class(classID, group, NCData, verbose)
+    %{
+        This function will recreate a class
+    %}
+
+    % get the name of the class
+    name = netcdf.getVar(group, classID).';
+
+    % instantiate it
+    class_instance = eval([name, '()']);
+
+    % get and assign properties
+    subgroups = netcdf.inqGrps(group); % numerical cell array with ID's of subgroups
+
+    if numel(subgroups) == 1
+        % get properties
+        varIDs = netcdf.inqVarIDs(subgroups);
+        for varID = varIDs
+            % var metadata
+            [varname, xtype, dimids, numatts] = netcdf.inqVar(subgroups, varID);
+            % data
+            data = netcdf.getVar(subgroups, varID);
+
+            % netcdf uses Row Major Order but MATLAB uses Column Major Order so we need to transpose all arrays w/ more than 1 dim
+            if all(size(data)~=1) || xtype == 2
+                data = data.';
+            end
+
+            % some classes have permissions... so we skip those
+            try
+                % if property already exists, assign new value
+                if isprop(class_instance, varname)
+                    class_instance.(varname) = data;
+                else
+                    addprop(class_instance, varname, data);
+                end
+            catch
+            end
+        end
+    else
+        % not supported
+    end
+    output = class_instance;
+end
 
 
@@ -192,5 +392,5 @@
     % and not pointers to the same memory address
     % this means that if address_in_model has more than 1 layer, we need to modify the code. For now, 
-    % we just hope this will do. An example of a no-solution would be model().abc.def.ghi.field
+    % 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
     
     model_copy.(address_in_model).(name_of_struct) = struct();
Index: /issm/trunk/src/m/contrib/musselman/write_netCDF.m
===================================================================
--- /issm/trunk/src/m/contrib/musselman/write_netCDF.m	(revision 27893)
+++ /issm/trunk/src/m/contrib/musselman/write_netCDF.m	(revision 27894)
@@ -159,5 +159,4 @@
     if numel(quality_control) ~= numel(class_instance_names)
         disp('Error: The class instance within your model.results class is not currently supported by this application');
-        disp(class(results_var.(class_instance_name)));
     else
         if verbose
@@ -299,4 +298,5 @@
 
 function create_group(location_of_child, list_of_layers, NetCDF, verbose)
+    %disp(list_of_layers)
     % location_of_child is an object
     % list_of_layers is a list like {'inversion', 'StressbalanceSolution','cost_functions_coefficients'}
@@ -324,20 +324,103 @@
         end
     end
-    % we may be dealing with an object
+    % sometimes objects are passed through twice so we account for that with this try/catch
     try
-        % if this line works, we're still dealing with a struct and need lower levels
-        if isempty(fieldnames(location_of_child))
-            % do nothing
-        elseif isstruct(location_of_child) && any(size(location_of_child) > 1)
-            % we have a nested struct
-            copy_nested_struct(variable_name, location_of_child, group, NetCDF, verbose)
+        % we may be dealing with an object
+        % first we screen for structs
+        if isstruct(location_of_child) % && any(size(location_of_child) > 1) -- this is being tested
+            % we have a struct
+            copy_nested_struct(variable_name, location_of_child, group, NetCDF, verbose);
+        
+        % now for cell arrays of datastructures:
+        elseif logical(~isstruct(location_of_child) && iscell(location_of_child) && isobject(location_of_child{1}))
+            copy_cell_array_of_objects(variable_name, location_of_child, group, NetCDF, verbose);
+        else
+            if ~isobject(location_of_child) && ~isstruct(location_of_child)
+                % we're dealing with raw data
+                create_var(variable_name, location_of_child, group, NetCDF, verbose);
+            end
         end
     catch
-        % if that line doesn't work, it means we're dealing with raw data
-        % not a nested struct, so we can pass
-        create_var(variable_name, location_of_child, group, NetCDF, verbose);
-    end
-end
-
+        % do nothing
+    end
+end
+
+
+
+function copy_cell_array_of_objects(variable_name, address_of_child, group, NetCDF, verbose)
+    % make subgroup to represent the array
+    [rows, cols] = size(address_of_child);
+    name_of_subgroup = [num2str(rows), 'x', num2str(cols), '_cell_array_of_objects'];
+    subgroup = netcdf.defGrp(group, name_of_subgroup);
+
+    % save the name of the cell array
+    write_string_to_netcdf('name_of_cell_array', variable_name, subgroup, NetCDF, verbose);
+
+    % save the dimensions of the cell array
+    create_var('rows', rows, subgroup, NetCDF, verbose);
+    create_var('cols', cols, subgroup, NetCDF, verbose);
+
+    % if this is a multidimensional cell array, iterate over rows here and cols in copy_objects
+    if rows>1
+        for row = 1:rows
+            % make a subgroup for each row
+            name_of_subgroup = ['Row_', num2str(row), '_of_', num2str(rows)];
+            subgroup = netcdf.defGrp(group, name_of_subgroup);
+            copy_objects(address_of_child, subgroup, NetCDF, cols, verbose);
+        end
+    else
+        copy_objects(address_of_child, subgroup, NetCDF, cols, verbose);
+    end
+end
+
+
+
+function copy_objects(address_of_child, group, NetCDF, cols, verbose)
+    for col = 1:cols
+        % make subgroup to contain each col of array
+        name_of_subgroup = ['Col_', num2str(col), '_of_', num2str(cols)];
+        subgroup = netcdf.defGrp(group, name_of_subgroup);
+
+        % get the kind of object we're working with:
+        if isstruct(address_of_child{col})
+            % handle structs
+            name_raw = fields(address_of_child{col});
+            variable_name = name_raw{1};
+            copy_nested_struct(variable_name, address_of_child, subgroup, NetCDF, verbose);
+            
+        elseif numel(properties(address_of_child{col})) > 0
+            % handle class instances
+            copy_class_instance(address_of_child{col}, subgroup, NetCDF, verbose);
+        else
+            disp('ERROR: Cell arrays of mixed types are not yet supported in read_netCDF!\n Deserialization will not be able to complete!')
+            % handle regular datastructures that are already supported
+            name_raw = fields(address_of_child);
+            variable_name = name_raw{col};
+            create_var(variable_name, address_of_child, subgroup, NetCDF, verbose);
+        end
+    end
+end
+
+
+function copy_class_instance(address_of_child, subgroup, NetCDF, verbose)
+    % get parent class name
+    name = class(address_of_child);
+
+    % save the name of the class
+    write_string_to_netcdf('class_is_a', name, subgroup, NetCDF, verbose);
+    
+    % make subgroup to contain properties
+    name_of_subgroup = ['Properties_of_', name];
+    subgroup = netcdf.defGrp(subgroup, name_of_subgroup);
+
+    % get properties
+    props = properties(address_of_child);
+
+    for property = 1:length(props)
+        variable_name = props{property};
+        create_var(variable_name, address_of_child.(variable_name), subgroup, NetCDF, verbose);
+    end
+
+end
 
 
@@ -345,4 +428,6 @@
     %{
         This function takes a struct of structs and saves them to netcdf. 
+
+        It also works with single structs.
 
         To do this, we get the number of dimensions (substructs) of the parent struct.
@@ -352,7 +437,4 @@
     %}
 
-    if verbose
-        disp("Beginning transfer of nested MATLAB struct to the NetCDF")
-    end
     % make a new subgroup to contain all the others:
     group = netcdf.defGrp(group, parent_struct_name);
@@ -386,5 +468,5 @@
     end
     if verbose
-        fprintf('Succesfully transferred nested MATLAB struct %s to the NetCDF\n', parent_struct_name)
+        fprintf(["Succesfully transferred nested MATLAB struct ",  parent_struct_name, " to the NetCDF\n"])
     end
 end
@@ -583,4 +665,5 @@
 
 function write_numeric_array_to_netcdf(variable_name, address_of_child, group, NetCDF, verbose)
+
     % get the dimensions we'll need
     intdim = netcdf.inqDimID(NetCDF,'int');
