FEM Result Export to File

About the development of the FEM module/workbench.

Moderator: bernd

whoneyc
Posts: 22
Joined: Tue Apr 20, 2021 9:44 pm

Re: FEM Result Export to File

Post by whoneyc »

Beautiful @fandaL ! That was exactly the glue I was looking for. I made your example a little more robust by adding a parameter to allow me to play with several displacement degrees. I wish I could make a PR to add this to the codebase, but the GUI system in FEM is very confusing.

Code: Select all

# These dictionaries list the nodes, that define faces of an element.
# The key is the face number, used internally by FreeCAD.
# The list contains the nodes in the element for each face.
tetFaces = {
    1: [0, 1, 2],
    2: [0, 3, 1],
    3: [1, 3, 2],
    4: [2, 3, 0]}

pentaFaces = {
    1: [0, 1, 2],
    2: [3, 5, 4],
    3: [0, 3, 4, 1],
    4: [1, 4, 5, 2],
    5: [0, 2, 5, 3]}

hexaFaces = {        # hexa8 or hexa20 (ignoring mid-nodes)
    1: [0, 1, 2, 3],
    2: [4, 7, 6, 5],
    3: [0, 4, 5, 1],
    4: [1, 5, 6, 2],
    5: [2, 6, 7, 3],
    6: [3, 7, 4, 0]}

pyraFaces = {      # pyra5 or pyra13 (ignoring mid-nodes)
    1: [0, 1, 2, 3],
    2: [0, 4, 1],
    3: [1, 4, 2],
    4: [2, 4, 3],
    5: [3, 4, 0]}

face_dicts = {
    4: tetFaces,
    5: pyraFaces,
    6: pentaFaces,
    8: hexaFaces,
    10: tetFaces,
    13: pyraFaces,
    15: pentaFaces,
    20: hexaFaces}

def femmesh_2_mesh_disp(myFemMesh, myResults=None, myDisp=1):
    import time
    shiftBits = 20  # allows a million nodes, needs to be higher for more nodes in a FemMesh

    # This code generates a dict and a faceCode for each face of all elements
    # All faceCodes are than sorted.

    start_time = time.process_time()
    faceCodeList = []
    faceCodeDict = {}

    if myFemMesh.VolumeCount > 0:
        for ele in myFemMesh.Volumes:
            element_nodes = myFemMesh.getElementNodes(ele)
            # print("element_node: ", element_nodes)
            faceDef = face_dicts[len(element_nodes)]

            for key in faceDef:
                nodeList = []
                codeList = []
                faceCode = 0
                shifter = 0
                for nodeIdx in faceDef[key]:
                    nodeList.append(element_nodes[nodeIdx])
                    codeList.append(element_nodes[nodeIdx])
                codeList.sort()
                for node in codeList:
                    faceCode += (node << shifter)
                    # x << n: x shifted left by n bits = Multiplication
                    shifter += shiftBits
                # print("codeList: ", codeList)
                faceCodeDict[faceCode] = nodeList
                faceCodeList.append(faceCode)
    elif myFemMesh.FaceCount > 0:
        for ele in myFemMesh.Faces:
            element_nodes = myFemMesh.getElementNodes(ele)
            # print("element_node: ", element_nodes)
            faceDef = {1: [0, 1, 2]}

            for key in faceDef:
                nodeList = []
                codeList = []
                faceCode = 0
                shifter = 0
                for nodeIdx in faceDef[key]:
                    nodeList.append(element_nodes[nodeIdx])
                    codeList.append(element_nodes[nodeIdx])
                codeList.sort()
                for node in codeList:
                    faceCode += (node << shifter)
                    # x << n: x shifted left by n bits = Multiplication
                    shifter += shiftBits
                # print("codeList: ", codeList)
                faceCodeDict[faceCode] = nodeList
                faceCodeList.append(faceCode)

    faceCodeList.sort()
    allFaces = len(faceCodeList)
    actFaceIdx = 0
    singleFaces = []
    # Here we search for faces, which do not have a counterpart.
    # These are the faces on the surface of the mesh.
    while actFaceIdx < allFaces:
        if actFaceIdx < (allFaces - 1):
            if faceCodeList[actFaceIdx] == faceCodeList[actFaceIdx + 1]:
                actFaceIdx += 2
            else:
                # print("found a single Face: ", faceCodeList[actFaceIdx])
                singleFaces.append(faceCodeList[actFaceIdx])
                actFaceIdx += 1
        else:
            FreeCAD.Console.PrintMessage("Found a last Face: {}\n".format(faceCodeList[actFaceIdx]))
            singleFaces.append(faceCodeList[actFaceIdx])
            actFaceIdx += 1

    output_mesh = []
    if myResults:
        FreeCAD.Console.PrintMessage("{}\n".format(myResults.Name))
        for myFace in singleFaces:
            face_nodes = faceCodeDict[myFace]
            dispVec0 = myResults.DisplacementVectors[myResults.NodeNumbers.index(face_nodes[0])]
            dispVec1 = myResults.DisplacementVectors[myResults.NodeNumbers.index(face_nodes[1])]
            dispVec2 = myResults.DisplacementVectors[myResults.NodeNumbers.index(face_nodes[2])]
            triangle = [myFemMesh.getNodeById(face_nodes[0]) + dispVec0 * myDisp,
                        myFemMesh.getNodeById(face_nodes[1]) + dispVec1 * myDisp,
                        myFemMesh.getNodeById(face_nodes[2]) + dispVec2 * myDisp]
            output_mesh.extend(triangle)
            # print("my triangle: ", triangle)
            if len(face_nodes) == 4:
                dispVec3 = myResults.DisplacementVectors[myResults.NodeNumbers.index(face_nodes[3])]
                triangle = [myFemMesh.getNodeById(face_nodes[2]) + dispVec2 * myDisp,
                            myFemMesh.getNodeById(face_nodes[3]) + dispVec3 * myDisp,
                            myFemMesh.getNodeById(face_nodes[0]) + dispVec0 * myDisp]
                output_mesh.extend(triangle)
                # print("my 2. triangle: ", triangle)

    else:
        for myFace in singleFaces:
            face_nodes = faceCodeDict[myFace]
            triangle = [myFemMesh.getNodeById(face_nodes[0]),
                        myFemMesh.getNodeById(face_nodes[1]),
                        myFemMesh.getNodeById(face_nodes[2])]
            output_mesh.extend(triangle)
            # print("my triangle: ", triangle)
            if len(face_nodes) == 4:
                triangle = [myFemMesh.getNodeById(face_nodes[2]),
                            myFemMesh.getNodeById(face_nodes[3]),
                            myFemMesh.getNodeById(face_nodes[0])]
                output_mesh.extend(triangle)
                # print("my 2. triangle: ", triangle)

    end_time = time.process_time()
    FreeCAD.Console.PrintMessage(
        "Mesh by surface search method: {}\n".format(end_time - start_time)
    )
    return output_mesh


out_mesh = femmesh_2_mesh_disp(FreeCAD.ActiveDocument.ResultMesh.FemMesh, FreeCAD.ActiveDocument.CCX_Results, 100)
import Mesh
Mesh.show(Mesh.Mesh(out_mesh))
fandaL
Posts: 440
Joined: Thu Jul 24, 2014 8:29 am

Re: FEM Result Export to File

Post by fandaL »

I will not help much with the gui, but I tried to update the example: https://github.com/FreeCAD/FreeCAD/blob ... py#L37-L44
since it gives error on

Code: Select all

from femexamples.manager import *
doc = run_ccx_cantileverfaceload()
With FreeCAD 0.20.1 no error is with:

Code: Select all

from femexamples.manager import run_example
doc = run_example("ccx_cantilever_faceload")
However, this example does not contain result object from the analysis, so it does not demonstrate use of the femmesh_2_mesh function completely. FemCalculixCantilever3D example (from Start page) contains result object. Any hint how to load it by python or polish the code? (Macro recorder gives me a full file path)
fandaL
Posts: 440
Joined: Thu Jul 24, 2014 8:29 am

Re: FEM Result Export to File

Post by fandaL »

fandaL wrote: Wed Sep 21, 2022 9:48 pm However, this example does not contain result object from the analysis, so it does not demonstrate use of the femmesh_2_mesh function completely.
It should work now:
https://github.com/fandaL/FreeCAD/commi ... 66d1a0c584
Paramter for the displacement scale is there. The example code loads the FEM example and runs Calculix.
fandaL
Posts: 440
Joined: Thu Jul 24, 2014 8:29 am

Re: FEM Result Export to File

Post by fandaL »

There is a bug related to femmesh2mesh
- open FEM example, e.g., FemCalculixCantilever3D
- activate the analysis container
- select the mesh object and the CCX_result object. Click "FEM mesh to mesh" command
-- mesh object is created, considering deformed shape
- select the mesh object (without selecting CCX_result). Click "FEM mesh to mesh" command
-- deformed mesh is created, when only undeformed mesh should have been created.

The resulting object remained in the memory. This should fix it:
https://github.com/fandaL/FreeCAD/commi ... f43353d800
mac_the_bike
Posts: 42
Joined: Sun Jun 30, 2019 12:56 pm

Re: FEM Result Export to File

Post by mac_the_bike »

https://forum.freecadweb.org/viewtopic. ... 6&start=10

whoneyc

I am assuming that what you require is to create a mesh which is the same as the source mesh but has the scaled displacements added to the coordinates of the nodes.

The attachment is a script that adds displacements to the nodal coordinates.

Tested with 10 node Tet elements.
Works for 3D elements and doesn't work with triangular 2D elements

inputMesh - the name the source FEM mesh, input, e.g. "FEMMeshGmsh", "ResultMesh"
outputMesh - the name of the output FEM mesh
this has the same element and node numbers as the inputMesh
scale - multiplicative scale of displacements

operation:
then, assuming the name of the file is dispmesh.py:
import dispmesh

highlight a results objects, e.g. "CCX_EigenMode_1_Results"

then, e.g.:
o = dispmesh.dispmesh("FEMMeshGmsh", "OUTPUT", 1000.)

o is a FemMeshObject
OUTPUT is a distorted FEM mesh.

For big models, it doesn't run quickly, probably it doesn't run quickly for small models but you don't notice.
I have checked this on 1 model, you must check the output for yourself for other models.

Hack this as much as you want.

You notice that this works for 3D elements but not 2D.
I have looked at the "ResultMesh" from a 2D triangular element model and it looks like the mesh consists of volume elements, 15 node Penta elements. (Highlight the "ResultMesh" and right-click on "Display FEM Mesh Info".) It looks like nodes 13, 14 and 15 of these elements correspond to the 3 corner nodes of the original triangular elements. So if you are using 6 node triangular elements some important data is lost.
Attachments
dispmesh.py
(3.15 KiB) Downloaded 37 times
User avatar
bernd
Veteran
Posts: 12849
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: FEM Result Export to File

Post by bernd »

fandaL wrote: Wed Sep 21, 2022 9:48 pm FemCalculixCantilever3D example (from Start page) contains result object. Any hint how to load it by python ...

Code: Select all

from os.path import join
the_file = join(FreeCAD.getResourceDir(), "examples", "FemCalculixCantilever3D.FCStd")
fc_file = FreeCAD.openDocument(the_file)
fc_file.FileName

Code: Select all

Python 3.8.6+ (heads/3.8-dirty:a12f459ec2, Nov  5 2020, 12:01:10) [MSC v.1927 64 bit (AMD64)] on win32
Type 'help', 'copyright', 'credits' or 'license' for more information.
>>> 
>>> from os.path import join
>>> the_file = join(FreeCAD.getResourceDir(), "examples", "FemCalculixCantilever3D.FCStd")
>>> fc_file = FreeCAD.openDocument(the_file)
>>> # App.setActiveDocument("FemCalculixCantilever3D")
>>> # App.ActiveDocument=App.getDocument("FemCalculixCantilever3D")
>>> # Gui.ActiveDocument=Gui.getDocument("FemCalculixCantilever3D")
>>> fc_file.FileName
'C:/0_BHA_privat/progr/FreeCAD/FreeCAD_0.20.xxxxx/data/examples/FemCalculixCantilever3D.FCStd'
>>> 
>>> 
it is an old FreeCAD but hopefully this has not changed ...
User avatar
bernd
Veteran
Posts: 12849
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: FEM Result Export to File

Post by bernd »

fandaL wrote: Thu Sep 29, 2022 8:18 pm The resulting object remained in the memory. This should fix it:
https://github.com/fandaL/FreeCAD/commi ... f43353d800
Has this been merged into master?
fandaL
Posts: 440
Joined: Thu Jul 24, 2014 8:29 am

Re: FEM Result Export to File

Post by fandaL »

bernd wrote: Sat Oct 15, 2022 8:26 pm
fandaL wrote: Thu Sep 29, 2022 8:18 pm The resulting object remained in the memory. This should fix it:
https://github.com/fandaL/FreeCAD/commi ... f43353d800
Has this been merged into master?
Yes. https://github.com/FreeCAD/FreeCAD/comm ... 64d111ff72
fandaL
Posts: 440
Joined: Thu Jul 24, 2014 8:29 am

Re: FEM Result Export to File

Post by fandaL »

bernd wrote: Sat Oct 15, 2022 8:22 pm
fandaL wrote: Wed Sep 21, 2022 9:48 pm FemCalculixCantilever3D example (from Start page) contains result object. Any hint how to load it by python ...

Code: Select all

from os.path import join
the_file = join(FreeCAD.getResourceDir(), "examples", "FemCalculixCantilever3D.FCStd")
fc_file = FreeCAD.openDocument(the_file)
fc_file.FileName
Thanks, it works well. I have used it in my branch which enables to use scale factor with python commands
https://github.com/FreeCAD/FreeCAD/comp ... isp_scale3
If you agree, I will send a pull request.

With this file modified, users can scale the output (after selecting mesh and result object)

Code: Select all

import femmesh.femmesh2mesh
out_mesh = femmesh.femmesh2mesh.femmesh_2_mesh(FreeCAD.ActiveDocument.Mesh.FemMesh, FreeCAD.ActiveDocument.CCX_Results, 5)
import Mesh
Mesh.show(Mesh.Mesh(out_mesh))
where 5 is the scale factor.
User avatar
bernd
Veteran
Posts: 12849
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: FEM Result Export to File

Post by bernd »

go for it
Post Reply