Is it possible to consider temperature dependent strength data for safety factor calculation?

Patrick Herberich
Patrick Herberich Member, Employee Posts: 2
First Comment First Anniversary Ansys Employee Photogenic

Is it possible to consider temperature dependent strength data for safety factor calculation within the stress tool?

Comments

  • Patrick Herberich
    Patrick Herberich Member, Employee Posts: 2
    First Comment First Anniversary Ansys Employee Photogenic

    It is possible via a Python Result object inside Mechanical. The following script calculates the safety factor considering temperature dependent limit stresses:

    def post_started(sender, analysis):# Do not edit this line
        define_dpf_workflow(analysis)
    
    
    def LinearInterpolation(x,x1,y1,x2,y2):
        # Simple linear interpolation
        # Returns -1 if x is outside of [x1,x2]
        if x<x1 or x>x2:
            return -1 # out of range
        if y1==y2:
            return y1
        else:
            a=(y2-y1)/(x2-x1)
            b=y1-a*x1
            return a*x+b
    
    def GetLimitStress(temperature,stress_data):
        # Find limit stress value for a given temperature
        # This is done by simple linear interpolation between two 
        # points of a limit stress/temperature curve
    
        # Find closest temperature in stress_data
        num_points=len(stress_data)/2
        for i in range(num_points):
            temp=stress_data[2*i]
            if temp>temperature:
                index=i
                break
        # Get temperature/limit stress points between which to interpolat          
        if index!=num_points:
            x2=stress_data[index*2]
            x1=stress_data[(index-1)*2]
            y2=stress_data[index*2+1]
            y1=stress_data[(index-1)*2+1]
        else:
            return -2 # temperature value is out of bounds
    
        return LinearInterpolation(temperature,x1,y1,x2,y2) # return limit stress
    
    def GetLimitStressField(stress,temperatures,stress_data):
        # From a field of stresses and temperatures, find limit stress
        # at each node. Stress_data defines the limit stress/temperature curve
        import mech_dpf
        import Ans.DataProcessing as dpf
    
        stress_nodes=stress.outputs.getfields_container()[0].ScopingIds
        limit_field=dpf.FieldsFactory.CreateScalarField(1,'Nodal')
    
        local_temp=temperatures.outputs.getfields_container()[0]
    
        for nid in stress_nodes:
            temp=local_temp.GetEntityDataById(nid)[0]
            limit_field.Add(nid,[float(GetLimitStress(temp,stress_data))])
    
        return limit_field
    
    def define_dpf_workflow(analysis):
        import mech_dpf
        import Ans.DataProcessing as dpf
    
        # Define limit stress data as list in form of [temp1,limit_stress1,temp2,limit_stress2,...])
        limit_stress_data=[-100.,200.,0.,220.,50.,210.,100,200.,1000.,150.]
    
        my_data_sources = dpf.DataSources(analysis.ResultFileName)
        my_time_scoping = dpf.Scoping()
        my_time_scoping.Ids = [1] # the first set
    
        # Retrieve structural temperature at nodes
        temperature=dpf.operators.result.structural_temperature(requested_location='Nodal')
        temperature.inputs.data_sources.Connect(my_data_sources)
        temperature.inputs.time_scoping.Connect(my_time_scoping)
    
        # Retrieve Mises stress at nodes
        s_eqv_op = dpf.operators.result.stress_von_mises()
        s_eqv_op.inputs.requested_location.Connect('Nodal')
        s_eqv_op.inputs.data_sources.Connect(my_data_sources)
        s_eqv_op.inputs.time_scoping.Connect(my_time_scoping)
    
        # Retrieve limit_stress for each node based on Mises and temperature
        limit_stress=GetLimitStressField(s_eqv_op,temperature,limit_stress_data)
    
        # Divide limit stress by Mises stress
        limit_factor=dpf.operators.math.component_wise_divide() 
        limit_factor.inputs.fieldA.Connect(limit_stress)
        limit_factor.inputs.fieldB.Connect(s_eqv_op)
    
    
        dpf_workflow = dpf.Workflow()
        dpf_workflow.Add(limit_factor)
        dpf_workflow.SetOutputContour(limit_factor)
        dpf_workflow.Record('wf_id', True)
        this.WorkflowId = dpf_workflow.GetRecordedId()
    
  • Mike.Thompson
    Mike.Thompson Member, Employee Posts: 239
    First Anniversary First Comment 5 Likes Ansys Employee

    Only for clarification and full context, I wanted to point out that mechanical has this general capability via a stress tool post processing object that will plot factors of safety that are temperature dependent based on material property.

    This forum is for scripted solutions, so obviously the solution is on point, and it is a good starting point for implementing more complex or specific processing routines, then what is offered in a native solution.

  • Pernelle Marone-Hitz
    Pernelle Marone-Hitz Member, Moderator, Employee Posts: 715
    First Comment First Anniversary Ansys Employee Solution Developer Community of Practice Member

    My understanding is that Mechanical's stress tool will indeed take into account temperature-dependent stress (as we can define E = f(T)),

    but natively it is not possible to define that the Tensile Yield Stress is temperature-dependent:

    which is what the code from @Patrick Herberich does.

  • Pernelle Marone-Hitz
    Pernelle Marone-Hitz Member, Moderator, Employee Posts: 715
    First Comment First Anniversary Ansys Employee Solution Developer Community of Practice Member

    Piggybacking on this solution. I've slightly modified the code so that instead of having the Yield Strenght vs Temp values hard-coded, they are read from a comma delimited csv file placed in the user_files directory of the project.


    Code is:

    def post_started(sender, analysis):# Do not edit this line
        define_dpf_workflow(analysis)
    
    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
    
    def LinearInterpolation(x,x1,y1,x2,y2):
        # Simple linear interpolation
        # Returns -1 if x is outside of [x1,x2]
        if x<x1 or x>x2:
            return -1 # out of range
        if y1==y2:
            return y1
        else:
            a=(y2-y1)/(x2-x1)
            b=y1-a*x1
            return a*x+b
    
    def GetLimitStress(temperature,stress_data):
        # Find limit stress value for a given temperature
        # This is done by simple linear interpolation between two 
        # points of a limit stress/temperature curve
    
        # Find closest temperature in stress_data
        num_points=len(stress_data)/2
        for i in range(num_points):
            temp=stress_data[2*i]
            if temp>temperature:
                index=i
                break
        # Get temperature/limit stress points between which to interpolat          
        if index!=num_points:
            x2=stress_data[index*2]
            x1=stress_data[(index-1)*2]
            y2=stress_data[index*2+1]
            y1=stress_data[(index-1)*2+1]
        else:
            return -2 # temperature value is out of bounds
    
        return LinearInterpolation(temperature,x1,y1,x2,y2) # return limit stress
    
    def GetLimitStressField(stress,temperatures,stress_data):
        # From a field of stresses and temperatures, find limit stress
        # at each node. Stress_data defines the limit stress/temperature curve
        import mech_dpf
        import Ans.DataProcessing as dpf
    
        stress_nodes=stress.outputs.getfields_container()[0].ScopingIds
        limit_field=dpf.FieldsFactory.CreateScalarField(1,'Nodal')
    
        local_temp=temperatures.outputs.getfields_container()[0]
    
        for nid in stress_nodes:
            temp=local_temp.GetEntityDataById(nid)[0]
            limit_field.Add(nid,[float(GetLimitStress(temp,stress_data))])
    
        return limit_field
    
    def get_stress_data():
        import csv
        import os
        limit_stress_data = []
        user_dir = user_files_directory()
        if os.path.exists(os.path.join(user_dir, 'TemperatureDependentYieldStrength.csv')):
            with open(os.path.join(user_dir, 'TemperatureDependentYieldStrength.csv')) as csv_file:
                csv_reader = csv.reader(csv_file, delimiter=',')
                line_count = 0
                for row in csv_reader:
                    if line_count == 0:
                        line_count += 1
                    else:
                        limit_stress_data.append(float(row[0]))
                        limit_stress_data.append(float(row[1]))
                        line_count += 1
        else:
            msg = Ansys.Mechanical.Application.Message('Temperature Dependent Yield Strenght file does not exist', MessageSeverityType.Error)
            ExtAPI.Application.Messages.Add(msg)
        return limit_stress_data
    
    def define_dpf_workflow(analysis):
        import mech_dpf
        import Ans.DataProcessing as dpf
    
        # Define limit stress data as list in form of [temp1,limit_stress1,temp2,limit_stress2,...])
        limit_stress_data = get_stress_data()
    
        my_data_sources = dpf.DataSources(analysis.ResultFileName)
        my_time_scoping = dpf.Scoping()
        my_time_scoping.Ids = [1] # the first set
    
        # Retrieve structural temperature at nodes
        temperature=dpf.operators.result.structural_temperature(requested_location='Nodal')
        temperature.inputs.data_sources.Connect(my_data_sources)
        temperature.inputs.time_scoping.Connect(my_time_scoping)
    
        # Retrieve Mises stress at nodes
        s_eqv_op = dpf.operators.result.stress_von_mises()
        s_eqv_op.inputs.requested_location.Connect('Nodal')
        s_eqv_op.inputs.data_sources.Connect(my_data_sources)
        s_eqv_op.inputs.time_scoping.Connect(my_time_scoping)
    
        # Retrieve limit_stress for each node based on Mises and temperature
        limit_stress=GetLimitStressField(s_eqv_op,temperature,limit_stress_data)
    
        # Divide limit stress by Mises stress
        limit_factor=dpf.operators.math.component_wise_divide() 
        limit_factor.inputs.fieldA.Connect(limit_stress)
        limit_factor.inputs.fieldB.Connect(s_eqv_op)
    
    
        dpf_workflow = dpf.Workflow()
        dpf_workflow.Add(limit_factor)
        dpf_workflow.SetOutputContour(limit_factor)
        dpf_workflow.Record('wf_id', True)
        this.WorkflowId = dpf_workflow.GetRecordedId()