Create a field and plot results

t.lauss
t.lauss Member Posts: 5
Photogenic First Comment
**
edited June 2023 in Structures

Hey,

I want to investigate the fatigue of given loads steps with DPF in Python. Therefore, I extract all data from the *.rst file and compute my damage value for each node (e.g., for a named selection). Finally, this results in a list of node ids (resulting from the named selection) and associated damage values (from my computation).

Can you please help me to create a "new" field (e.g., damage)? How can I plot this results/field in a 3D contour plot?

Thank you,

Thomas

Tagged:

Best Answers

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

    Glad you like those posts! And nice to see that you've made great progress! You can plot the result on custom space domains, see this page for some examples: https://dpf.docs.pyansys.com/examples/00-basic/09-results_over_space_subset.html#sphx-glr-examples-00-basic-09-results-over-space-subset-py

  • t.lauss
    t.lauss Member Posts: 5
    Photogenic First Comment
    **
    edited January 2023 Answer ✓

    Below you will find a summary of the source code.

    # -*- coding: utf-8 -*-
    """
    Created on Wed Dec 28 12:37:46 2022
    @author: t.lauss
    """
    
    from ansys.dpf import core as dpf
    from ansys.dpf.core import operators as ops
    import functions # contains all functions for fatigue computation
    import os
    
    # load result files
    file_name = r"file.rst"
    link_file = r"...path_to_file..."
    your_result_file = os.path.join(link_file,file_name)
    
    # load model and print model infos
    model = dpf.Model(your_result_file)
    
    # Print available named selections
    print('Named selections in model:', model.metadata.available_named_selections)
    
    # Restrict data to a specific named selection
    ns_operator = ops.scoping.on_named_selection()
    ns_operator.inputs.data_sources.connect(model)
    ns_operator.inputs.named_selection_name.connect('ROD_CYL_FACE')
    # Get node numbers for nodes in named selection
    mesh_data = ns_operator.outputs.mesh_scoping.get_data()
    node_ids = mesh_data.ids
    
    # Extract principal streses on the named selection
    principal_stress = ops.result.stress_principal_1(data_sources=model)
    
    # Create list for result sets (every time id is associated to a load step for fatigue analysis)
    time_ids = list(range(1, model.metadata.time_freq_support.n_sets+1))
     
    # Restrict scoping to named selection
    principal_stress.inputs.mesh_scoping.connect(ns_operator.outputs.mesh_scoping)
    principal_stress.inputs.time_scoping.connect(time_ids)
    # extract fields for named selection and load steps
    fields = principal_stress.outputs.fields_container.get_data()
    
    # number of nodes
    N_nodes = len(mesh_data.ids)
    # number of time steps
    N_time = len(time_ids)
    
    # Parameter for fatigue calculation
    ...
    
    # Damage computation for all nodes of named selection
    damage = N_nodes*[0.0]
    for i in range(0,N_nodes):# loop over all nodes
        node_id = node_ids[i] # node id
        s1 = N_time*[0.0]     # temporary list of maximum principla stress for one node and all time steps
        idRed = N_time*[0]    # load step ID for stress calculation
        for j in range(0,N_time): # loop obrt all time steps
            # extract maximum principal stress for time step and node
            s1[j] = fields[j].get_entity_data_by_id(node_ids[i])[0]
            # get reduced pivoted load step IDs
            idRed[j] = N_time-j
        
        # Initialize damage value with zero
        Di = 0.0
        
        # associate stress data with fatigue spectrum (with reduced ID and load IDs)
        # get list of stresses to be classified by rainflow counting
        data = functions.nodeHashListLeewardLC1(idRed,s1) 
        # rainflow clasification
        res = functions.rainflowCount(data)
        N_Count = len(res)
        # Damage calculation for each rainflow cycle
        for k in range(0,N_Count):
            smax = res[k][0][0]
            smin = res[k][0][1]
            count = res[k][1]
            Di = Di + functions.getDamage(...)
        
        # ...Do this for all load cases...
        
        # Store accumulated damage value in list
        damage[i] = Di
        # Print infos to console
        print(node_id, Di, i/N_nodes*100, "%")
    
    
    # get the location and the max. Damage value
    maxDamage = max(damage)
    maxIndex  = damage.index(maxDamage)
    print("max. Damage: ", max(damage), "at Node: ", node_ids[maxIndex])
    
    
    ################################################################
    ############# Plot the damage in a 3d contour plot #############
    ################################################################
    
    # create a field which contains the damage value at particular nodes
    field_damage = dpf.fields_factory.create_scalar_field(N_nodes, dpf.locations.nodal)
    # store data in field at nodes
    for i in range(0,N_nodes):
        field_damage.append(damage[i], node_ids[i])
    
    # extract mesh for body with named selection name "RODEND"
    mesh_scoping = model.metadata.named_selection("RODEND")
    mesh = model.metadata.meshed_region
    op = dpf.operators.mesh.from_scoping(scoping=mesh_scoping, inclusive=1, mesh=mesh)
    result_mesh = op.outputs.mesh()
    # assign mesh to field that contains the damage values
    field_damage.meshed_region = result_mesh
    # plot the damage values
    sargs = dict(title="damage", fmt="%.2e")
    field_damage.plot(scalar_bar_args=sargs, meshed_region=result_mesh, screenshot='image.png',
                      cpos=[(0.0, -80.0, 20.0), (-0.1, -0.1, -1.0), (0.0, 0.8, -0.1)])
    # You can set the camera positions using the ``cpos`` argument.
    # The three tuples in the list for the ``cpos`` argument represent the camera
    # position, focal point, and view respectively.
    


Answers

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

    Hi @t.lauss , to create a new field, use dpf.fields_factory. An example is available here: https://dpf.docs.pyansys.com/examples/00-basic/03-create_entities.html

    Plotting options are explained there: https://dpf.docs.pyansys.com/examples/05-plotting/00-basic_plotting.html#sphx-glr-examples-05-plotting-00-basic-plotting-py

    If you're looking into plotting the results in Workbench Mechanical, you will have to use a Python Result. This post could be helpful: https://www.linkedin.com/pulse/script-tip-friday-how-sum-damage-results-structural-analysis/

  • t.lauss
    t.lauss Member Posts: 5
    Photogenic First Comment
    **

    Thank you for your help. The examples and the skript tips on friday are excellent, i absolutely like this.

    I created a scalar field with the dpf.fields_factory and store all damage values and the corresponding node ids in the field. I think this is working now.

    The problem now is that the results at the named selcetion (see Fig. 1) is hidden by other bodies (see Fig. 2). Is it possible to plot only the mesh and fileds of the named selection and/or is it possible to hide bodies during plotting?


  • t.lauss
    t.lauss Member Posts: 5
    Photogenic First Comment
    **

    I had already tried to create the plot using this example, but unfortunately I did not succeed.

    Here is my code (damage is a list with the damage values and node_ids is a list of same size with the associated node numbers)

    # create a field which contains the damage value at particular nodes
    # this is a “reserve” mechanism, not a resize one. This means that you need to append data to grow the size of your field.
    field_damage = dpf.fields_factory.create_scalar_field(N_nodes, dpf.locations.nodal)
    # store data in field at nodes
    for i in range(0,N_nodes):
        field_damage.append(Damage[i], node_ids[i])
    
    
    # create a mesh scoping for the named selection "ROD_CYL_FACE"
    mesh_scoping = model.metadata.named_selection("ROD_CYL_FACE")
    print(mesh_scoping)
    
    #???????????????? NOT WORKING ????????????????
    field_damage.meshed_region = ops.mesh.from_scoping(mesh_scoping)
    
    # plot 
    sargs = dict(title="damage", fmt="%.2e")
    field_damage.plot(scalar_bar_args=sargs)
    

    Output of print(mesh_scoping): DPF Scoping:  with Nodal location and 2175 entities

    After the mesh_scoping I don't really know how to proceed. How can I associate the mesh_scoping and the field_damage?

    Moreover, in the mentioned DPF examples I do not found how to plot only the results of one body and the other bodys are not visible. My problem is that the interesting damage region is hidden by other bodys and therefore not visible for me.

  • t.lauss
    t.lauss Member Posts: 5
    Photogenic First Comment
    **

    Thank you for your help! My code works now 😀


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

    Thanks @t.lauss , happy to help! If you are willing to share, feel free to post your code as an example here for others to benefit from.

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

    Thanks a lot for sharing @t.lauss ! I'm sure this will be of great help to other users!