How to assess the maximum stress range for a fatigue assessment when there are several load cases?


This might be a trivial task if there is a manageable number of load cases. Once the load case number is increasing and/or the data is sitting in different result files the effort for doing it may get time consuming.
Answers
-
One way would be via a python result object in conjunction with the Data Processing Framework module, to fetch the according data and evaluate the maximum stress range over all time points. Further on, it is possible to reference other result files to include these within the evaluation, assuming they are utilizing the same mesh (Node IDs). This example will perform the stress range based on maximum and minimum principal stress.
To enable this, you will need to include a Python Result Object >>>
The object itself has a main script and a property provider (e.g. enabling scoping methods)
After inserting the object, the property provider script was altered to include a geometry scoping possibility which may reduce the time needed for evaluation. The function reload_props() was replaced by following script
- def reload_props():
- this.PropertyProvider = None
- provider = Provider()
- group_one = provider.AddGroup("Group 1")
- scoping_prop = group_one.AddProperty("Scoping Property", "Scoping", "property_templates")
- this.PropertyProvider = provider
Reload the properties for that object
Then you should be able to select a geometric entity for that object.
Afterwards replace the whole default main script with the following python snippet, whereas the part of the individual result file reference may need some adjustment.
- def post_started(sender, analysis):# Do not edit this line
- define_dpf_workflow(analysis)
- def define_dpf_workflow(analysis):
- import mech_dpf
- import Ans.DataProcessing as dpf
- #Utilizing geometry scoping > time reduction in calculation time
- scoping_refs = this.GetCustomPropertyByPath("Group 1/Scoping Property/Geometry Selection").Value
- mech_dpf.setExtAPI(ExtAPI)
- nodes_loc = mech_dpf.GetNodeScopingByRefId(scoping_refs)
- #refering to the individual rst files
- dataSource_a = dpf.DataSources()
- dataSource_a.SetResultFilePath(analysis.ResultFileName) #the result file where the py result object was added to
- dataSource_b = dpf.DataSources()
- dataSource_b.SetResultFilePath(r'C:\...Mechanical\pyResultObject\StressRange_2023R1_files\dp0\SYS\MECH\file.rst')
- #result file list which will be looped thru
- data = [dataSource_a,dataSource_b] # [dataSource_a,dataSource_b,dataSource_c, ...]
- maxprin_fields = []
- minprin_fields = []
- #function to convert units if needed
- def convert_unit(field,unit):
- op = dpf.operators.math.unit_convert()
- op.inputs.unit_name.Connect(unit)
- op.inputs.entity_to_convert.Connect(field)
- converted_field = op.outputs.converted_entity.GetDataT1()
- return converted_field
- #loop thru the individual result files
- for i,rst in enumerate(data):
- op = dpf.operators.metadata.time_freq_provider() # operator instanciation
- op.inputs.data_sources.Connect(rst)
- my_time_freq_support= op.outputs.time_freq_support.GetData() #get the time points in the result file
- # read in min max principal stress for all time points
- stress_prin1 = dpf.operators.result.stress_principal_1(data_sources=rst,mesh_scoping=nodes_loc)
- stress_prin3 = dpf.operators.result.stress_principal_3(data_sources=rst,mesh_scoping=nodes_loc)
- #loop thru the time points
- for j,time in enumerate(my_time_freq_support.TimeFreqs.Data):
- stress_prin1.inputs.time_scoping.Connect(time)
- stress_prin3.inputs.time_scoping.Connect(time)
- stress_prin1.inputs.mesh_scoping.Connect(nodes_loc)
- stress_prin3.inputs.mesh_scoping.Connect(nodes_loc)
- field_container_max = stress_prin1.outputs.fields_container.GetData()
- field_container_min = stress_prin3.outputs.fields_container.GetData()
- #create list of min and max stress
- maxprin_fields.append(field_container_max[0])
- minprin_fields.append(field_container_min[0])
- #create a field container holding all the data
- max_prin_field_container = dpf.FieldsContainerFactory.OverTimeFreqFieldsContainer(maxprin_fields)
- min_prin_field_container = dpf.FieldsContainerFactory.OverTimeFreqFieldsContainer(minprin_fields)
- # check unit systems if there is a mismatch the first entry will be utilized for converting
- count=max_prin_field_container.FieldCount
- for i in range(0,count):
- if i ==0:
- unit = max_prin_field_container[i].Unit
- else:
- if max_prin_field_container[i].Unit != unit:
- max_prin_field_container[i]=convert_unit(max_prin_field_container[i],unit)
- if min_prin_field_container[i].Unit != unit:
- min_prin_field_container[i]=convert_unit(min_prin_field_container[i],unit)
- #fetch the min and max over all time points of all provided result files
- op = dpf.operators.min_max.min_max_by_entity() # operator instanciation
- op.inputs.fields_container.Connect(max_prin_field_container)
- my_field_max = op.outputs.field_max.GetData()
- op.inputs.fields_container.Connect(min_prin_field_container)
- my_field_min = op.outputs.field_min.GetData()
- #evaluate the range > delta_sigma = simga_1 - simga_3
- stress_range = dpf.operators.math.minus(fieldA=my_field_max,fieldB=my_field_min)
- ############### Plotting
- dpf_workflow = dpf.Workflow()
- dpf_workflow.Add(stress_range)
- dpf_workflow.SetOutputContour(stress_range.outputs.field.GetData(),dpf.enums.GFXContourType.GeomFaceScoping)
- dpf_workflow.Record('wf_id', True)
- this.WorkflowId = dpf_workflow.GetRecordedId()
The last step would be to connect the script followed by ‘Evaluate All Results’
The outcome will look like this S1(1072.064) – S3(-545.344) =delta_stress (1617.408)
0