From b9e35e0f4063968405b6f567399256d5bf723de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vanneste=20F=C3=A9lix?= Date: Fri, 19 Apr 2024 10:05:50 +0200 Subject: [PATCH] RM listActiveNode & reducedMass: (#112) dquote> - old mechanism that is not used anymore, using new SOFA matrix assembly & solver replace this dquote> - add utility fct to execute SOFA scene in batch and get if stdout has errors dquote> - In sceneCreation fix if node we reduce has multiple parents --- python/mor/gui/ui_mor.py | 5 +- .../mor/reduction/container/reductionParam.py | 35 +-------- python/mor/reduction/reduceModel.py | 48 ++---------- .../script/ConvertRIDinActiveNodes.py | 5 -- .../reduction/template/phase2_prepareECSW.py | 9 +-- python/mor/utility/sceneCreation.py | 73 ++++--------------- python/mor/utility/utility.py | 38 +++++++++- python/mor/wrapper/replaceAndSave.py | 5 +- 8 files changed, 69 insertions(+), 149 deletions(-) diff --git a/python/mor/gui/ui_mor.py b/python/mor/gui/ui_mor.py index f2c912f0..20be1454 100644 --- a/python/mor/gui/ui_mor.py +++ b/python/mor/gui/ui_mor.py @@ -314,8 +314,7 @@ def checkPhases(self): ["/debug/debug_scene.py","/debug/stateFile.state"], ["/data/modes.txt"], ["/debug/reducedFF_*","/debug/*_elmts.txt"], #["/debug/step2_stateFile.state", - ["/data/*_RID.txt","/data/*_weight.txt","/data/*listActiveNodes.txt","/reduced_*"] - # "/data/*_reduced.txt" search for reduced mass, remove it for the moment + ["/data/*_RID.txt","/data/*_weight.txt","/reduced_*"] ] def check(file,item): @@ -850,4 +849,4 @@ def main(): app.exec_() # and execute the app if __name__ == '__main__': # if we're running file directly and not importing it - main() # run the main function \ No newline at end of file + main() # run the main function diff --git a/python/mor/reduction/container/reductionParam.py b/python/mor/reduction/container/reductionParam.py index 86714e74..28045d7a 100644 --- a/python/mor/reduction/container/reductionParam.py +++ b/python/mor/reduction/container/reductionParam.py @@ -29,9 +29,6 @@ def __init__(self,tolModes,tolGIE,addRigidBodyModes,dataDir,saveVelocitySnapshot self.RIDFilesNames = [] self.weightsFilesNames = [] self.savedElementsFilesNames = [] - self.listActiveNodesFilesNames = [] - self.massName = '' - self.nbrOfModes = -1 self.periodSaveGIE = 6 #10 @@ -50,7 +47,7 @@ def setNbTrainingSet(self,rangeOfAction,incr): self.nbTrainingSet = int(rangeOfAction/incr) - def addParamWrapper(self ,nodeToReduce ,prepareECSW = True, paramForcefield = None ,paramMappedMatrixMapping = None ,paramMORMapping = None): + def addParamWrapper(self ,nodeToReduce ,prepareECSW = True, paramForcefield = None ,paramMORMapping = None): ''' ''' nodeToReduce = "".join(nodeToReduce) @@ -67,15 +64,6 @@ def addParamWrapper(self ,nodeToReduce ,prepareECSW = True, paramForcefield = No 'paramMORMapping' : { 'input': '@../MechanicalObject', 'modesPath': self.dataDir+self.modesFileName}, - - 'paramMappedMatrixMapping' : { - 'nodeToParse': nodeToParse, - 'template': 'Vec1d,Vec1d', - 'object1': '@./MechanicalObject', - 'object2': '@./MechanicalObject', - 'timeInvariantMapping1': True, - 'timeInvariantMapping2': True, - 'performECSW': False} } defaultParamPerform = { @@ -88,34 +76,20 @@ def addParamWrapper(self ,nodeToReduce ,prepareECSW = True, paramForcefield = No 'paramMORMapping' : { 'input': '@../MechanicalObject', 'modesPath': self.dataFolder+self.modesFileName}, - - 'paramMappedMatrixMapping' : { - 'nodeToParse': nodeToParse, - 'template': 'Vec1d,Vec1d', - 'object1': '@./MechanicalObject', - 'object2': '@./MechanicalObject', - 'timeInvariantMapping1': True, - 'timeInvariantMapping2': True, - 'listActiveNodesPath' : self.dataFolder+'listActiveNodes.txt', - 'performECSW': True, - 'usePrecomputedMass': True, - 'precomputedMassPath': self.dataFolder+self.massName} } - if paramForcefield and paramMappedMatrixMapping and paramMORMapping : + if paramForcefield and paramMORMapping : pass else: if prepareECSW: self.paramWrapper = ( (nodeToReduce , {'paramForcefield': defaultParamPrepare['paramForcefield'].copy(), - 'paramMORMapping': defaultParamPrepare['paramMORMapping'].copy(), - 'paramMappedMatrixMapping': defaultParamPrepare['paramMappedMatrixMapping'].copy()} ) ) + 'paramMORMapping': defaultParamPrepare['paramMORMapping'].copy()} ) ) else : self.paramWrapper = ( (nodeToReduce , {'paramForcefield': defaultParamPerform['paramForcefield'].copy(), - 'paramMORMapping': defaultParamPerform['paramMORMapping'].copy(), - 'paramMappedMatrixMapping': defaultParamPerform['paramMappedMatrixMapping'].copy()} ) ) + 'paramMORMapping': defaultParamPerform['paramMORMapping'].copy()} ) ) return self.paramWrapper @@ -129,4 +103,3 @@ def setFilesName(self): self.RIDFilesNames.append('RID_'+nodeName+'.txt') self.weightsFilesNames.append('weight_'+nodeName+'.txt') self.savedElementsFilesNames.append('elmts_'+nodeName+'.txt') - self.listActiveNodesFilesNames.append('listActiveNodes_'+nodeName+'.txt') \ No newline at end of file diff --git a/python/mor/reduction/reduceModel.py b/python/mor/reduction/reduceModel.py index f50eb4a0..d3f36cde 100644 --- a/python/mor/reduction/reduceModel.py +++ b/python/mor/reduction/reduceModel.py @@ -131,12 +131,13 @@ def __init__(self, self.activesNodesLists = [] self.listSofaScene = [] - strInfo = 'periodSaveGIE : '+str(self.reductionParam.periodSaveGIE)+' | ' - strInfo += 'nbTrainingSet : '+str(self.reductionParam.nbTrainingSet)+' | ' - strInfo += 'nbIterations : '+str(self.reductionAnimations.nbIterations)+'\n' - # strInfo += "List of phase :"+str(self.reductionAnimations.phaseNumClass)+'\n' - strInfo += "##################################################" - print(strInfo) + if (verbose): + strInfo = 'periodSaveGIE : '+str(self.reductionParam.periodSaveGIE)+' | ' + strInfo += 'nbTrainingSet : '+str(self.reductionParam.nbTrainingSet)+' | ' + strInfo += 'nbIterations : '+str(self.reductionAnimations.nbIterations)+'\n' + # strInfo += "List of phase :"+str(self.reductionAnimations.phaseNumClass)+'\n' + strInfo += "##################################################" + print(strInfo) def setListSofaScene(self,phasesToExecute=None,phase=None): """ @@ -388,13 +389,6 @@ def phase3(self,phasesToExecute=None,nbrOfModes=None): for fileName in self.reductionParam.savedElementsFilesNames : u.copyFileIntoAnother(results[self.phaseToSaveIndex]["directory"]+slash+fileName,self.packageBuilder.debugDir+fileName) - - ### commented out waiting for change in c++ code - # optimization done in MMM but now removed - # self.reductionParam.massName = glob.glob(results[self.phaseToSaveIndex]["directory"]+slash+"*_reduced.txt")[0] - # # print("massName -----------------------> ",self.reductionParam.massName) - # u.copy(self.reductionParam.massName,self.reductionParam.dataDir) - ##### files = glob.glob(results[self.phaseToSaveIndex]["directory"]+slash+"*_Gie.txt") if files: @@ -427,7 +421,6 @@ def phase4(self,nbrOfModes=None): Final step : - compute the RID and Weigts with :py:mod:`.ReadGieFileAndComputeRIDandWeights` - - compute the Active Nodes with :py:mod:`.ConvertRIDinActiveNodes` - finalize the package - add it to the plugin library if option activated @@ -475,16 +468,8 @@ def phase4(self,nbrOfModes=None): self.reductionParam.savedElementsFilesNames[j] = self.reductionParam.savedElementsFilesNames[i] self.reductionParam.savedElementsFilesNames[i] = tmp - ### commented out waiting for change in c++ code - # optimization done in MMM but now removed - # tmp = glob.glob(self.packageBuilder.dataDir+"*_reduced.txt")[0] - # tmp = os.path.normpath(tmp) - # self.reductionParam.massName = tmp.split(slash)[-1] - #### - self.reductionParam.RIDFilesNames = [] self.reductionParam.weightsFilesNames = [] - self.reductionParam.listActiveNodesFilesNames = [] for fileName in self.reductionParam.gieFilesNames : if not os.path.isfile(self.packageBuilder.debugDir+fileName): raise IOError("There is no GIE file at "+self.packageBuilder.debugDir+fileName\ @@ -492,34 +477,15 @@ def phase4(self,nbrOfModes=None): self.reductionParam.RIDFilesNames.append(fileName.replace('_Gie','_RID')) self.reductionParam.weightsFilesNames.append(fileName.replace('_Gie','_weight')) - self.reductionParam.listActiveNodesFilesNames.append(fileName.replace('_Gie','_listActiveNodes')) - self.listActiveNodesFilesNames = [] for i , fileName in enumerate(self.reductionParam.gieFilesNames) : - # index = self.reductionParam.gieFilesNames.index(fileName) script.readGieFileAndComputeRIDandWeights( self.packageBuilder.debugDir+fileName, self.packageBuilder.dataDir+self.reductionParam.RIDFilesNames[i], self.packageBuilder.dataDir+self.reductionParam.weightsFilesNames[i], self.reductionParam.tolGIE, verbose= self.verbose) - # print(index) - # print(len(self.reductionParam.savedElementsFilesNames)) - # if index-1 < len(self.reductionParam.savedElementsFilesNames): - self.activesNodesLists.append( script.convertRIDinActiveNodes(self.packageBuilder.dataDir+self.reductionParam.RIDFilesNames[i], - self.packageBuilder.debugDir+self.reductionParam.savedElementsFilesNames[i], - self.packageBuilder.dataDir+self.reductionParam.listActiveNodesFilesNames[i], - verbose= self.verbose)) - - finalListActiveNodes = [] - for activeNodes in self.activesNodesLists: - finalListActiveNodes = list(set().union(finalListActiveNodes,activeNodes)) - finalListActiveNodes = sorted(finalListActiveNodes) - with open(self.packageBuilder.dataDir+'listActiveNodes.txt', "w") as file: - for item in finalListActiveNodes: - file.write("%i\n" % item) - file.close() filename = "phase3_performECSW.py" filesandtemplates = [(open(pathToTemplate+filename).read(), filename)] diff --git a/python/mor/reduction/script/ConvertRIDinActiveNodes.py b/python/mor/reduction/script/ConvertRIDinActiveNodes.py index 0fb5ccbb..e2de526c 100644 --- a/python/mor/reduction/script/ConvertRIDinActiveNodes.py +++ b/python/mor/reduction/script/ConvertRIDinActiveNodes.py @@ -47,11 +47,6 @@ def convertRIDinActiveNodes(RIDFileName,connectivityFileName,listActiveNodesFile dimension = len(lineSplit) listActiveNodes = [] for i in RIDlist: - #if verbose : - # print "#######################" - # print "elem number: ", i - # for coordIndex in range(dimension): - # print connecList[i][coordIndex] lenStart = len(listActiveNodes) for coordIndex in range(dimension): if connecList[i][coordIndex] not in listActiveNodes: diff --git a/python/mor/reduction/template/phase2_prepareECSW.py b/python/mor/reduction/template/phase2_prepareECSW.py index c2c57b50..d3811298 100644 --- a/python/mor/reduction/template/phase2_prepareECSW.py +++ b/python/mor/reduction/template/phase2_prepareECSW.py @@ -27,7 +27,6 @@ nbrOfModes = $NBROFMODES periodSaveGIE = $PERIODSAVEGIE paramWrapper = $PARAMWRAPPER -phaseToSave = $PHASETOSAVE path, param = paramWrapper param['nbrOfModes'] = $NBROFMODES @@ -48,12 +47,6 @@ def createScene(rootNode): # Add MOR plugin if not found #u.addPlugin(rootNode,"ModelOrderReduction") - # Save connectivity list that will allow us after work only on the necessary elements - - if phase == phaseToSave: - u.saveElements(rootNode,rootNode.dt,replaceAndSave.forcefield) - param['paramMappedMatrixMapping']['saveReducedMass'] = True - # Modify the scene to perform hyper-reduction according # to the informations collected by the wrapper @@ -64,4 +57,4 @@ def createScene(rootNode): for path , item in replaceAndSave.pathToUpdate.items(): data , newValue = item obj = get(rootNode,path) - setattr(obj,data,newValue) \ No newline at end of file + setattr(obj,data,newValue) diff --git a/python/mor/utility/sceneCreation.py b/python/mor/utility/sceneCreation.py index c893dbf4..5dcb2fac 100644 --- a/python/mor/utility/sceneCreation.py +++ b/python/mor/utility/sceneCreation.py @@ -37,6 +37,9 @@ 'HyperReducedRestShapeSpringsForceField':'points' } +import Sofa +import numpy as np + tmp = 0 @@ -320,17 +323,22 @@ def modifyGraphScene(node,nbrOfModes,newParam): try : currentNode = get(node,pathTmp[1:]) solver = getNodeSolver(currentNode) + print("SOLVER",solver) if currentNode.getPathName() == pathTmp: - if 'paramMappedMatrixMapping' in param: + if 'prepareECSW' in param['paramForcefield'] or 'performECSW' in param['paramForcefield'] : print('Create new child modelMOR and move node in it') - myParent = list(currentNode.parents) - modelMOR = myParent[0].addChild(currentNode.name.value+'_MOR') - myParent[0].removeChild(currentNode) + myParents = list(currentNode.parents) + modelMOR = Sofa.Core.Node(currentNode.name.value+'_MOR') + for parent in myParents: + parent.removeChild(currentNode) + parent.addChild(modelMOR) + modelMOR.addChild(currentNode) - for obj in solver: - currentNode.removeObject(obj) - modelMOR.addObject(obj) + if len(solver)>0: + for obj in solver: + currentNode.removeObject(obj) + modelMOR.addObject(obj) modelMOR.addObject('MechanicalObject', **argMecha) @@ -349,55 +357,6 @@ def modifyGraphScene(node,nbrOfModes,newParam): except : print("[ERROR] In modifyGraphScene , cannot modify scene from path : "+pathTmp[1:]) -def saveElements(node,dt,forcefield): - ''' - **Depending on the forcefield will go search for the right kind - of elements (tetrahedron/triangles...) to save** - - +------------+---------------------------------+----------------------------------------------------+ - | argument | type | definition | - +============+=================================+====================================================+ - | node | :class:`sofaPy3:Sofa.Core.Node` | from which node will search to save elements | - +------------+---------------------------------+----------------------------------------------------+ - | dt | sc | time step of our SOFA scene | - +------------+---------------------------------+----------------------------------------------------+ - | forcefield | list(str) || list of path to the forcefield working on the | - | | || elements we want to save see :py:obj:`.forcefield`| - +------------+---------------------------------+----------------------------------------------------+ - - After determining what to save we will add an animation with a *duration* of 0 that will - be executed only once when the scene is launched saving the elements. - - To do that we use :func:`stlib:splib.animation.animate` - - :return: None - ''' - - import numpy as np - def save(node,container,valueType, **param): - global tmp - elements = container.findData(valueType).value - np.savetxt('reducedFF_'+ node.name.value + '_' + str(tmp)+'_'+valueType+'_elmts.txt', elements,fmt='%i') - tmp += 1 - print('save : '+'elmts_'+node.name.value+' from '+container.name.value+' with value Type '+valueType) - - for objPath in forcefield: - nodePath = '/'.join(objPath.split('/')[:-1]) - - obj = get(node,objPath[1:]) - currentNode = get(node,nodePath[1:]) - - if obj.getClassName() == 'HyperReducedRestShapeSpringsForceField': - container = obj - else: - container = getContainer(currentNode) - - if obj.getClassName() in forceFieldImplemented and container: - valueType = forceFieldImplemented[obj.getClassName()] - - if valueType: - animate(save, {"node" : currentNode ,'container' : container, 'valueType' : valueType, 'startTime' : 0}, 0) - def createDebug(rootNode,pathToNode,stateFile="stateFile.state"): ''' **Will, from our original scene, remove all unnecessary component and add a ReadState component @@ -435,4 +394,4 @@ def createDebug(rootNode,pathToNode,stateFile="stateFile.state"): node.removeChild(child) node.addObject('ReadState', filename=stateFile) - rootNode.addChild(node) \ No newline at end of file + rootNode.addChild(node) diff --git a/python/mor/utility/utility.py b/python/mor/utility/utility.py index 77c05979..cbb4b649 100644 --- a/python/mor/utility/utility.py +++ b/python/mor/utility/utility.py @@ -27,6 +27,10 @@ import shutil import errno +from subprocess import Popen, PIPE, call +import tempfile +import time + def update_progress(progress): barLength = 50 # Modify this to change the length of the progress bar @@ -105,4 +109,36 @@ def customLauncher(filesandtemplates,param,resultDir): t = Template(content, searchList=param) theFile.write(str(t)) theFile.close() - i+=1 \ No newline at end of file + i+=1 + +def executeSofaScene(sofaScene,param=["-g", "batch", "-l", "SofaPython3", "-n", "5"],verbose=False): + + if os.path.isfile(sofaScene): + + arg = ["runSofa"]+param+[sofaScene] + + # print(arg) + # print(os.path.dirname(sofaScene)) + begin = time.time() + try: + a = Popen(arg, stdout=PIPE, stderr=PIPE,universal_newlines=True) + except: + print("Unable to find runSofa, please add the runSofa location to your PATH and restart sofa-launcher.") + sys.exit(-1) + + astdout, astderr = a.communicate() + a.stdout.close() + a.stderr.close() + end = time.time() + logfile = open(os.path.dirname(sofaScene)+"/reduction-log", "w+") + logfile.write("========= STDOUT-LOG============\n") + logfile.write(astdout) + logfile.write("========= STDERR-LOG============\n") + logfile.write(astderr) + logfile.close() + if '[ERROR]' in astderr: + return False + return True + + else: + print("ERROR the file you try to launch doesn't exist, you have to execute the phase first") diff --git a/python/mor/wrapper/replaceAndSave.py b/python/mor/wrapper/replaceAndSave.py index 4222ede8..81b02855 100644 --- a/python/mor/wrapper/replaceAndSave.py +++ b/python/mor/wrapper/replaceAndSave.py @@ -180,8 +180,8 @@ def MORreplace(node,type,newParam,initialParam): save = True path , param = newParam - # print(currentPath,path) - if path in currentPath : + 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): @@ -189,7 +189,6 @@ def MORreplace(node,type,newParam,initialParam): # print('TYPE : ' + str(type)) # print(getCategories(type)) if type in forceFieldImplemented : - # print("---------------------------------> here") type , valueType = forceFieldImplemented[type] name = 'reducedFF_'+ node.name.value + '_' + str(tmp) tmp += 1