Python Result example - mapping data

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

Is there an example of Python Result to map result data from one mesh to another mesh?

Tagged:

Answers

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

    Here's an example. This examples grabs the equivalent stress of another analysis, maps it to the current mesh, and plots the difference between the current equivalent stress and the equivalent stress obtained in the other analysis.

    Insert a Python Result object:

    Modify the define_dpf_workflow() method by copying and pasting the code below. Make sure to modify the path to the other result file! Also, due to formatting issues, please note that everything after the first line has to be indented - in doubt, download the model attached in another post, it contains the code with correct formatting.

    def define_dpf_workflow(analysis):
    import os
    import mech_dpf
    import Ans.DataProcessing as dpf
    mech_dpf.setExtAPI(ExtAPI)

    path_to_other_rst = os.path.join('C:\Users\Test','file.rst') # MODIFY THIS TO YOUR PATH
    analysis = ExtAPI.DataModel.Project.Model.Analyses[0]

    dataSource_1 = dpf.DataSources(analysis.ResultFileName)
    dataSource_2 = dpf.DataSources(path_to_other_rst)

    model_1 = dpf.Model(dataSource_1)
    mesh_1 = model_1.Mesh
    model_2 = dpf.Model(dataSource_2)
    mesh_2 = model_2.Mesh

    seqv_1 = dpf.operators.result.stress_von_mises()
    seqv_1.inputs.data_sources.Connect(dataSource_1)
    seqv_2 = dpf.operators.result.stress_von_mises()
    seqv_2.inputs.data_sources.Connect(dataSource_2)

    mapping_op = dpf.operators.mapping.on_coordinates()
    mapping_op.inputs.coordinates.Connect(mesh_1.CoordinatesField)
    mapping_op.inputs.fields_container.Connect(seqv_2.outputs.fields_container)
    mapping_op.inputs.mesh.Connect(mesh_1)

    minus_fc = dpf.operators.math.minus_fc()
    minus_fc.inputs.field_or_fields_container_A.Connect(seqv_1.outputs.fields_container)
    minus_fc.inputs.field_or_fields_container_B.Connect(mapping_op.outputs.fields_container.GetData())

    output = dpf.operators.utility.forward()
    output.inputs.any.Connect(minus_fc)


    dpf_workflow = dpf.Workflow()
    dpf_workflow.Add(output)
    dpf_workflow.SetOutputContour(output)
    dpf_workflow.Record('wf_id', False)
    this.WorkflowId = dpf_workflow.GetRecordedId()

    You can then connect and evaluate the result:

  • Mike.Thompson
    Mike.Thompson Member, Employee Posts: 366
    25 Answers 100 Comments Second Anniversary 25 Likes
    ✭✭✭✭

    @Pernelle Marone-Hitz , can you please also post the .wbpz file for the full context?

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

    @Mike.Thompson This is just a dummy model. Solve C component, store .rst somewhere, modify Python Result in B to grab the path to .rst from C. Models don't even have to be in the same Workbench project.

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

    Important note: to get better handling of midside nodes, starting from 2024R1 use the newly added pin "use_quadratic_elements":
    mapping_op.inputs.use_quadratic_elements.Connect(True)
    This is specifically useful for distorted quadratic elements.

  • Javier Vique
    Javier Vique Member, Employee Posts: 86
    Second Anniversary 5 Answers 25 Likes 10 Comments
    ✭✭✭✭

    If, instead of comparing two meshes, we want to map our results to a mesh done by a third-party tool, we can make use of the same operator, but we generate a field which contains the nodes coordinates from that third-party tool. Below the script which reads the nodes coordinates from fcoordinates_file (I took coarse mesh nodal coordinates of Pernelle's example) and maps the displacement solution:

    import os
    import mech_dpf 
    import Ans.DataProcessing as dpf
    
    analysis=Model.Analyses[0]
    dataSource = dpf.DataSources(analysis.ResultFileName)
    
    model=dpf.Model(dataSource)
    rst_mesh=model.Mesh
    
    op_d = dpf.operators.result.displacement()
    op_d.inputs.data_sources.Connect(dataSource)
    d_fc = op_d.outputs.fields_container.GetData()
    
    path = 'here_your_path'
    coordinates_file = os.path.join(path,'CoordinatesFiles.txt')
    with open(coordinates_file, 'r') as f:
        data = f.readlines()
    coordinates = []
    [coordinates.append(line.strip().split()) for line in data]
    coordinates = [[float(item) for item in inner_list] for inner_list in coordinates]
    
    fields_coordinates = dpf.FieldsFactory.Create3DVectorField(numEntities=len(coordinates),location='Nodal')
    [fields_coordinates.Add(i,coordinates[i-1]) for i in range(1,len(coordinates)+1)]
    
    op = dpf.operators.mapping.on_coordinates()
    op.inputs.fields_container.Connect(d_fc)
    op.inputs.coordinates.Connect(fields_coordinates)
    op.inputs.create_support.Connect(True)
    op.inputs.mesh.Connect(rst_mesh)
    my_fields_container = op.outputs.fields_container.GetData()
    

    Please be aware that newly added pin mapping_op.inputs.use_quadratic_elements.Connect(True) is not available in Ansys 2023R2, that's why it is not used, but it is recommended.

  • LudwigK
    LudwigK Member Posts: 2
    Name Dropper First Comment
    **

    Hello everyone,

    I was working with the example TestModel_Mapped.wbpz from @Pernelle Marone-Hitz (Uploaded August 9th) and on my side I have problems recreating the correct results.

    I use Ansys 2023 R2.02.

    When using the exact settings and Model (finer mesh in Model 2 and coarse mesh in model 1), I get way different mapped results:

    When using the same mesh (6 mm) for model 2 and 1 I get good results:

    There seems to be a mesh dependency (which in some way is o.k. but this seems to be a bigger problem).

    Without using:
    mapping_op.inputs.mesh.Connect(mesh_1)
    the results are way better. From the documentation:
    https://dpf.docs.pyansys.com/version/stable/api/ansys.dpf.core.operators.mapping.on_coordinates.html
    this command is just optional, but should still work, right?

    Anyone observed similar problems or know where this comes from?

    Thank you very much!

    Best regards
    Ludwig

  • LudwigK
    LudwigK Member Posts: 2
    Name Dropper First Comment
    **

    short update:
    I tested the exact same project in 2024R1 and the problem still appears.

    Can anyone confirm this problem?

    Thanks!
    Ludwig

  • Josselin GUEDON
    Josselin GUEDON Member Posts: 3
    Name Dropper First Comment
    **
    edited March 2024

    Hello @LudwigK,

    I think I experienced the same issue, removing the line mapping_op.inputs.mesh.Connect(mesh_1) seems to show better results.

    This plot is only the mapping of the refine mesh results (C) onto the coarse mesh (B).

    When I keep connecting mesh_1, I get this :

    However I am not sure how to get a better contour plot.
    @Pernelle Marone-Hitz , @Mike.Thompson would you have a clue ?

    Thanks.
    Josselin

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

    @Josselin GUEDON , can you please try with 2024R1 ? If the problem can be reproduced with the latest version, please open an official support case and I will look further into it.

  • Josselin GUEDON
    Josselin GUEDON Member Posts: 3
    Name Dropper First Comment
    **

    @Pernelle Marone-Hitz , I believe I can reproduce the problem in the latest version and it gives me somewhat an even worst plot (with holes in it) :

    I have opened a SR as you requested.
    For information, this test case is not customer related.

    Josselin

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

    Thanks @Josselin GUEDON , we'll look into this. Looks like something's off and could well be a bug.

  • Mike.Thompson
    Mike.Thompson Member, Employee Posts: 366
    25 Answers 100 Comments Second Anniversary 25 Likes
    ✭✭✭✭

    You should also be aware of many mapping additions at V24.1. You can now use the mapping that mechanical/workbench has used for many releases in DPF.

    https://discuss.ansys.com/discussion/3064

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

    Hi all, I had some feedback from the DPF team and there are two things to mention here:

    1. Issue when using mapping_op.inputs.mesh.Connect(mesh_1) : This is an error on my side, this line should not have been used here. Additional information for everyone to have a better understanding:

    The mapping_on_coordinates operator works by mapping an input field/fields_container (input 0) on a set of nodes (input 1). Then, there are the following configuration (optional) inputs:

    • create_support (input 2): this creates a MeshedSupport for the output FieldsContainer, consisting on a "fake" MeshedRegion with as many nodes as nodes there are in the set of input nodes (input 1), with point elements.
    • mapping_on_scoping (input 3): this just restricts the mapping to the scoping of the first Field of the FieldsContainer. This is to enhance performance (for example, if your FC has 2000 fields, one for each time step, and the mesh doesn't change, you can compute interpolation weights for just one Scoping and then apply it to all of them).
    • use_quadratic_elements (input 200): this enhances the way elements are searched. At the end of the day, we are mapping based on shape functions in a mesh, so the first step is to locate each node in input 1 in the mesh. If the element is quadratic, the sides can be parabolas, and taking this detail into account may lead to a more correct element identification. It, however, means spending more time in this search/locate step. Therefore, the default is False (as a general rule, quadratic elements are not normally that distorted). This was added in 24R1.
    • mesh (input 7): this is the key. If this pin is not connected, the mesh used to locate the input nodes will be the support of the input field. However, if connected, this will be the input mesh.
      In the case here, we are mapping the results of a fine mesh into a coarse mesh. However, the operator was connected like this:
    mapping_op = dpf.operators.mapping.on_coordinates()
    mapping_op.inputs.coordinates.Connect(nodes_of_coarse_mesh)
    mapping_op.inputs.fields_container.Connect(nodal_result_on_fine_mesh)
    mapping_op.inputs.use_quadratic_elements.Connect(True)
    mapping_op.inputs.mesh.Connect(coarse_mesh)
    

    The connections above don't make sense. We were using as "mesh" in input 7 a mesh that has nothing to do with the support of of nodal_result_on_fine_mesh: What is node 245 (for example) in the scoping of nodal_result_on_fine_mesh has nothing to do with the meaning of node 245 in coarse_mesh. Hence the first plot with random values.

    1. Plot with holes: Mapping results onto an external mesh is a very tricky operation involving many tolerances and estimations. A coarse mesh will most likely result in some missed mappings because it is impossible to find a "best" tolerance that works for all meshes. Whenever we 'miss' a node, the mapping will fail and that will result in an incomplete/patchy display. (Mechanical's policy is that it will draw/color-in the full element only if all nodes belonging to that element have result values. Else, it will only draw/color the nodes but the 'interior' of the element will not be filled in)
  • Gordon1998
    Gordon1998 Member Posts: 12
    Name Dropper First Comment
    **

    Hi, all.
    @Pernelle Marone-Hitz @Josselin GUEDON @Javier Vique
    Thanks for the great topic in the post. I have successfully replicated all the test results in Pernelle's example. However, I encountered a problem when I wanted to map the displacement results. If I keep everything else the same and only change the target results from

    seqv_1 = dpf.operators.result.stress_von_mises()
    seqv_1.inputs.data_sources.Connect(dataSource_1)
    seqv_2 = dpf.operators.result.stress_von_mises()
    seqv_2.inputs.data_sources.Connect(dataSource_2)
    

    to

    seqv_1 = dpf.operators.result.displacement()
    seqv_1.inputs.data_sources.Connect(dataSource_1)
    seqv_2 = dpf.operators.result.displacement()
    seqv_2.inputs.data_sources.Connect(dataSource_2)
    

    Then, mapped data will only yield null results. I have also tested 'displacement_X', 'displacement_Y', and 'displacement_Z'; none of them can produce any results.

    However, all strain and stress results can be mapped properly. I am wondering if there may be something wrong with mapping the displacement results. I have also had the same issue reported in my post. The link is given below:

    https://discuss.ansys.com/discussion/4613/read-results-at-the-specific-locations#latest