Source code for mor.wrapper.replaceAndSave

# -*- coding: utf-8 -*-
'''
**Functions that will be use during wrapping**

**Global Variable**

.. py:attribute:: forceFieldImplemented

    List of ForceField implemented and there associated HyperReduced one
    This will be use to *swap* forcefield during scene creation with :py:func:`.MORreplace`

.. py:attribute:: myModel

    OrderedDict that will contain:
        - has key Sofa.node.name
        - has items list of tuple (type,argument) each one coresponding to a component

.. py:attribute:: myMORModel

list of tuple (type,argument) each one coresponding to a component

.. py:attribute:: pathToUpdate


.. py:attribute:: forcefield

----------------------------------------------

**Methods**

'''
from collections import OrderedDict


forceFieldImplemented = {   'TetrahedralCorotationalFEMForceField':('HyperReducedTetrahedralCorotationalFEMForceField','tetrahedra'),
                            'TetrahedronHyperelasticityFEMForceField':('HyperReducedTetrahedronHyperelasticityFEMForceField','tetrahedra'),
                            'HexahedronFEMForceField':('HyperReducedHexahedronFEMForceField','hexahedra'),
                            'TetrahedronFEMForceField':('HyperReducedTetrahedronFEMForceField','tetrahedra'),
                            'TriangleFEMForceField':('HyperReducedTriangleFEMForceField','triangles'),
                            'RestShapeSpringsForceField':('HyperReducedRestShapeSpringsForceField','points')

                        }

myModel = OrderedDict() # Ordered dic containing has key Sofa.Node.name & has var a tuple of (Sofa_componant_type , param_solver)

myMORModel = [] # list of tuple (solver_type , param_solver)

pathToUpdate = {}

forcefield = []

tmp = 0

[docs] def modifyPath(currentPath,type,initialParam,newParam): ''' **Correct wrong link induce by the change later done in the scene** This step isn't always needed for execution because all the DataLink are made BEFORE we change the scene with :py:func:`.modifyGraphScene` while the links are all correct (normally). But this way when we will "save" the scene with all the data value the links will be correct. Also for the links to DATA (@myCoponent.myData) or DataLink poorly implemented if the link is false during initialization this link (string representing the path) will be lost and won't be tried again during bwdInit. To correct that, we need to update after our scene modification, the changed links. We do that with :py:obj:`.pathToUpdate` ''' return_bool = False initialParam_copy = initialParam.copy() # print(currentPath,type)#,initialParam,newParam) for key , value in initialParam_copy.items(): if isinstance(value, str): if '@' in value: # print(value) path , param = newParam # print(path) # print(currentPath) pathToObj = '' if path+'/' in currentPath+'/': for i,nodeName in enumerate(currentPath.split('/')): # print(nodeName) if nodeName == path.split('/')[-1]: # print('here') tmp_value = currentPath.split('/') tmp_value.insert(i,nodeName+'_MOR') tmp_value = '/'.join(tmp_value) pathToObj = tmp_value[1:]+'/'+initialParam_copy.get("name",str(type)) if '@../' in value and currentPath == path: # print("------------> TO CHANGE 2") tmp_value = value.split('/') tmp_value.insert(1,'..') tmp_value = '/'.join(tmp_value) initialParam_copy[key] = tmp_value pathToUpdate[pathToObj] = (key,tmp_value) return_bool = True elif value.find(path+'/') != -1 or value.find(path+'.') != -1: # here if linked to node in reduction we need to add the new node we created "nodeName_MOR" # only needed when trying to write a proper new scene # print("------------> TO CHANGE 1") for i,nodeName in enumerate(value.split('/')): # print(nodeName) if nodeName == path.split('/')[-1]: # print('here') tmp_value = value.split('/') tmp_value.insert(i,nodeName+'_MOR') tmp_value = '/'.join(tmp_value) initialParam_copy[key] = tmp_value pathToUpdate[pathToObj] = (key,tmp_value) return_bool = True else: pathToObj = currentPath[1:]+'/'+initialParam_copy.get("name",str(type)) if value.find(path+'/') != -1 or value.find(path+'.') != -1: # here if linked to node in reduction we need to add the new node we created "nodeName_MOR" # only needed when trying to write a proper new scene # print("------------> TO CHANGE 1") for i,nodeName in enumerate(value.split('/')): # print(nodeName) if nodeName == path.split('/')[-1]: # print('here') tmp_value = value.split('/') tmp_value.insert(i,nodeName+'_MOR') tmp_value = '/'.join(tmp_value) initialParam_copy[key] = tmp_value pathToUpdate[pathToObj] = (key,tmp_value) return_bool = True if return_bool: return initialParam_copy
[docs] def MORreplace(node,type,newParam,initialParam): ''' **Will replace classical ForceField by HyperReduced one** +--------------+-----------+--------------------------------------------------------------------------------+ | argument | type | definition | +==============+===========+================================================================================+ | node | Sofa.node | On which node the current object will be set | +--------------+-----------+--------------------------------------------------------------------------------+ | type | undefined | Type of the Sofa.object | +--------------+-----------+--------------------------------------------------------------------------------+ | newParam | dic || Contains numerous argument to modify/replace some component of the SOFA scene.| | | || *more details see* :py:class:`.ReductionParam` | +--------------+-----------+--------------------------------------------------------------------------------+ | initialParam | dic | Contains all the initial argument of the SOFA component being instanciated | +--------------+-----------+--------------------------------------------------------------------------------+ This function work thanks to the :py:class:`stlib.scene.Wrapper` of the `STLIB <https://github.com/SofaDefrost/STLIB>`_ SOFA plugin that will call this function BEFORE creating any SOFA component enabling us to replace/modify the SOFA component before its creation This function will also, if there is *save* in the *newParam* key, save the initial component type & argument into 2 global variable :py:obj:`.myModel` & :py:obj:`.myMORModel` that will be used later by :py:func:`.writeGraphScene` to create a reusable component. We *save* our scene here with all the complications it will produce, wrong links (corrected by :py:func:`modifyPath`), need to differentiate components from *myModel* that will be moved in *myMORModel*, ect... Because this way the component parameters are not polluted by all unnecessary *dataFields* that are initialized during creation. ''' global tmp from SofaRuntime import getCategories currentPath = node.getPathName() # print('NODE : '+node.name.value) # print('TYPE : '+str(type)) # print('PARAM :'+str(newParam[0][0]) ) save = False if 'save' in newParam[1]: save = True path , param = newParam lastNode = path.split('/')[-1] if path == currentPath or lastNode+'/' in currentPath : # print('\n') # Change the initial Forcefield by the HyperReduced one with the new argument if "ForceField" in getCategories(type): # print('NODE : ' + node.name.value) # print('TYPE : ' + str(type)) # print(getCategories(type)) if type in forceFieldImplemented : type , valueType = forceFieldImplemented[type] name = 'reducedFF_'+ node.name.value + '_' + str(tmp) tmp += 1 initialParam['name'] = name initialParam['nbModes'] = param['nbrOfModes'] for key in param['paramForcefield']: initialParam[key] = param['paramForcefield'][key] # We've already put the path to the "data" folder we now have to add the right file if param['paramForcefield'].get('performECSW') == True: initialParam['RIDPath'] += name + '_RID.txt' initialParam['weightsPath'] += name + '_weight.txt' saveParam = modifyPath(currentPath,type,initialParam,newParam) if save: if currentPath not in myModel: myModel[currentPath] = [] if saveParam: myModel[currentPath].append((str(type),saveParam)) else: myModel[currentPath].append((str(type),initialParam)) forcefield.append(currentPath+'/'+initialParam.get("name",str(type))) # print(type, initialParam) return type , initialParam else: print("[WARNING] No HyperReducedForceField exist for "+type) saveParam = modifyPath(currentPath,type,initialParam,newParam) if save: if currentPath == path : if type.find('Solver') != -1 or type == 'EulerImplicitSolver' or type == 'GenericConstraintCorrection': myMORModel.append((str(type),initialParam)) else: if currentPath not in myModel: myModel[currentPath] = [] if saveParam: myModel[currentPath].append((str(type),saveParam)) else: myModel[currentPath].append((str(type),initialParam)) else: if currentPath not in myModel: myModel[currentPath] = [] if saveParam: myModel[currentPath].append((str(type),saveParam)) else: myModel[currentPath].append((str(type),initialParam)) elif save: saveParam = modifyPath(currentPath,type,initialParam,newParam) # this way we will take the path we want "to keep" and all its parents if newParam[1]['animationPaths']: for path in newParam[1]['animationPaths']: # If the animationPaths contain the name of the obj currentObjPath = currentPath + '/' + initialParam.get("name",str(type)) if currentPath.find(path) != -1 or currentObjPath.find(path) != -1: if currentPath not in myModel: myModel[currentPath] = [] if saveParam: myModel[currentPath].append((str(type),saveParam)) else: myModel[currentPath].append((str(type),initialParam)) modifyPath(currentPath,type,initialParam,newParam)