How to access tabular data values in Mechanical scripting without the GUI open?

Jimmy He
Jimmy He Member, Employee Posts: 24
10 Comments 5 Likes First Answer First Anniversary
✭✭✭✭

I want to access the tabular data values of Mechanical result objects, but I need to do that without the GUI open, so the GetPane() approach will not work. Also, the mechanical objects are reaction probes, so they do not have ExportToTextFile method.

I understand that we can create a result object for each output time step and read the value at the displayed time instead of from the table, but I am hoping to see if there are any alternatives that are more efficient and elegant.

Answers

  • Mike.Thompson
    Mike.Thompson Member, Employee Posts: 366
    25 Answers 100 Comments Second Anniversary 25 Likes
    ✭✭✭✭

    The most stable way to do this is bypass the objects and recreate the post processing with DPF. In general you can use the element nodal forces operator to get reaction probe data. If you have more specifics then please post, but in general it would be great to have a DPF based method that recreates the probe calcs.

  • Landon Mitchell Kanner
    Landon Mitchell Kanner Member, Employee, GitHub-issue-creator Posts: 327
    50 Answers 100 Comments Second Anniversary 25 Likes
    ✭✭✭✭

    The tabular data is calculated on the fly by the GUI, so there is no way to get it directly in batch mode.
    Here is a hack I put together a while ago. Hope it is useful. Full file is attached in case I missed any needed functions.

    APINames = ['Minimum','Maximum','Average'] 
    
    def GetTabularData(obj):
        ExtAPI.Log.WriteMessage('GetTabularData..')
        TableProps = GetTableProperties(obj)
        return TP2TD(obj,TableProps)
    
    def GetTableProperties(obj):
        ExtAPI.Log.WriteMessage('GetTableProperties...')
        VisProps = obj.VisibleProperties
        TableProps = []
        if not IsResult(obj):
            for p in VisProps:
                if p.APIName != None:
                    prop = getattr(obj,p.APIName)
                    if prop.GetType() == Ansys.ACT.Mechanical.Fields.Field:
                        TableProps.Add(prop)
                        # with open(r'D:\test.txt','w') as f:
                            # f.write(prop.Output.Name)
                    #prop.Inputs[0].Name
        else:
            TableProps = [getattr(obj,APIName) for APIName in APINames]
        return TableProps
    
    def TP2TD(obj,tp):
        ExtAPI.Log.WriteMessage('TP2TD...')
        ExtAPI.Log.WriteMessage('debug0')
        # Would be more efficient to update all objs at once
        if not IsResult(obj):
            ExtAPI.Log.WriteMessage('Load')
            td0 = []
            td0.Add('Time [s]')
            for p in tp:
                ExtAPI.Log.WriteMessage('debug1: {}'.format(p.Name))
                td0.Add(p.Output.Name+' [{}]'.format(p.Output.Unit))
                if p.Output.DiscreteValueCount == 0 and p.Output.DefinitionType == p.Output.DefinitionType.Formula:
                    ExtAPI.Log.WriteMessage('DiscreteValueCount == 0')
                    formula = p.Output.Formula
                    p.Output.Formula = formula
            td = [td0]
            if len(tp)>0:
                for i in range(p.Output.DiscreteValueCount):
                    row = [str(tp[0].Inputs[0].DiscreteValues[i].Value)]
                    for p in tp:
                        row.Add(str(p.Output.DiscreteValues[i].Value))
                    td.Add(row)
        else:
            ExtAPI.Log.WriteMessage('Result')
            units = [str(getattr(obj,APIName).Unit) for APIName in APINames]
            td0 = []
            td0.Add('Time [s]')
            ExtAPI.Log.WriteMessage('debug2')
            for APIName,unit in zip(APINames,units):
                td0.Add('{} [{}]'.format(APIName,unit))
            td = [td0]
            analysis = obj.Parent.Parent #SOK?
            if os.path.isfile(analysis.ResultFileName):  # Just neeed row0 for output list
                with analysis.GetResultsData() as reader:
                    Timepoints = reader.ListTimeFreq
                obj.By = obj.By.ResultSet 
                obj.CalculateTimeHistory = False
                ExtAPI.Log.WriteMessage('debug3')
                for i,time in enumerate(Timepoints):
                    obj.SetNumber = i+1
                    obj.EvaluateAllResults()
                    values = [str(time)]+[str(getattr(obj,APIName).Value) for APIName in APINames]
                    td.Add(values)
        return td
    
    def IsResult(obj):
        #Is there a Better way?
        return 'Ansys.ACT.Automation.Mechanical.Results' in str(obj.GetType())
    
  • Jimmy He
    Jimmy He Member, Employee Posts: 24
    10 Comments 5 Likes First Answer First Anniversary
    ✭✭✭✭

    @Mike.Thompson said:
    The most stable way to do this is bypass the objects and recreate the post processing with DPF. In general you can use the element nodal forces operator to get reaction probe data. If you have more specifics then please post, but in general it would be great to have a DPF based method that recreates the probe calcs.

    Hi Mike, thanks for your suggestion! I am looking into a DPF solution now.

  • Jimmy He
    Jimmy He Member, Employee Posts: 24
    10 Comments 5 Likes First Answer First Anniversary
    ✭✭✭✭

    @Landon Mitchell Kanner said:
    The tabular data is calculated on the fly by the GUI, so there is no way to get it directly in batch mode.
    Here is a hack I put together a while ago. Hope it is useful. Full file is attached in case I missed any needed functions.

    APINames = ['Minimum','Maximum','Average'] 
    
    def GetTabularData(obj):
        ExtAPI.Log.WriteMessage('GetTabularData..')
        TableProps = GetTableProperties(obj)
        return TP2TD(obj,TableProps)
    
    def GetTableProperties(obj):
        ExtAPI.Log.WriteMessage('GetTableProperties...')
        VisProps = obj.VisibleProperties
        TableProps = []
        if not IsResult(obj):
            for p in VisProps:
                if p.APIName != None:
                    prop = getattr(obj,p.APIName)
                    if prop.GetType() == Ansys.ACT.Mechanical.Fields.Field:
                        TableProps.Add(prop)
                        # with open(r'D:\test.txt','w') as f:
                            # f.write(prop.Output.Name)
                    #prop.Inputs[0].Name
        else:
            TableProps = [getattr(obj,APIName) for APIName in APINames]
        return TableProps
    
    def TP2TD(obj,tp):
        ExtAPI.Log.WriteMessage('TP2TD...')
        ExtAPI.Log.WriteMessage('debug0')
        # Would be more efficient to update all objs at once
        if not IsResult(obj):
            ExtAPI.Log.WriteMessage('Load')
            td0 = []
            td0.Add('Time [s]')
            for p in tp:
                ExtAPI.Log.WriteMessage('debug1: {}'.format(p.Name))
                td0.Add(p.Output.Name+' [{}]'.format(p.Output.Unit))
                if p.Output.DiscreteValueCount == 0 and p.Output.DefinitionType == p.Output.DefinitionType.Formula:
                    ExtAPI.Log.WriteMessage('DiscreteValueCount == 0')
                    formula = p.Output.Formula
                    p.Output.Formula = formula
            td = [td0]
            if len(tp)>0:
                for i in range(p.Output.DiscreteValueCount):
                    row = [str(tp[0].Inputs[0].DiscreteValues[i].Value)]
                    for p in tp:
                        row.Add(str(p.Output.DiscreteValues[i].Value))
                    td.Add(row)
        else:
            ExtAPI.Log.WriteMessage('Result')
            units = [str(getattr(obj,APIName).Unit) for APIName in APINames]
            td0 = []
            td0.Add('Time [s]')
            ExtAPI.Log.WriteMessage('debug2')
            for APIName,unit in zip(APINames,units):
                td0.Add('{} [{}]'.format(APIName,unit))
            td = [td0]
            analysis = obj.Parent.Parent #SOK?
            if os.path.isfile(analysis.ResultFileName):  # Just neeed row0 for output list
                with analysis.GetResultsData() as reader:
                    Timepoints = reader.ListTimeFreq
                obj.By = obj.By.ResultSet 
                obj.CalculateTimeHistory = False
                ExtAPI.Log.WriteMessage('debug3')
                for i,time in enumerate(Timepoints):
                    obj.SetNumber = i+1
                    obj.EvaluateAllResults()
                    values = [str(time)]+[str(getattr(obj,APIName).Value) for APIName in APINames]
                    td.Add(values)
        return td
    
    def IsResult(obj):
        #Is there a Better way?
        return 'Ansys.ACT.Automation.Mechanical.Results' in str(obj.GetType())
    

    Hi Landon, thank you so much for your response! I tested your code on my model, it seems like it relies on the Maximum/Mininum/Average attributes of the result object. This works well for getting the max of the temperature fields, but these attributes are not available for a thermal reaction probe, so I was unable to use this to get reaction force values.

  • Jimmy He
    Jimmy He Member, Employee Posts: 24
    10 Comments 5 Likes First Answer First Anniversary
    ✭✭✭✭

    To refine my question a bit more given the added information from the previous responses, I am now looking to extract results using DPF. I have a thermal analysis and I am looking to extract the total heat rate (in Watts) over a named selection. This can be done in Mechanical by a reaction probe scoped to the named selection. So, I started with the reaction_force and nodal_force operators in DPF. However, neither of those seem to be available for a rth file.

    I have the following workaround using heat flux:

    op = dpf.operators.result.heat_flux() # operator instantiation
    op.inputs.data_sources.Connect(data_sources)
    # op.inputs.requested_location.Connect('Nodal')
    op.inputs.mesh_scoping.Connect(mesh_scoping)
    op.inputs.time_scoping.Connect(time_scoping)
    norm = dpf.operators.math.norm_fc() # operator instantiation
    norm.inputs.fields_container.Connect(op)
    sum_op = dpf.operators.math.accumulate_fc() # operator instantiation
    sum_op.inputs.fields_container.Connect(norm)
    
    my_scale_factor = 1e-6 / len(node_ids) * face_area
    scale = dpf.operators.math.scale_fc() # operator instantiation
    scale.inputs.fields_container.Connect(sum_op)
    scale.inputs.ponderation.Connect(my_scale_factor)
    
    heat_rate = scale.outputs.fields_container.GetData()
    

    but this sometimes give some large errors, presumably because dpf.operators.result.heat_flux() by default returns results in element_nodal location, when passed a nodal named selection (switching output location to nodal), some operations are performed under the hood (https://github.com/ansys/pydpf-core/issues/258), but I am not 100% sure.

    Could anyone suggest a better method to get the total heat rate on the named selection? Thanks a lot!