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