Index: /issm/trunk/src/m/contrib/musselman/read_netCDF.m
===================================================================
--- /issm/trunk/src/m/contrib/musselman/read_netCDF.m	(revision 27874)
+++ /issm/trunk/src/m/contrib/musselman/read_netCDF.m	(revision 27875)
@@ -17,5 +17,5 @@
 
 function model_copy = read_netCDF(filename)
-    fprintf('NetCDF42C v1.1.13\n');
+    fprintf('NetCDF42C v1.1.14\n');
 
     % make a model framework to fill that is in the scope of this file
@@ -109,5 +109,13 @@
         for variable = variables
             [varname, xtype, dimids, numatts] = netcdf.inqVar(group_location_in_file, variable);
-            copy_variable_data_to_new_model(group_location_in_file,varname, xtype);
+
+            % keep an eye out for nested structs:
+            if strcmp(varname, 'this_is_a_nested')
+                is_nested = true;
+                copy_nested_struct(group_location_in_file)
+            else
+                is_nested = false;
+                copy_variable_data_to_new_model(group_location_in_file,varname, xtype);
+            end
         end
     catch ME
@@ -117,17 +125,99 @@
     % try to find groups in current level, if it doesn't work it's because there is nothing there
     try
-        % search for nested groups in the current level to feed back to this function
-        groups = netcdf.inqGrps(group_location_in_file);
-        if not(isempty(groups))
-            for group = groups
-                %disp('found nested group!!')
-                group_id = netcdf.inqNcid(group_location_in_file, netcdf.inqGrpName(group));
-                %disp(netcdf.inqGrpNameFull(group_id))
-                walk_nested_groups(group);
-            end
-        end
-    catch
-    end
-end
+        % if it's not a nested struct, keep searching for subgroups
+        if isnested
+            % do nothing
+        else
+            % search for nested groups in the current level to feed back to this function
+            groups = netcdf.inqGrps(group_location_in_file);
+            if not(isempty(groups))
+                for group = groups
+                    %disp('found nested group!!')
+                    group_id = netcdf.inqNcid(group_location_in_file, netcdf.inqGrpName(group));
+                    %disp(netcdf.inqGrpNameFull(group_id))
+                    walk_nested_groups(group);
+                end
+            end
+        end
+    catch % no nested groups here
+    end
+end
+
+
+
+function copy_nested_struct(group_location_in_file)
+    global model_copy;
+    global NCData;
+    %{
+        A common multidimensional struct array is the 1xn md.results.TransientSolution struct. 
+        The process to recreate is as follows:
+            1. Get the name of the struct from group variable name_of_struct
+            2. Get the fieldnames from the subgroups 
+            3. Recreate the struct with fieldnames 
+            4. Populate the fields with their respective values
+    %}
+
+    % step 1
+    varid = netcdf.inqVarID(group_location_in_file, 'name_of_struct');
+    name_of_struct = netcdf.getVar(group_location_in_file, varid)';
+
+    % step 2
+    subgroups = netcdf.inqGrps(group_location_in_file); % numerical cell array with ID's of subgroups
+    % get single subgroup's data
+    single_subgroup_ID = subgroups(1);
+    subgroup_varids = netcdf.inqVarIDs(single_subgroup_ID);
+    fieldnames = {};
+    for variable = subgroup_varids
+        [varname, xtype, dimids, numatts] = netcdf.inqVar(single_subgroup_ID, variable);
+        fieldnames{end+1} = varname;
+    end
+
+    % step 3
+    address_in_model = strrep(netcdf.inqGrpNameFull(group_location_in_file), '/', '');
+    % we cannot assign a variable to represent this object as MATLAB treats all variables as copies
+    % and not pointers to the same memory address
+    % this means that if address_in_model is more than 1 item, we need to modify the code. For now, 
+    % we just hope this will do
+    
+    model_copy.(address_in_model).(name_of_struct) = struct();
+    
+    % for every fieldname in the subgroup, create an empty field
+    for fieldname = string(fieldnames)
+        model_copy.(address_in_model).(name_of_struct).(fieldname) = {};
+    end
+
+    % use repmat to make the struct array multidimensional along the fields axis
+    number_of_dimensions = numel(subgroups);
+    model_copy.(address_in_model).(name_of_struct) = repmat(model_copy.(address_in_model).(name_of_struct), 1, number_of_dimensions);
+    
+    % step 4
+    % for every layer of the multidimensional struct array, populate the fields
+    for current_layer = 1:number_of_dimensions
+        % choose subgroup
+        current_layer_subgroup_ID = subgroups(current_layer);
+        % get all vars
+        current_layer_subgroup_varids = netcdf.inqVarIDs(current_layer_subgroup_ID);
+        % get individual vars and set fields at layer current_layer
+        for varid = current_layer_subgroup_varids
+            [varname, xtype, dimids, numatts] = netcdf.inqVar(current_layer_subgroup_ID, varid);
+            data = netcdf.getVar(current_layer_subgroup_ID, 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
+            
+            % set the field
+            model_copy.(address_in_model).(name_of_struct)(current_layer).(varname) = data;
+            %address_to_struct_in_model = setfield(address_to_struct_in_model(current_layer), varname, data)
+        end
+        model_copy.(address_in_model).(name_of_struct)(current_layer);
+        fprintf("Successfully saved layer %s to multidimension struct array\n", num2str(current_layer))
+    end
+    fprintf('Successfully recreated multidimensional structure array %s in md.%s\n', name_of_struct, address_in_model)
+end
+
+
+
 
 %{
@@ -142,5 +232,5 @@
     %disp(varname)
     % this is an inversion band-aid
-    if strcmp(varname, 'inversion_class_name')
+    if strcmp(varname, 'inversion_class_name') || strcmp(varname, 'name_of_struct') || strcmp(varname, 'solution')
         % we don't need this
     else
@@ -149,16 +239,17 @@
             %disp(netcdf.inqGrpNameFull(group_location_in_file))
             %disp(class(netcdf.inqGrpNameFull(group_location_in_file)))
-            adress_to_attr = strrep(netcdf.inqGrpNameFull(group_location_in_file), '/', '.');
-            
-            data = netcdf.getVar(group_location_in_file, netcdf.inqVarID(group_location_in_file, varname));
-            
-    
-            % matlab needs to know that ' ' = char()
+            address_to_attr = strrep(netcdf.inqGrpNameFull(group_location_in_file), '/', '.');
+            varid = netcdf.inqVarID(group_location_in_file, varname);
+            data = netcdf.getVar(group_location_in_file, varid);
+            
+    
+            % if we have an empty string
             if xtype == 2 && isempty(all(data))
                 data = cell(char());
+            % if we have an empty cell-char array
             elseif numel(data) == 1 && xtype == 3 && data == -32767
                 data = cell(char());
             end
-            % band-aid for cell-char-arrays:
+            % band-aid for some cell-char-arrays:
             if xtype == 2 && strcmp(data, 'default')
                 data = {'default'};
@@ -168,4 +259,15 @@
             if all(size(data)~=1) || xtype == 2
                 data = data.';
+            end
+
+            % if we have a list of strings
+            if xtype == 2
+                try
+                    if strcmp(netcdf.getAtt(group, varid, "type_is"), 'cell_array_of_strings')
+                        data = cellstr(data)
+                    end
+                catch
+                    % no attr found so we pass
+                end
             end
             
@@ -174,9 +276,9 @@
             %xtype
             if xtype == 10
-                arg_to_eval = ['model_copy', adress_to_attr, '.', varname, ' = ' , 'double(data);'];
+                arg_to_eval = ['model_copy', address_to_attr, '.', varname, ' = ' , 'double(data);'];
                 eval(arg_to_eval);
                 %disp('saved int64 as int16')
             else
-                arg_to_eval = ['model_copy', adress_to_attr, '.', varname, ' = ' , 'data;'];
+                arg_to_eval = ['model_copy', address_to_attr, '.', varname, ' = ' , 'data;'];
                 eval(arg_to_eval);
             end
Index: /issm/trunk/src/m/contrib/musselman/write_netCDF.m
===================================================================
--- /issm/trunk/src/m/contrib/musselman/write_netCDF.m	(revision 27874)
+++ /issm/trunk/src/m/contrib/musselman/write_netCDF.m	(revision 27875)
@@ -11,5 +11,5 @@
 
 function write_netCDF(model_var, filename)
-    disp('MATLAB C2NetCDF4 v1.1.13');
+    disp('MATLAB C2NetCDF4 v1.1.14');
     % model_var = class object to be saved
     % filename = path and name to save file under
@@ -179,9 +179,14 @@
             % there are 2 cases: the location is a struct, the location is a class
             if isstruct(model_subclass)
+                % if the current field is a nested struct assume it has valuable data that needs to be saved
+                if isstruct(location_of_child) && any(size(location_of_child) > 1)
+                    create_group(location_of_child, list_of_layers);
+                
                 % this would mean that the layer above the layer we're interested in is a struct, so
                 % we can navigate our empty model as such
-                if isfield(empty_model_subclass, current_child)
+                elseif isfield(empty_model_subclass, current_child)
                     % the layer we're interested in does exist, we just need to compare states
                     location_of_child_in_empty_model = empty_model_subclass.(current_child);
+
                     % if the current attribute is a numerical array assume it has valuable data that needs to be saved
                     if isnumeric(location_of_child) && logical(numel(location_of_child) > 1)
@@ -264,4 +269,5 @@
     % first we make the group at the highest level (ie, inversion)
     group_name = list_of_layers{1};
+    variable_name = list_of_layers{end};
     
     % if the group is already made, get it's ID instead of creating it again
@@ -284,17 +290,69 @@
         end
     end
-    % if the object is not a variable, we have nothing to save
+    % we may be dealing with an object
     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)
         end
     catch
-        % if that line doesn't work, it means we're dealing with data
-        % Lastly, handle the variable(s)
-        variable_name = list_of_layers{end};
+        % 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);
     end
 end
+
+
+
+function copy_nested_struct(parent_struct_name, address_of_struct, group)
+    %{
+        This function takes a struct of structs and saves them to netcdf. 
+
+        To do this, we get the number of dimensions (substructs) of the parent struct.
+        Next, we iterate through each substruct and record the data. 
+        For each substruct, we create a subgroup of the main struct.
+        For each variable, we create dimensions that are assigned to each subgroup uniquely.
+    %}
+
+    disp("Beginning transfer of nested MATLAB struct to the NetCDF")
+
+    % make sure other systems can flag the nested struct type
+    dimID = netcdf.defDim(group, 'struct', 6);
+    string_var = netcdf.defVar(group, 'this_is_a_nested', "NC_CHAR", dimID);
+    uint_method=uint8('struct').';
+    method_ID = char(uint_method);
+    netcdf.putVar(group, string_var, method_ID);
+
+    % make sure other systems know the name of the parent struct
+    uint_method=uint8(parent_struct_name).';
+    method_ID = char(uint_method);
+    dimID = netcdf.defDim(group, 'struct_name', [numel(method_ID)]);
+    string_var = netcdf.defVar(group, 'name_of_struct', "NC_CHAR", dimID);
+    netcdf.putVar(group, string_var, method_ID);
+    
+    % 'a' will always be 1 and is not useful to us
+    [a, no_of_dims] = size(address_of_struct);
+
+    for substruct = 1:no_of_dims
+        % we start by making subgroups with nice names like "TransientSolution_substruct_44"
+        name_of_subgroup = [parent_struct_name, '_substruct_', num2str(substruct)];
+        subgroup = netcdf.defGrp(group, name_of_subgroup);
+
+        % do some housekeeping to keep track of the current layer
+        current_substruct = address_of_struct(substruct);
+        substruct_fields = fieldnames(current_substruct)'; % transpose because matlab only interates over n x 1 arrays
+        
+        % now we need to iterate over each variable of the nested struct and save it to this new subgroup
+        for variable_name = string(substruct_fields)
+            address_of_child = current_substruct.(variable_name);
+            create_var(variable_name, address_of_child, subgroup);
+        end
+    end
+    fprintf('Succesfully transferred nested MATLAB struct %s to the NetCDF\n', parent_struct_name)
+end
+
 
 
@@ -338,5 +396,5 @@
     
     % This first conditional statement will catch numeric arrays (matrices) of any dimension and save them
-    if any(size(address_of_child)>1) && ~ischar(address_of_child)
+    if any(size(address_of_child)>1) && ~iscellstr(address_of_child) && ~ischar(address_of_child)
         write_numeric_array_to_netcdf(variable_name, address_of_child, group);
 
@@ -349,9 +407,7 @@
         variable = netcdf.defVar(group, variable_name, "NC_DOUBLE", intdim);
 
-    % or a list of strings -- this needs work as it can only handle a list of 1 string
-    elseif iscell(address_of_child) && ischar(address_of_child{1})
-        for i = 1:numel(address_of_child)
-            write_string_to_netcdf(variable_name, address_of_child{i}, group, true);
-        end
+    % or a list of strings
+    elseif iscellstr(address_of_child) || iscell(address_of_child) && ischar(address_of_child{1})
+        write_cell_with_strings(variable_name, address_of_child, group)
         
     % or an empty list
@@ -396,17 +452,51 @@
     end
 
-    disp(['Successfully transferred data from ', variable_name, ' to the NetCDF']);
-end
-
-
-
-
-function write_string_to_netcdf(variable_name, address_of_child, group, list)
+    fprintf('Successfully transferred data from %s to the NetCDF\n', variable_name);
+end
+
+
+function write_cell_with_strings(variable_name, address_of_child, group)
+    %{
+    Write cell array (ie {'one' 'two' 'three'}) to netcdf
+    %}
+    global NetCDF
+    
+    if isempty(address_of_child)
+        % if the char array is empty, save an empty char
+        name_of_dimension = ['char', num2str(0)];
+        try
+            dimID = netcdf.defDim(group, name_of_dimension, 0);
+        catch
+            dimID = netcdf.inqDimID(group, name_of_dimension);
+        end
+        % Now we can make a variable in this dimension:
+        string_var = netcdf.defVar(group, variable_name, "NC_CHAR", [dimID]);
+        % we leave empty now
+    else
+        % covert data to char array
+        method_ID = char(address_of_child);
+    
+        % make dimensions
+        [rows, cols] = size(method_ID);
+        
+        IDDim1 = netcdf.defDim(group,'cols',cols);
+        IDDim2 = netcdf.defDim(group,'rows',rows);
+    
+        % create the variable slot
+        IDVarId = netcdf.defVar(group,variable_name,'NC_CHAR', [IDDim1 IDDim2]);
+    
+        % save the variable
+        netcdf.putVar(group, IDVarId, method_ID'); %transpose
+    
+        % tell other platforms that this is a cell of strings
+        netcdf.putAtt(group, IDVarId, 'type_is','cell_array_of_strings');
+    end
+end
+
+
+function write_string_to_netcdf(variable_name, address_of_child, group)
     % netcdf and strings don't get along.. we have to do it 'custom':
     global NetCDF;
-    % If 'list' is not provided, assume it is false
-    if nargin < 4
-        list = false;
-    end
+
     the_string_to_save = address_of_child;
 
