Export results during a DOE

Pernelle Marone-Hitz
Pernelle Marone-Hitz Member, Moderator, Employee Posts: 871
100 Answers 500 Comments 250 Likes First Anniversary
✭✭✭✭
edited June 2023 in Structures

I'd like to export temperature results for each DP of a DOE analysis. How can I do that?

Best Answer

  • Pernelle Marone-Hitz
    Pernelle Marone-Hitz Member, Moderator, Employee Posts: 871
    100 Answers 500 Comments 250 Likes First Anniversary
    ✭✭✭✭
    Answer ✓

    The Python Code object can be used for this.

    First, insert a temperature result that will be used for all DPs:

    enter image description here

    Then, insert a Python Code object enter image description herethat will be triggered after post: enter image description here

    Modify the Property Provider so that this object uses an input parameter:

    def reload_props():
        this.PropertyProvider = None
        # Create the property instance
        provider = Provider()
        # Create a group named Group 1.
        group = provider.AddGroup("Parameter")
        # Create a property with control type Expression and a property with control type Double, and add it to the Group 1
        double_prop = group.AddProperty("DPValue", Control.Double)
        double_prop.CanParameterize = True
        double_prop.ParameterType = ParameterType.Input
        
        this.PropertyProvider = provider
        
    
    
    
    # region Property Provider Template
    from Ansys.ACT.Mechanical.AdditionalProperties import PropertyProviderAdapter
    from Ansys.ACT.Mechanical.AdditionalProperties import *
    
    """
    The property_templates module is located in %awp_root212%\aisol\DesignSpace\DSPages\Python\mech_templates
    """
    from mech_templates import property_templates
    
    property_templates.set_ext_api(ExtAPI)
    
    class Provider(Ansys.ACT.Interfaces.Mechanical.IPropertyProvider):
        """
        Provider template that implements IPropertyProvider to demonstrate the usage of IPropertyProvider.
        It provides helper methods and classes that manage properties that can be dynamically added to an object.
        """
        
        # region These are callbacks that as a user you may want to modify to get specific behavior
        def IsValid(self, prop):
            """
            Called when checking the validity of a property, with the property instance.
            """
    
            # for double property use the ValidRange property to check validity
            if(isinstance(prop, DoubleProperty)):
                return prop.ValidRange[0] <= prop.Value and prop.ValidRange[1] >= prop.Value
            
            return True
        
        def IsReadOnly(self, prop):
            """
            Called when checking if a property should be readonly, with the property instance.
            """
    
            return False
        
        def IsVisible(self, prop):
            """
            Called when checking if a property should be visible, with the property instance.
            """
    
            return True
        
        def SetValue(self, prop, val):
            """
            Allows you to override the setter of the Value property on the property instance. 
            Keyword Arguments:
                prop -- property of which the value is being set
                val -- the value that was set
            Returns:
                The value that the Value property should be set to
            """
            return val
    
        def GetValue(self, prop, val):
            """
            Allows you to override the getter of the Value property on the property instance. 
            Keyword Arguments:
                prop -- property of which the value is being set
                val -- current value of the Value property
            Returns:
                The value that the getter on the internal value should return
            """
            return val
        # endregion   
        
        # structures that hold property instances
        prop_list = []
        prop_map = {}
        prop_groups = set()
    
        class __AnsGroup():
            """
            Helper group class to group properties, and provides methods to add properties to groups.
            """
            provider = None
            def __init__(self,name=None, provider=None):
                self.name = name
                self.provider = provider
            
            def __AddScopingProperty(self, name):
                """
                Adds a scoping property with a given name to this group.
                
                Keyword Arguments : 
                    name -- unique name for the scoping property
                """
                scoping_prop = property_templates.ScopingProperty(name, self.name)
                
                for prop in scoping_prop.GetGroupedProps():
                    self.provider.AddProperty(prop)
                return scoping_prop.GetGroupedProps()
            
            def AddProperty(self, name=None, prop_control=None, module_name=None):
                """
                Creates an instance of the property and connects delgates in 
                the associated Property Propvider.
    
                Keyword Arguments : 
                    name -- unique name for the scoping property
                    prop_control -- one of the built in controls, or extended controls
                    module_name -- module where the control is defined
                """
    
                #special case for scoping property
                if(prop_control == "Scoping" and module_name == "property_templates"):
                    return self.__AddScopingProperty(name)
                
                #if no module_name is passed, use the globals in current module
                #that has the built in controls imported
                prop_mod_globals = None
                if(module_name != None):
                    if(module_name not in globals()):
                        raise Exception("Unknown module : " + module_name)
                    
                    prop_mod_globals = globals()[module_name].get_globals()
                else:
                    prop_mod_globals = globals()
                
                #class name is built based on control + "Property"
                #    Double - > DoubleProperty
                prop_class_name = str(prop_control) + "Property"
                
                if(prop_class_name not in prop_mod_globals):
                    raise Exception("Unknown property class : " + prop_class_name)
                
                #instantiate the property based on module and class name
                prop = prop_mod_globals[prop_class_name](self.name + "/" + name, self.name)
                
                if(prop == None):
                    raise Exception("Issue while creating the property instance.")
                
                #set the delegates to property provider functions
                prop.IsValidCallback = self.provider.IsValid
                prop.IsReadOnlyCallback = self.provider.IsReadOnly
                prop.IsVisibleCallback = self.provider.IsVisible
                prop.GetValueCallback = self.provider.GetValue
                prop.SetValueCallback = self.provider.SetValue
    
                #as a default make the property name the property display name
                prop.DisplayName = name
                
                #add property to the provider
                self.provider.AddProperty(prop)
                
                return prop
            
        def __init__(self):
            pass
    
        def GetProperties(self):
            """
            Returns a list of properties in the order that they were added to the property provider. 
            """
            return [self.prop_map[propName] for propName in self.prop_list]
        
        def AddGroup(self, name=None):
            """
            Creates an instance of helper group class and returns it.
            """
            if name in self.prop_groups:
                raise Exception("Group with name " + name + " already exists, please use a unique group name.")
            
            #keep groups names so we can make sure no duplicate groups are added
            self.prop_groups.add(name)
            
            return self.__AnsGroup(name, self)
        
        def AddProperty(self, prop):
            """
            Method used by the helper group class to add the property to the data-structure holding
            the property instances.
            """
            if(prop.Name in self.prop_map):
                raise Exception("Property name must be unique, property with name '" + prop.Name + "' already exisits.")
            
            self.prop_list.append(prop.Name)
            self.prop_map[prop.Name] = prop
    #end region
    
    
    
    """
    Reload the properties at the end to make sure the class definition is executed before instantiation
    """
    reload_props()
    

    As for the script part, two methods are used. One to grab the user_files directory, one called during execution to grab the existing temperature result and export the data to a csv file:

    import os
    
    def GetUserFileFolder(analysis):
        '''
            Return UserDir: path of the folder
        '''
        try:
            WorkDir = analysis.WorkingDir
            UserDir = os.path.dirname(WorkDir)
            for i in range(3):
                UserDir = os.path.dirname(UserDir)
            UserDir = os.path.join(UserDir, 'user_files')
            if not os.path.exists(UserDir):
                os.makedirs(UserDir)
            return UserDir
        except:
            ExtAPI.Log.WriteMessage("Error : Exception in GetUserFileFolder()")
            return None
    
    def after_post(this, solution):# Do not edit this line
        temp_result = ExtAPI.DataModel.Project.Model.GetChildren(DataModelObjectCategory.TemperatureResult,True)[0]
        user_files = GetUserFileFolder(solution.Parent)
        DPValue = this.GetCustomPropertyByPath("Parameter/DPValue").Value
        file_name = "TemperatureResult_" + str(DPValue) + ".csv"
        temp_result.ExportToTextFile(os.path.join(user_files,file_name))
    

    From the parameter set, define a value for the DPValue parameter (defined in the properties provider) so that each export file has a different name:

    enter image description here

    When the DPs are updated, the exported files will be saved to the user_files directory:

    enter image description here

    Finally, some useful tips:

    • For the exported file to include nodal location, from Mechanical, go to File / Options / Export / Include Node Location.

    • For the Python code object to work during a DOE, from the WB project schematic, go to Tools / Options / Mechanical / Connect run Python code objects when Mechanical is launched

Answers

  • Pavel
    Pavel Member Posts: 9
    5 Likes Name Dropper First Answer Photogenic
    **

    Out of curiosity, any reason not to use native function to get user_files dir?

    def user_files_directory():
        user_files = ''
    
        if ExtAPI.Context == 'Project':
            user_files = GetUserFilesDirectory()
        elif ExtAPI.Context == 'Mechanical':
            import wbjn
            user_files = wbjn.ExecuteCommand(ExtAPI, "returnValue(GetUserFilesDirectory())")
        return user_files
    
  • Pernelle Marone-Hitz
    Pernelle Marone-Hitz Member, Moderator, Employee Posts: 871
    100 Answers 500 Comments 250 Likes First Anniversary
    ✭✭✭✭

    Hi @Pavel , your function for gettting the userfiles directory will work just fine too. I just reused some code I had lying around, that dates back to before GetUserFilesDirectory() was implemented, is all.