Skip to content

Commit

Permalink
RM listActiveNode & reducedMass: (#112)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
VannesteFelix authored Apr 19, 2024
1 parent 8c0536c commit b9e35e0
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 149 deletions.
5 changes: 2 additions & 3 deletions python/mor/gui/ui_mor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
main() # run the main function
35 changes: 4 additions & 31 deletions python/mor/reduction/container/reductionParam.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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 = {
Expand All @@ -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

Expand All @@ -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')
48 changes: 7 additions & 41 deletions python/mor/reduction/reduceModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -475,51 +468,24 @@ 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\
+"\nPlease give one at this location or indicate the correct location or re-generate one with phase 3")

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)]
Expand Down
5 changes: 0 additions & 5 deletions python/mor/reduction/script/ConvertRIDinActiveNodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
9 changes: 1 addition & 8 deletions python/mor/reduction/template/phase2_prepareECSW.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
nbrOfModes = $NBROFMODES
periodSaveGIE = $PERIODSAVEGIE
paramWrapper = $PARAMWRAPPER
phaseToSave = $PHASETOSAVE

path, param = paramWrapper
param['nbrOfModes'] = $NBROFMODES
Expand All @@ -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

Expand All @@ -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)
setattr(obj,data,newValue)
73 changes: 16 additions & 57 deletions python/mor/utility/sceneCreation.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
'HyperReducedRestShapeSpringsForceField':'points'
}

import Sofa
import numpy as np

tmp = 0


Expand Down Expand Up @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -435,4 +394,4 @@ def createDebug(rootNode,pathToNode,stateFile="stateFile.state"):
node.removeChild(child)

node.addObject('ReadState', filename=stateFile)
rootNode.addChild(node)
rootNode.addChild(node)
38 changes: 37 additions & 1 deletion python/mor/utility/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -105,4 +109,36 @@ def customLauncher(filesandtemplates,param,resultDir):
t = Template(content, searchList=param)
theFile.write(str(t))
theFile.close()
i+=1
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")
Loading

0 comments on commit b9e35e0

Please sign in to comment.