Get successive elements with max user-defined result value.

Pernelle Marone-Hitz
Pernelle Marone-Hitz Member, Moderator, Employee Posts: 871
100 Answers 500 Comments 250 Likes First Anniversary
✭✭✭✭
edited June 2023 in Structures

I have a user-defined result (UDR)created in Mechanical. I'd like to select an element, and based on that starting point get the successive N elements with UDR value in the depth of the solid body.

Tagged:

Answers

  • Pernelle Marone-Hitz
    Pernelle Marone-Hitz Member, Moderator, Employee Posts: 871
    100 Answers 500 Comments 250 Likes First Anniversary
    ✭✭✭✭

    This post combines and adapts the code written in these two posts:

    A user-defined result (in this example, a nodal result, ie with Averaged option) is inserted in Mechanical:

    The properties of the Python Result allows the user to select the UDR, the element starting point (thanks to a named selection), and the number of layers to be considered:


    This is achieved by editing the Property Provider as follows:

    def reload_props():
        
        this.PropertyProvider = None
        # Create the property instance
        provider = Provider()
        
    
    
        input_group = provider.AddGroup("Input")
        ns_prop = input_group.AddProperty("Elemental Named Selection Name", Control.Expression)
        udr_prop = input_group.AddProperty("UDR Name", Control.Expression)
        nb_layer_prop = input_group.AddProperty("Number of Layers", Control.Double)
    
    
        
        output_group = provider.AddGroup("Output")
        output_prop = output_group.AddProperty("Average stress over selected elements", Control.Double)
        output_prop.CanParameterize = True
        output_prop.ParameterType = ParameterType.Output
        
        # Connects the provider instance back to the object by setting the PropertyProvider member on this, 'this' being the 
        # current instance of the Python Code object.
        this.PropertyProvider = provider
    

    The code for the Python Result is the combination of the codes shared in the two posts mentioned above, with some specifics:

    • The values in the user-defined result are transformed into a DPF field, and this field is passed to a fields container
    • The fields container is passed to an averaging operator to transform nodal data into elemental data. IMPORTANT NOTE: this part explains why plotted results might differ from the ones obtained in Mechanical, as the averaging method and the order of averaging is not the same.

    The code for the Python Result is:

    def post_started(sender, analysis):# Do not edit this line
        define_dpf_workflow(analysis)
    
    
    
    
    def define_dpf_workflow(analysis):
        
        # import DPF
        import mech_dpf
        import Ans.DataProcessing as dpf
        mech_dpf.setExtAPI(ExtAPI)
        
        # Connect to result file
        dataSource = dpf.DataSources(analysis.ResultFileName)
        
        # Retrieve property values
        selection_name = str(this.GetCustomPropertyByPath("Input/Elemental Named Selection Name").Value)
        udr_name = str(this.GetCustomPropertyByPath("Input/UDR Name").Value)
        n_layers = int(this.GetCustomPropertyByPath("Input/Number of Layers").Value)
        
        # Retrieve mesh 
        model=dpf.Model(dataSource)
        mesh=model.Mesh # whole mesh
        skin_mesh=dpf.operators.mesh.skin(mesh) # skin mesh 
        skin_nodes=skin_mesh.outputs.getnodes_mesh_scoping() # get nodes of skin mesh
        conv_to_elems=dpf.operators.scoping.transpose(mesh_scoping=skin_nodes,meshed_region=mesh) # convert to elements
        skin_elems_scoping=conv_to_elems.outputs.getmesh_scoping_as_scoping() # create a scoping with skin elements
    
    
        # Access User Defined Result and create a field out of it
        udr = ExtAPI.DataModel.GetObjectsByName(udr_name)[0]
        nb_nodes = len(udr.PlotData['Node']) # get number of nodes 
        udr_field = dpf.FieldsFactory.CreateScalarField(nb_nodes) # instanciate field
        udr_field.MeshedRegionSupport = mesh # attach mesh
        udr_field.ScopingIds = udr.PlotData['Node'] # give list of nodes
        udr_field.Data = [0. for i in range(0,nb_nodes)]# fill field with zeros
        node_ids = udr.PlotData['Node']
        values = [value for value in udr.PlotData['Values']]
        for ii in range(0,nb_nodes):
            index=udr_field.ScopingIds.IndexOf(node_ids[ii])         # get index of node in created field
            udr_field.UpdateEntityDataByEntityIndex(index,[values[ii]]) 
            
        # Create fields container from field
        field_container = dpf.FieldsContainer()
        field_container.Labels = ['time']
        field_container.Add(udr_field,{'time':1})
            
        # Transform nodal field to elemental field
        s = dpf.operators.averaging.nodal_to_elemental_fc()
        s.inputs.fields_container.Connect(field_container)
        s.inputs.mesh.Connect(mesh)
    
    
        # Get starting element from named selection
        selection_name=selection_name.upper()
        # Start element
        elem=dpf.operators.scoping.on_named_selection(named_selection_name=selection_name,
            requested_location='Elemental',data_sources=dataSource)
        elem_scoping=elem.outputs.getmesh_scoping()
        
        # Retrieve max stress on selected element
        s.inputs.scoping.Connect(elem_scoping)
        s_max=dpf.operators.min_max.min_max(s.outputs.getfields_container()[0])
        
        # Collect element id and stress values
        elem_ids=[elem.outputs.getmesh_scoping().Ids[0]]
        smax=[s_max.outputs.field_max.GetData().Data[0]]
        
        # Find next n elements with highest stresses but don't take into account 
        # any element with free surface (i.e. linked to outer skin)
        for i in range(0,n_layers):
            conv_to_nodes=dpf.operators.scoping.transpose(mesh_scoping=elem_scoping,meshed_region=mesh) # get nodes of selected element
            neighbour_mesh_op= dpf.operators.mesh.from_scoping(scoping=conv_to_nodes,mesh=mesh) # get elements attached to nodes
            # Remove skin related elements
            diff_scoping=dpf.operators.scoping.intersect(scopingA=neighbour_mesh_op.outputs.getmesh().ElementScoping,scopingB=skin_elems_scoping) 
            inside_elements_scoping=diff_scoping.outputs.scopingA_min_intersection.GetData()
            # Remove any of the previously identified elements from the scoping
            el_ids=list(inside_elements_scoping.Ids)
            for el in elem_ids:
                try:
                    el_ids.remove(el)
                except:
                    pass
            inside_elements_scoping.Ids=el_ids
            # Find element with highest stress value
            s.inputs.scoping.Connect(inside_elements_scoping)
            s_max=dpf.operators.min_max.min_max(s.outputs.getfields_container()[0])
            # Collect element id and stress values
            next_elem_id=s_max.outputs.field_max.GetData().ScopingIds[0]
            elem_ids.append(next_elem_id)
            smax.append(s_max.outputs.field_max.GetData().Data[0])
            # Before ending the loop, update start element to be last one found
            elem_scoping.Ids=[next_elem_id]
            
        # Set output value
        this.GetCustomPropertyByPath("Output/Average stress over selected elements").Value=sum(smax)/len(smax)
        
        # Display mesh will be only selected elements
        selected_scoping=dpf.Scoping()
        selected_scoping.Ids=elem_ids
        selected_scoping.Location='Elemental'
        s.inputs.scoping.Connect(selected_scoping)
        display_mesh=dpf.operators.mesh.from_scoping(scoping=selected_scoping,mesh=mesh)
    
    
        dpf_workflow = dpf.Workflow()
        dpf_workflow.Add(s)
        dpf_workflow.SetOutputMesh(display_mesh.outputs.getmesh())
        dpf_workflow.SetOutputContour(s)
        dpf_workflow.Record('wf_id', False)
        this.WorkflowId = dpf_workflow.GetRecordedId()
    

    And this is how the result looks like: