Changeset 27878


Ignore:
Timestamp:
08/16/23 21:26:04 (19 months ago)
Author:
musselman
Message:

Added functionality to read_netCDF_commit.py to handle mutlidimensional structure arrays generated in matlab under the results subclass.

File:
1 edited

Legend:

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

    r27874 r27878  
    99from m1qn3inversion import m1qn3inversion
    1010from taoinversion import taoinversion
     11from collections import OrderedDict
     12
    1113
    1214
     
    6668
    6769def make_results_subclasses():
     70    '''
     71        There are 3 possible subclasses: solution, solutionstep, resultsdakota.
     72        In the NetCDF file these are saved as a list of strings. Ie, say there are 2
     73        instances of solution under results, StressbalanceSolution and TransientSolution.
     74        In the NetCDF file we would see solution = "StressbalanceSolution", "TransientSolution"
     75        To deconstruct this, we need to iteratively assign md.results.StressbalanceSolution = solution()
     76        and md.results.TransientSolution = solution() and whatever else.
     77    '''
     78    # start with the subclasses
    6879    for subclass in NCData.groups['results'].variables.keys():
    6980        class_instance = subclass + '()'
    70         class_instance_name = NCData.groups['results'].variables[subclass][:][...].tobytes().decode()
    71         setattr(model_copy.results, class_instance_name, eval(class_instance))
     81
     82        # now handle the instances
     83        for instance in NCData.groups['results'].variables[subclass][:]:
     84            # this is an ndarray of numpy bytes_ that we have to convert to strings
     85            class_instance_name = instance.tobytes().decode('utf-8').strip()
     86            # from here we can make new subclasses named as they were in the model saved
     87            setattr(model_copy.results, class_instance_name, eval(class_instance))
     88            print(f'Successfully created results subclass instance {class_instance} named {class_instance_name}.')
    7289
    7390
     
    90107    # first, we enter the group by: filename.groups['group_name']
    91108    # second we search the current level for variables: filename.groups['group_name'].variables.keys()
     109    # at this step we check for multidimensional structure arrays and filter them out
    92110    # third we get nested group keys by: filename.groups['group_name'].groups.keys()
    93111    # if a variables exists, copy the data to the model framework by calling copy function
     
    95113
    96114    for variable in eval(group_location_in_file + '.variables.keys()'):
    97         location_of_variable_in_file = group_location_in_file + ".variables['" + str(variable) + "']"
    98         # group_location_in_file is like filename.groups['group1'].groups['group1.1'].groups['group1.1.1']
    99         # Define the regex pattern to match the groups within brackets
    100         pattern = r"\['(.*?)'\]"
    101         # 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
    102         matches = re.findall(pattern, location_of_variable_in_file)
    103         variable_name = matches[-1]
    104         location_of_variable_in_model = '.'.join(matches[:-1])
    105         copy_variable_data_to_new_model(location_of_variable_in_file, location_of_variable_in_model, variable_name)
    106 
    107     for nested_group in eval(group_location_in_file + '.groups.keys()'):
    108         new_nested_group = group_location_in_file + ".groups['" + str(nested_group) + "']"
    109         walk_nested_groups(new_nested_group)
     115        if variable == 'this_is_a_nested' and 'results' in group_location_in_file:
     116            # have to do some string deconstruction to get the name of the class instance/last group from 'NetCDF.groups['group1'].groups['group1.1']'
     117            pattern = r"\['(.*?)'\]"
     118            matches = re.findall(pattern, group_location_in_file)
     119            name_of_struct = matches[-1] #eval(group_location_in_file + ".variables['solution']")
     120            copy_multidimensional_results_struct(group_location_in_file, name_of_struct)
     121            istruct = True
     122
     123        elif variable == 'this_is_a_nested' and 'qmu' in group_location_in_file:
     124            print('encountered qmu structure that is not yet supported.')
     125            # have to do some string deconstruction to get the name of the class instance/last group from 'NetCDF.groups['group1'].groups['group1.1']'
     126            #pattern = r"\['(.*?)'\]"
     127            #matches = re.findall(pattern, group_location_in_file)
     128            #name_of_struct = matches[-1] #eval(group_location_in_file + ".variables['solution']")
     129            #name_of_struct = eval(group_location_in_file + ".variables['']")
     130            #copy_multidimensional_qmu_struct(group_location_in_file, name_of_struct)
     131            isstruct = True
     132   
     133        else:
     134            location_of_variable_in_file = group_location_in_file + ".variables['" + str(variable) + "']"
     135            # group_location_in_file is like filename.groups['group1'].groups['group1.1'].groups['group1.1.1']
     136            # Define the regex pattern to match the groups within brackets
     137            pattern = r"\['(.*?)'\]"
     138            # 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
     139            matches = re.findall(pattern, location_of_variable_in_file)
     140            variable_name = matches[-1]
     141            location_of_variable_in_model = '.'.join(matches[:-1])
     142            copy_variable_data_to_new_model(location_of_variable_in_file, location_of_variable_in_model, variable_name)
     143           
     144    if 'istruct' in locals():
     145        pass
     146    else:
     147        for nested_group in eval(group_location_in_file + '.groups.keys()'):
     148            new_nested_group = group_location_in_file + ".groups['" + str(nested_group) + "']"
     149            walk_nested_groups(new_nested_group)
     150
     151
     152
     153'''
     154    MATLAB has Multidimensional Structure Arrays in 2 known classes: results and qmu.
     155    The python classes results.py and qmu.py emulate this MATLAB object in their own
     156    unique ways. The functions in this script will assign data to either of these
     157    classes such that the final structure is compatible with its parent class.
     158'''
     159
     160def copy_multidimensional_results_struct(group_location_in_file, name_of_struct):
     161    '''
     162    A common multidimensional array is the 1xn md.results.TransientSolution object.
     163
     164    The way that this object emulates the MATLAB mutli-dim. struct. array is with
     165    the solution().steps attr. which is a list of solutionstep() instances
     166        The process to recreate is as follows:
     167            1. Get instance of solution() with solution variable (the instance is made in make_results_subclasses)
     168            2. For each subgroup, create a solutionstep() class instance
     169             2a. Populate the instance with the key:value pairs
     170             2b. Append the instance to the solution().steps list
     171    '''
     172    # step 1
     173    class_instance_name = name_of_struct
     174   
     175    # for some reason steps is not already a list
     176    setattr(model_copy.results.__dict__[class_instance_name], 'steps', list())
     177
     178    steps = model_copy.results.__dict__[class_instance_name].steps
     179   
     180    # step 2
     181    layer = 1
     182    for subgroup in eval(group_location_in_file + ".groups.keys()"):
     183        solutionstep_instance = solutionstep()
     184        # step 2a
     185        subgroup_location_in_file = group_location_in_file + ".groups['" + subgroup + "']"
     186        for key in eval(subgroup_location_in_file + ".variables.keys()"):
     187            value = eval(subgroup_location_in_file + ".variables['" + str(key) + "'][:]")
     188            setattr(solutionstep_instance, key, value)
     189        # step 2b
     190        steps.append(solutionstep_instance)
     191        print('Succesfully saved layer ' + str(layer) + ' to results.' + str(class_instance_name) + ' struct.')
     192        layer += 1
     193
     194    print('Successfully recreated results structure ' + str(class_instance_name))
    110195
    111196
     
    113198def copy_variable_data_to_new_model(location_of_variable_in_file, location_of_variable_in_model, variable_name):
    114199    # as simple as navigating to the location_of_variable_in_model and setting it equal to the location_of_variable_in_file
    115 
    116200    # NetCDF4 has a property called "_FillValue" that sometimes saves empty lists, so we have to catch those
    117201    FillValue = -9223372036854775806
    118 
    119     # results band-aid...
    120     if str(location_of_variable_in_model + '.' + variable_name) =='results.solutionstep':
    121         pass
    122     # qmu band-aid
    123     elif 'qmu.statistics.method' in str(location_of_variable_in_model + '.' + variable_name):
    124         pass
    125     # handle any strings:
    126     elif 'char' in eval(location_of_variable_in_file + '.dimensions[0]'):
    127         setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:][...].tobytes().decode()'))
    128     # handle ndarrays + lists
    129     elif len(eval(location_of_variable_in_file + '[:]'))>1:
    130         # check for bool
    131         try: # there is only one datatype assigned the attribute 'units' and that is bool, so anything else will go right to except
    132             if eval(location_of_variable_in_file + '.units') == 'bool':
    133                 setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, np.array(eval(location_of_variable_in_file + '[:]'), dtype = bool))
    134             else:
     202    try:
     203        # results band-aid...
     204        #print(str(location_of_variable_in_model + '.' + variable_name))
     205        if str(location_of_variable_in_model + '.' + variable_name) in ['results.solutionstep', 'results.solution', 'results.resultsdakota']:
     206            pass
     207        # qmu band-aid
     208        elif 'qmu.statistics.method' in str(location_of_variable_in_model + '.' + variable_name):
     209            pass
     210        # handle any strings:
     211        elif 'char' in eval(location_of_variable_in_file + '.dimensions[0]'):
     212            setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:][...].tobytes().decode()'))
     213        # handle ndarrays + lists
     214        elif len(eval(location_of_variable_in_file + '[:]'))>1:
     215            # check for bool
     216            try: # there is only one datatype assigned the attribute 'units' and that is bool, so anything else will go right to except
     217                if eval(location_of_variable_in_file + '.units') == 'bool':
     218                    setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, np.array(eval(location_of_variable_in_file + '[:]'), dtype = bool))
     219                else:
     220                    setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:]'))
     221            except:
    135222                setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:]'))
    136         except:
    137             setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:]'))
    138     # catch everything else
     223        # catch everything else
     224        else:
     225            # check for FillValue. use try/except because try block will only work on datatypes like int64, float, single element lists/arrays ect and not nd-arrays/n-lists etc
     226            try:
     227                # this try block will only work on single ints/floats/doubles and will skip to the except block for all other cases
     228                if FillValue == eval(location_of_variable_in_file + '[:][0]'):
     229                    setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, [])
     230                else:
     231                    # we have to convert numpy datatypes to native python types with .item()
     232                    var_to_save = eval(location_of_variable_in_file + '[:][0]')  # note the [0] on the end
     233                    setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, var_to_save.item())
     234            except:
     235                    setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:]'))
     236    except AttributeError:
     237        copy_variable_data_to_new_model_dict(location_of_variable_in_file, location_of_variable_in_model)
     238
     239    print('Successfully saved ' + location_of_variable_in_model + '.' + variable_name + ' to model.')
     240
     241
     242
     243
     244
     245
     246def copy_variable_data_to_new_model_dict(location_of_variable_in_file, location_of_variable_in_model):
     247    # as simple as navigating to the location_of_variable_in_model and setting it equal to the location_of_variable_in_file
     248
     249    # NetCDF4 has a property called "_FillValue" that sometimes saves empty lists, so we have to catch those
     250    FillValue = -9223372036854775806
     251
     252    # the key will be the last item in the location
     253    key = ''.join(location_of_variable_in_model.split('.')[-1])
     254
     255    # update the location to point to the dict instead of the dict key
     256    location_of_variable_in_model = '.'.join(location_of_variable_in_model.split('.')[:-1])
     257
     258    # verify we're working with a dict:
     259    if isinstance(eval('model_copy.' + location_of_variable_in_model), OrderedDict):
     260        dict_object = eval('model_copy.' + location_of_variable_in_model)
     261       
     262        # handle any strings:
     263        if 'char' in eval(location_of_variable_in_file + '.dimensions[0]'):
     264            data = eval(location_of_variable_in_file + '[:][...].tobytes().decode()')
     265            dict_object.update({key: data})
     266           
     267        # handle ndarrays + lists
     268        elif len(eval(location_of_variable_in_file + '[:]'))>1:
     269            # check for bool
     270            try: # there is only one datatype assigned the attribute 'units' and that is bool, so anything else will go right to except
     271                if eval(location_of_variable_in_file + '.units') == 'bool':
     272                    data = np.array(eval(location_of_variable_in_file + '[:]'), dtype = bool)
     273                    dict_object.update({key: data})
     274                else:
     275                    data = eval(location_of_variable_in_file + '[:]')
     276                    dict_object.update({key: data})
     277            except:
     278                data = eval(location_of_variable_in_file + '[:]')
     279                dict_object.update({key: data})
     280        # catch everything else
     281        else:
     282            # check for FillValue. use try/except because try block will only work on datatypes like int64, float, single element lists/arrays ect and not nd-arrays/n-lists etc
     283            try:
     284                # this try block will only work on single ints/floats/doubles and will skip to the except block for all other cases
     285                if FillValue == eval(location_of_variable_in_file + '[:][0]'):
     286                    dict_object.update({key: []})
     287                else:
     288                    # we have to convert numpy datatypes to native python types with .item()
     289                    var_to_save = eval(location_of_variable_in_file + '[:][0]')  # note the [0] on the end
     290                    dict_object.update({key:  var_to_save.item()})
     291            except:
     292                data = eval(location_of_variable_in_file + '[:]')
     293                dict_object.update({key: data})
    139294    else:
    140         # check for FillValue. use try/except because try block will only work on datatypes like int64, float, single element lists/arrays ect and not nd-arrays/n-lists etc
    141         try:
    142             # this try block will only work on single ints/floats/doubles and will skip to the except block for all other cases
    143             if FillValue == eval(location_of_variable_in_file + '[:][0]'):
    144                 setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, [])
    145             else:
    146                 # we have to convert numpy datatypes to native python types with .item()
    147                 var_to_save = eval(location_of_variable_in_file + '[:][0]')  # note the [0] on the end
    148                 setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, var_to_save.item())
    149         except:
    150             setattr(eval('model_copy.' + location_of_variable_in_model), variable_name, eval(location_of_variable_in_file + '[:]'))
    151     print('Successfully saved ' + location_of_variable_in_model + '.' + variable_name + ' to model.')
    152 
    153 
    154 
    155 
    156 
    157 
    158 
    159 
    160 
    161 
    162 
    163 
    164 
    165 
    166 
    167 
    168 
    169 
    170 
    171 
    172 
    173 
    174 
    175 
    176 
    177 
    178 
    179 
     295        print(f"Unrecognized object was saved and cannot be reconstructed: {location_of_variable_in_model}")
     296
     297
     298
     299
     300
     301
     302
     303
     304
     305
     306
     307
     308
     309
     310
     311
     312
     313
     314
     315
     316
Note: See TracChangeset for help on using the changeset viewer.