Postprocess stress over profile length for each layer of a solid composite model.
I have a solid composite model created in ACP.
I would like to post-process the stress over the profile length for each layer, so for example along the curved line that passes through these nodes:
Answers
-
This is possible by using DPF and a Python Result. Below is a detailed explanation of how the example works and can be adapted.
Defining named selections
The code shared below uses a logic based on named selection names.
A first named selection should provide the "base", so the surface from which the solid is extruded in ACP:
A second named selection should provide the extrusion direction:
Note - it is recommended to use CAPITAL letters to define the names of the named selections, as the named selections are always sent to the solver with capital letters and Python is case sensitive.
Selection for the named selections and the layer number
The user has to provide the name of the named selections, and the layer number for which they would like to do the postprocessing. This is done in the details' view of the Python Result object:
To obtain such a details' view, the Property Provider of the Python Result object has to be modified as follows:
def reload_props(): this.PropertyProvider = None provider = Provider() group1 = provider.AddGroup("Named Selections") base_ns = group1.AddProperty("Base face NS name", Control.Expression) extrusion_ns = group1.AddProperty("Extrusion face NS name", Control.Expression) group2 = provider.AddGroup("Layer number") double_prop = group2.AddProperty("Layer number", Control.Double) this.PropertyProvider = provider
Getting the logic to obtain the appropriate nodes for post-processing
The logic is very similar to the MAPDL "ESLN/NSLE" logic, which selects nodes attached to the selected elements, and elements attached to the selected nodes.
First we select all the nodes attached to the base face:
Then we select all the elements attached to these nodes:
Then the nodes attached to the selected elements:
We keep a list of already selected nodes to filter out the nodes that have already been used:
With this logic, we are able to get the nodes on a specific layer:
Then, to obtain the path, we must only keep the nodes that also belong to the "extrusion" face:
This is obtained by a simple boolean operation to get the intersection between two scopings.
Obtaining a line display
This is achieved by using the
GFXContourType.GeomEdgeScoping
option in the SetOutPutContour() method.Script for the Python Result
The complete DPF script for the Python Result object is as follows:
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 mech_dpf.setExtAPI(ExtAPI) dataSource = dpf.DataSources(analysis.ResultFileName) base_ns_name = str(this.GetCustomPropertyByPath("Named Selections/Base face NS name").Value) extrusion_ns_name = str(this.GetCustomPropertyByPath("Named Selections/Extrusion face NS name").Value) nb_layer = int(this.GetCustomPropertyByPath("Layer number/Layer number").Value) # Retrieve mesh model=dpf.Model(dataSource) mesh=model.Mesh # whole mesh # Get first layer of nodes nodes_level_0 = dpf.operators.scoping.on_named_selection(named_selection_name=base_ns_name,data_sources=dataSource) used_node_ids = list(nodes_level_0.outputs.mesh_scoping.GetData().Ids) for layer_index in range(1,nb_layer): elements_level_0 = dpf.operators.scoping.transpose(mesh_scoping=nodes_level_0,meshed_region=mesh) #elements_level_0.outputs.mesh_scoping.GetDataT1().Ids nodes_level_1_all = dpf.operators.scoping.transpose(mesh_scoping=elements_level_0,meshed_region=mesh) diff_nodes=dpf.operators.scoping.intersect(scopingA=nodes_level_1_all,scopingB=nodes_level_0) nodes_level_1 = diff_nodes.outputs.scopingA_min_intersection #nodes_level_1.GetData().Ids node_ids_level_1 = list(nodes_level_1.GetData().Ids) node_ids_to_keep = [id for id in node_ids_level_1 if id not in used_node_ids] added_used_node_ids = [id for id in node_ids_level_1 if id in used_node_ids] used_node_ids = used_node_ids + added_used_node_ids nodes_level_0 = dpf.Scoping() nodes_level_0.Ids = node_ids_to_keep used_node_ids = used_node_ids + node_ids_to_keep # Get nodes also part of EXTRUSION NS nodes_extrusion_ns = dpf.operators.scoping.on_named_selection(named_selection_name=extrusion_ns_name,data_sources=dataSource) # nodes_extrusion_ns.outputs.mesh_scoping.GetData().Ids diff_nodes_2=dpf.operators.scoping.intersect(scopingA=nodes_level_0,scopingB=nodes_extrusion_ns) nodes_path = diff_nodes_2.outputs.intersection #nodes_path.GetData().Ids # Equivalent stress on selected nodes seqv = dpf.operators.result.stress_von_mises() seqv.inputs.data_sources.Connect(dataSource) seqv.inputs.mesh_scoping.Connect(nodes_path) seqv.outputs.fields_container.GetData() # Plot dpf_workflow = dpf.Workflow() dpf_workflow.Add(seqv) dpf_workflow.SetOutputContour(seqv,dpf.enums.GFXContourType.GeomEdgeScoping) dpf_workflow.Record('wf_id', False) this.WorkflowId = dpf_workflow.GetRecordedId()
This is the result that is obtained:
0