コード例 #1
0
    def __init__(self,
                 originalScene,
                 nodeToReduce,
                 listObjToAnimate,
                 tolModes,
                 tolGIE,
                 outputDir,
                 packageName='myReducedModel',
                 addToLib=False,
                 verbose=False,
                 addRigidBodyModes=False,
                 nbrCPU=4,
                 phaseToSave=None,
                 saveVelocitySnapshots=None):

        self.originalScene = os.path.normpath(originalScene)
        self.nodeToReduce = nodeToReduce

        ### Obj Containing all the argument & function about how the shaking will be done and with which actuators
        self.reductionAnimations = ReductionAnimations(listObjToAnimate)

        ### Obj Containing all the argument & function about how to create the end package and where
        outputDir = os.path.normpath(outputDir)
        self.packageBuilder = PackageBuilder(outputDir, packageName, addToLib)

        ### Obj Containing all the argument & function about the actual reduction
        self.reductionParam = ReductionParam(tolModes, tolGIE,
                                             addRigidBodyModes,
                                             self.packageBuilder.dataDir,
                                             saveVelocitySnapshots)

        ### With the previous parameters (listObjToAnimate/nbPossibility) we can set our training set number
        self.reductionParam.setNbTrainingSet(
            listObjToAnimate[0].params['rangeOfAction'],
            listObjToAnimate[0].params['incr'])

        self.reductionParam.addParamWrapper(self.nodeToReduce)

        self.reductionParam.setFilesName()

        self.phaseToSave = phaseToSave
        self.phaseToSaveIndex = 0
        self.nbrCPU = nbrCPU
        self.verbose = verbose

        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)
コード例 #2
0
class ReduceModel():
    """
    **Main class that will perform the reduction**

    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | argument          | type                            | definition                                                                                |
    +===================+=================================+===========================================================================================+
    | originalScene     | str                             | absolute path to original scene                                                           |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | nodeToReduce      | str                             | Paths to models to reduce                                                                 |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | listObjToAnimate  | list(:py:class:`.ObjToAnimate`) | list conaining all the ObjToAnimate that will be use to shake our model                   |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | tolModes          | float                           | tolerance applied to choose the modes                                                     |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | tolGIE            | float                           | tolerance applied to calculated GIE                                                       |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | outputDir         | str                             | absolute path to output directiry in which all results will be stored                     |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | packageName       | str                             | Which name will have the final componant ( & package if the option addToLib is activated) |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | addToLib          | Bool                            | If ``True`` will add in the python library of this plugin the finalized reduced component |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | verbose           | Bool                            | display more or less verbose                                                              |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | addRigidBodyModes | list(int)                       | List of 3 of 0/1 that will allow translation along [x,y,z] axis of our reduced model      |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | nbrCPU            | int                             | Number of CPU we will use to generate/calculate the reduced model                         |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+
    | phaseToSave       | list(int)                       | List of 0/1 indicating during which phase to save the elements/X0                         |
    |                   |                                 | ``by default will save during first phase``                                               |
    +-------------------+---------------------------------+-------------------------------------------------------------------------------------------+

    """
    def __init__(self,
                 originalScene,
                 nodeToReduce,
                 listObjToAnimate,
                 tolModes,
                 tolGIE,
                 outputDir,
                 packageName='myReducedModel',
                 addToLib=False,
                 verbose=False,
                 addRigidBodyModes=False,
                 nbrCPU=4,
                 phaseToSave=None,
                 saveVelocitySnapshots=None):

        self.originalScene = os.path.normpath(originalScene)
        self.nodeToReduce = nodeToReduce

        ### Obj Containing all the argument & function about how the shaking will be done and with which actuators
        self.reductionAnimations = ReductionAnimations(listObjToAnimate)

        ### Obj Containing all the argument & function about how to create the end package and where
        outputDir = os.path.normpath(outputDir)
        self.packageBuilder = PackageBuilder(outputDir, packageName, addToLib)

        ### Obj Containing all the argument & function about the actual reduction
        self.reductionParam = ReductionParam(tolModes, tolGIE,
                                             addRigidBodyModes,
                                             self.packageBuilder.dataDir,
                                             saveVelocitySnapshots)

        ### With the previous parameters (listObjToAnimate/nbPossibility) we can set our training set number
        self.reductionParam.setNbTrainingSet(
            listObjToAnimate[0].params['rangeOfAction'],
            listObjToAnimate[0].params['incr'])

        self.reductionParam.addParamWrapper(self.nodeToReduce)

        self.reductionParam.setFilesName()

        self.phaseToSave = phaseToSave
        self.phaseToSaveIndex = 0
        self.nbrCPU = nbrCPU
        self.verbose = verbose

        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)

    def setListSofaScene(self, phasesToExecute=None):
        """
        **Will generate a list containing dictionnaries, 
        where each dictionnary is a set of argument for the execution of one SOFA scene.**

        +-----------------+-----------+----------------------------------------------------------+
        | argument        | type      | definition                                               |
        +=================+===========+==========================================================+
        | phasesToExecute | list(int) | Allow to choose which phase to execute for the reduction |
        |                 |           |                                                          |
        |                 |           | ``by default will select all the phase``                 |
        +-----------------+-----------+----------------------------------------------------------+

        The number of dictionnaries generated depend upon either the number of action possibility 
        (self.reductionAnimations.nbPossibility) or you can give with *phasesToExecute* specifically 
        which possibility you want to execute.

        **example :**

            You have 2 :py:class:`.ObjToAnimate` (thing that will be animated during the execution). 
            From self.reductionAnimations you will have 2^2 possibilities:
            
            [0,0] | [0,1] | [1,0] | [1,1] --> where 0 mean no animation & 1 animation

            * if you give no argument, phasesToExecute = [0,1,2,3]
                ``it will execute possibilty 0,1,2 & 3``
            * if you give phasesToExecute=[1,3]
                ``it will execute possibility 1 & 3``

        """
        self.listSofaScene = []

        if not phasesToExecute:
            phasesToExecute = list(
                range(self.reductionAnimations.nbPossibility))

        if not self.phaseToSave:
            self.phaseToSave = [0] * len(
                self.reductionAnimations.phaseNumClass[0])

        for i in phasesToExecute:
            if i >= self.reductionAnimations.nbPossibility or i < 0:
                raise ValueError(
                    "phasesToExecute incorrect, select an non-existent phase : "
                    + phasesToExecute)
            if self.phaseToSave == self.reductionAnimations.phaseNumClass[i]:
                self.phaseToSaveIndex = self.reductionAnimations.phaseNumClass.index(
                    self.phaseToSave)
                # print("INDEX -------------------> "+str(self.phaseToSaveIndex))

            self.listSofaScene.append({
                "ORIGINALSCENE":
                self.originalScene,
                "LISTOBJTOANIMATE":
                self.reductionAnimations.listObjToAnimate,
                "PHASE":
                self.reductionAnimations.phaseNumClass[i],
                "PERIODSAVEGIE":
                self.reductionParam.periodSaveGIE,
                "PARAMWRAPPER":
                self.reductionParam.paramWrapper,
                "nbIterations":
                self.reductionAnimations.nbIterations,
                "PHASETOSAVE":
                self.phaseToSave
            })

    def performReduction(self, phasesToExecute=None, nbrOfModes=None):
        """
        **Perform all the steps of the reduction in one function**

        +-----------------+-----------+----------------------------------------------------------+
        | argument        | type      | definition                                               |
        +=================+===========+==========================================================+
        | phasesToExecute | list(int) || Allow to choose which phase to execute for the reduction|
        |                 |           || *more details see* :py:func:`setListSofaScene`          |
        +-----------------+-----------+----------------------------------------------------------+
        | nbrOfModes      | int       || Number of modes you want to keep                        |
        |                 |           || ``by default will keep them all``                       |
        +-----------------+-----------+----------------------------------------------------------+
        
        If you are sure of all the parameters this way is recommended to gain time

        """
        ### This initila time we allow us to give at the end the total time execution
        init_time = time.time()

        self.phase1(phasesToExecute)
        self.phase2()
        self.phase3(phasesToExecute, nbrOfModes)
        self.phase4(nbrOfModes)

        tps = int(round(time.time() - init_time))
        print("TOTAL TIME --- %s ---" % (datetime.timedelta(seconds=tps)))

    def phase1(self, phasesToExecute=None):
        """
        **The step will launch in parallel multiple Sofa scene (nbrCPU by nbrCPU number of scene) until
        it has run all the scene in the sequence.** 

        +-----------------+-----------+----------------------------------------------------------+
        | argument        | type      | definition                                               |
        +=================+===========+==========================================================+
        | phasesToExecute | list(int) || Allow to choose which phase to execute for the reduction|
        |                 |           || *more details see* :py:func:`setListSofaScene`          |
        +-----------------+-----------+----------------------------------------------------------+
        
        To run the SOFA scene in parallele we use the ``sofa launcher`` utility

        What does it do to each scene:

            - Add animation to each :py:class:`.ObjToAnimate` we want for our model in the predifined sequence
            - Add a componant to save the shaking resulting states (WriteState)
            - Take all the resulting states files and combines them in one file put in the ``debug`` dir with a debug scene

        """
        start_time = time.time()

        if not phasesToExecute:
            phasesToExecute = list(
                range(self.reductionAnimations.nbPossibility))

        self.setListSofaScene(phasesToExecute)

        filenames = ["phase1_snapshots.py", "debug_scene.py"]
        filesandtemplates = []
        for filename in filenames:
            filesandtemplates.append(
                (open(pathToTemplate + filename).read(), filename))

        results = startSofa(self.listSofaScene,
                            filesandtemplates,
                            launcher=ParallelLauncher(self.nbrCPU))

        if self.verbose:
            for res in results:
                print("Results: ")
                print("    directory: " + res["directory"])
                print("        scene: " + res["scene"])
                print("     duration: " + str(res["duration"]) + " sec")

        self.packageBuilder.copyAndCleanState(
            results, self.reductionParam.periodSaveGIE,
            self.reductionParam.stateFileName,
            self.reductionParam.velocityFileName)
        u.copy(
            results[self.phaseToSaveIndex]["directory"] + slash +
            "debug_scene.py", self.packageBuilder.debugDir)

        print("PHASE 1 --- %s seconds ---" % (time.time() - start_time))

    def phase2(self):
        """
        **With the previous result obtain in during :py:func:`phase1` we compute the modes**

        See :py:mod:`.ReadStateFilesAndComputeModes` for the way the modes are determined.

        It will set ``nbrOfModes`` to its maximum, but it can be changed has argument to the next step : :py:func:`phase3`

        """
        # MOR IMPORT
        from mor.reduction.script import readStateFilesAndComputeModes

        start_time = time.time()

        u.checkExistance(self.packageBuilder.dataDir)

        self.reductionParam.nbrOfModes = readStateFilesAndComputeModes(
            stateFilePath=self.packageBuilder.debugDir +
            self.reductionParam.stateFileName,
            modesFileName=self.packageBuilder.dataDir +
            self.reductionParam.modesFileName,
            tol=self.reductionParam.tolModes,
            addRigidBodyModes=self.reductionParam.addRigidBodyModes,
            verbose=self.verbose)

        if self.reductionParam.nbrOfModes == -1:
            raise ValueError(
                "problem of execution of readStateFilesAndComputeModes")

        print("PHASE 2 --- %s seconds ---" % (time.time() - start_time))

    def phase3(self, phasesToExecute=None, nbrOfModes=None):
        """
        **This step will launch in parallel multiple Sofa scene (nbrCPU by nbrCPU number of scene) until
        it has run all the scene in the sequence.**

        +-----------------+-----------+----------------------------------------------------------+
        | argument        | type      | definition                                               |
        +=================+===========+==========================================================+
        | phasesToExecute | list(int) || Allow to choose which phase to execute for the reduction|
        |                 |           || *more details see* :py:func:`setListSofaScene`          |
        +-----------------+-----------+----------------------------------------------------------+
        | nbrOfModes      | int       || Number of modes you want to keep                        |
        |                 |           || ``by default will keep them all``                       |
        +-----------------+-----------+----------------------------------------------------------+

        To run the SOFA scene in parallele we use the ``sofa launcher`` utility

        What does it do to each scene:

            - Take the previous one and add the model order reduction component:
               - HyperReducedFEMForceField
               - MappedMatrixForceFieldAndMas
               - ModelOrderReductionMapping
            - Produce an Hyper Reduced description of the model
            - Produce files listing the different element to keep
            - Take all the resulting states files and combines them in one file put in the ``debug`` dir with a debug scene


        """
        start_time = time.time()

        if not phasesToExecute:
            phasesToExecute = list(
                range(self.reductionAnimations.nbPossibility))
        if not nbrOfModes:
            nbrOfModes = self.reductionParam.nbrOfModes

        if not os.path.isfile(self.packageBuilder.dataDir +
                              self.reductionParam.modesFileName):
            raise IOError("There is no mode file at "+self.packageBuilder.dataDir+self.reductionParam.modesFileName\
                +"\nPlease give one at this location or indicate the correct location or re-generate one with phase 1 & 2")

        nbrOfModesPossible = self.packageBuilder.checkNodeNbr(
            self.reductionParam.modesFileName)

        if not nbrOfModes:
            nbrOfModes = self.reductionParam.nbrOfModes
        if nbrOfModes == -1:
            nbrOfModes = self.reductionParam.nbrOfModes = nbrOfModesPossible

        if (nbrOfModes <= 0) or (nbrOfModes > nbrOfModesPossible):
            raise ValueError("nbrOfModes incorrect\n"\
                +"  nbrOfModes given :"+str(nbrOfModes)+" | nbrOfModes max possible : "+str(nbrOfModesPossible))

        self.setListSofaScene(phasesToExecute)

        for i in range(len(phasesToExecute)):
            self.listSofaScene[i]['NBROFMODES'] = nbrOfModes

        filenames = [
            "phase2_prepareECSW.py", "phase1_snapshots.py", "debug_scene.py"
        ]
        filesandtemplates = []
        for filename in filenames:
            filesandtemplates.append(
                (open(pathToTemplate + filename).read(), filename))
        results = startSofa(self.listSofaScene,
                            filesandtemplates,
                            launcher=ParallelLauncher(self.nbrCPU))

        if self.verbose:
            for res in results:
                print("Results: ")
                print("    directory: " + res["directory"])
                print("        scene: " + res["scene"])
                print("     duration: " + str(res["duration"]) + " sec")

        files = glob.glob(results[self.phaseToSaveIndex]["directory"] + slash +
                          "*_elmts.txt")
        if files:
            for i, file in enumerate(files):
                file = os.path.normpath(file)
                files[i] = file.split(slash)[-1]
            # print("FILES ----------->",files)
            self.reductionParam.savedElementsFilesNames = files

        for fileName in self.reductionParam.savedElementsFilesNames:
            u.copyFileIntoAnother(
                results[self.phaseToSaveIndex]["directory"] + slash + fileName,
                self.packageBuilder.debugDir + fileName)
        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:
            for i, file in enumerate(files):
                file = os.path.normpath(file)
                files[i] = file.split(slash)[-1]
            # print("FILES ----------->",files)
            self.reductionParam.gieFilesNames = files
        else:
            raise IOError("Missing GIE Files")

        self.packageBuilder.copyAndCleanState(
            results,
            self.reductionParam.periodSaveGIE,
            'step2_' + self.reductionParam.stateFileName,
            gie=self.reductionParam.gieFilesNames)

        print("PHASE 3 --- %s seconds ---" % (time.time() - start_time))

    def phase4(self, nbrOfModes=None):
        """
        **The final step will gather all the results in 1 folder and build a reusable scene from it**

        +-----------------+-----------+----------------------------------------------------------+
        | argument        | type      | definition                                               |
        +=================+===========+==========================================================+
        | nbrOfModes      | int       || Number of modes you want to keep                        |
        |                 |           || ``by default will keep them all``                       |
        +-----------------+-----------+----------------------------------------------------------+

        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

        """
        # MOR IMPORT
        from .script import readGieFileAndComputeRIDandWeights, convertRIDinActiveNodes

        start_time = time.time()

        if not os.path.isfile(self.packageBuilder.dataDir +
                              self.reductionParam.modesFileName):
            raise IOError("There is no mode file at "+self.packageBuilder.dataDir+self.reductionParam.modesFileName\
                +"\nPlease give one at this location or indicate the correct location or re-generate one with phase 1 & 2")

        nbrOfModesPossible = self.packageBuilder.checkNodeNbr(
            self.reductionParam.modesFileName)

        if not nbrOfModes:
            nbrOfModes = self.reductionParam.nbrOfModes
        if nbrOfModes == -1:
            nbrOfModes = self.reductionParam.nbrOfModes = nbrOfModesPossible

        if (nbrOfModes <= 0) or (nbrOfModes > nbrOfModesPossible):
            raise ValueError("nbrOfModes incorrect\n"\
                +"  nbrOfModes given :"+str(nbrOfModes)+" | nbrOfModes max possible : "+str(nbrOfModesPossible))

        # print(files)
        files = glob.glob(self.packageBuilder.debugDir + "*_elmts.txt")
        if files:
            for i, file in enumerate(files):
                file = os.path.normpath(file)
                files[i] = file.split(slash)[-1]
            # print("FILES ----------->",files)
            self.reductionParam.savedElementsFilesNames = files

        files = glob.glob(self.packageBuilder.debugDir + "*_Gie.txt")
        if files:
            for i, file in enumerate(files):
                file = os.path.normpath(file)
                files[i] = file.split(slash)[-1]
            # print("FILES ----------->",files)
            self.reductionParam.gieFilesNames = files

        for i, gie in enumerate(self.reductionParam.gieFilesNames):
            tmp = gie.replace('_Gie.txt', '')
            for j, elmts in enumerate(
                    self.reductionParam.savedElementsFilesNames):
                if tmp in elmts:
                    tmp = self.reductionParam.savedElementsFilesNames[j]
                    self.reductionParam.savedElementsFilesNames[
                        j] = self.reductionParam.savedElementsFilesNames[i]
                    self.reductionParam.savedElementsFilesNames[i] = tmp

        # print(self.reductionParam.savedElementsFilesNames)
        # print(self.reductionParam.gieFilesNames)
        tmp = glob.glob(self.packageBuilder.dataDir + "*_reduced.txt")[0]
        tmp = os.path.normpath(tmp)
        self.reductionParam.massName = tmp.split(slash)[-1]
        # print("massName -----------------------> ",self.reductionParam.massName)

        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)
            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(
                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)
                             ]

        self.reductionParam.addParamWrapper(self.nodeToReduce,
                                            prepareECSW=False)

        finalScene = {}
        finalScene["ORIGINALSCENE"] = self.originalScene
        finalScene["PARAMWRAPPER"] = self.reductionParam.paramWrapper
        finalScene['NBROFMODES'] = nbrOfModes
        finalScene["nbIterations"] = 1
        finalScene["ANIMATIONPATHS"] = self.reductionAnimations.listOfLocation
        finalScene["PACKAGENAME"] = self.packageBuilder.packageName

        results = startSofa([finalScene],
                            filesandtemplates,
                            launcher=ParallelLauncher(1))
        self.packageBuilder.finalizePackage(results[0])