Cancel
Showing results for 
Search instead for 
Did you mean: 

Get element centroid by array

Creator
Creator

Hello,

 

I'm trying to speed up my API/Excel code. At the moment I'm looping through the elements:

 

While feSet.Next

'Get element info
        rc = feElem.Get(feSet.CurrentID)
        rc = feElem.GetCentroid(arrCG)
        
'Set arrays for addarray load sequence
        nFace = 2           'Face of element to be analyzed
        arrnFace(setCount * 3) = nFace
        arrnFace(setCount * 3 + 1) = nFace
        arrnFace(setCount * 3 + 2) = nFace
        arreID(setCount) = feSet.CurrentID
        arrFunctionID(setCount * 5) = 0

setCount = setCount + 1
Wend

 

Looping through 15.000 elements makes this a bit slow. Is there a way to get the ID information and centroid information of the elements as an array from the femap database?

 

Thanks in advance,

 

Casper

18 REPLIES

Re: Get element centroid by array

Legend
Legend

Hello Casper,

 

 

You can get all elements IDs and coordinates using one call Element.GetAllArray. The function is a bit complex and requires to filter coordinates. 

 

methods.png

 

In the Nodes - you can find coordinates. 

 

If works very quickly. Let me know if you need an example. I think I have it somewhere.

 

Yarko

Re: Get element centroid by array

Creator
Creator

Hello Yarko,

 

Aha, I've been looking at that method, but thought it was possible to extract elements centroid coordinates.

I'll give it a try, but an extra example is always appreciated! Smiley Happy

 

Thanks in advance.

 

Casper

Re: Get element centroid by array

Legend
Legend

Hello Casper,

 

 

I've prepared a script which calculates centroid for elements. As I mentioned in previous post - Element.GetAllArray function was used. Also Node.GetCoordArray is used, because Ifirst function gives nodeIds but not coordinates as I thought.

 

I've added some comments to explain what I did.


Sub Main
    Dim App As femap.model
    Set App = feFemap()

    'Get all elements
    Dim Element As femap.Elem
    Set Element = App.feElem

    Dim numElem As Long
    Dim entID As Variant
    Dim propID As Variant
    Dim elemType As Variant
    Dim topology As Variant
    Dim Lyr As Variant
    Dim color As Variant
    Dim formulation As Variant
    Dim orient As Variant
    Dim offset As Variant
    Dim release As Variant
    Dim orientSET As Variant
    Dim orientID As Variant
    Dim Nodes As Variant
    Dim connectTYPE As Variant
    Dim connectSEG As Variant

    Element.GetAllArray(0, numElem, entID, propID, _
    elemType, topology, Lyr, color, formulation,orient,offset, _
    release, orientSET, orientID, Nodes, connectTYPE, connectSEG )

    'Get coordinates of all nodes
    Dim n As femap.Node
    Set n = App.feNode

    Dim NumNode As Long
    Dim nodeID As Variant
    Dim Xyz As Variant
    n.GetCoordArray(0, NumNode, nodeID, Xyz)

    Dim ElementNodes(19) As Integer

    'Loop through all elements and calculate centroid for each
    For i = 0 To numElem - 1
        'copy elements nodes to separate array
        For j = 0 To 19
            ElementNodes(j) = Nodes(j + i* 20)
        Next

        'calculate centroid based on nodes coordinates. Quick Method.
        Dim centroid() As Double
        centroid = CalculateCentroid(ElementNodes,NumNode, nodeID, Xyz)
        'printing a message will slow down the tool. Use only for testing
        'App.feAppMessage(FCM_NORMAL, "Method1. Element ID = " & entID(i) & "  x = " & centroid(0) & ", y = " & centroid(1) & ", z = " & centroid(2))

        'calculate centroid using Element.GetCentroid. For testing purpose
        'Dim centroid2() As Double
        'centroid2 = CalculateCentroidUsingFemapFunction(Element, entID(i))
        'App.feAppMessage(FCM_NORMAL, "MEthod2. Element ID = " & entID(i) & "  x = " & centroid2(0) & ", y = " & centroid2(1) & ", z = " & centroid2(2))
    Next

End Sub

Function CalculateCentroid(ByVal ElementNodes As Variant,  ByVal NumNode As Integer, ByVal NodesIDs As Variant, ByVal  Xyz As Variant) As Double()
    Dim centroid(2) As Double
    Dim count As Integer

    For i = 0 To 19
        If ElementNodes(i) <> 0 Then
            For j = 0 To NumNode-1
                If (NodesIDs(j) = ElementNodes(i)) Then
                    centroid(0) += Xyz(j*3)
                    centroid(1) += Xyz(j*3+1)
                    centroid(2) += Xyz(j*3 + 2)
                    count += 1
                End If
            Next
        End If
    Next

    centroid(0) /= count
    centroid(1) /= count
    centroid(2) /= count

    CalculateCentroid =  centroid
End Function

Function CalculateCentroidUsingFemapFunction(ByVal Element As femap.Elem, ByVal ElementId As Integer) As Double()
    Dim centroid(2) As Double
    Dim cgXyz As Variant
    Element.Get(ElementId)
    Element.GetCentroid(cgXyz)

    centroid(0) = cgXyz(0)
    centroid(1) = cgXyz(1)
    centroid(2) = cgXyz(2)

    CalculateCentroidUsingFemapFunction = cgXyz
End Function

 

I've create 2 methods: 1)CalculateCentroid - fast method. 2) CalculateCentroidUsingFemapFunction - slow method, call foreach element GetCentroid, it is for testing purpose. 

 

The results for the tool:

Method1. Element ID = 1 x = -0.08333335, y = 0.4, z = 0
MEthod2. Element ID = 1 x = -0.08333335, y = 0.4, z = 0
Method1. Element ID = 2 x = -0.08333335, y = 0.2, z = 0
MEthod2. Element ID = 2 x = -0.08333335, y = 0.2, z = 0
Method1. Element ID = 3 x = -0.08333335, y = 0, z = 0
MEthod2. Element ID = 3 x = -0.08333335, y = 0, z = 0
Method1. Element ID = 4 x = -0.08333335, y = -0.2, z = 0
MEthod2. Element ID = 4 x = -0.08333335, y = -0.2, z = 0

 

Let me know if you are happy with performance. Because there is one possible improvement but a bit tricky.

If you have question about the code don't hesitate to ask.

 

Yarko

Re: Get element centroid by array

Creator
Creator

Hi Yarko,

 

Thanks that you'vw wirten out the code already. I was underway writing down the concept myself.
however upon testing the code (I'm running it from excel VBA) I'm getting an overflow error message. Me and my colleague have investigated it, but without any solution. Smiley Mad

The error appears in this part of the code:

 

1    For i = 0 To numElem - 1
2        'copy elements nodes to separate array
3        For j = 0 To 19
4            ElementNodes(j) = Nodes(j + i * 20)
5        Next
6
7        'calculate centroid based on nodes coordinates. Quick Method.
8        Dim mycentroid2() As Double
9        mycentroid2 = CalculateCentroid(ElementNodes, NumNode, nodeID, Xyz)

Either at  the 4th line, or at the 9th line..

 

I'm without any clues on possibilities. Do you have any clue?

 

Thanks in advance. Casper

Re: Get element centroid by array

Legend
Legend

Hi Casper,

 

 

I've tested this script only in Femap on small model. 

I will try to run it on big model from Femap and also will check from Excel VBA.

 

When I will find something I will let you know.

 

Yarko

Re: Get element centroid by array

Legend
Legend

Hello Casper,

 

 

I've found where was the problem. In the following line:

Dim ElementNodes(19) As Integer

If replace on this, it works. 

Dim ElementNodes(19) As Long

But I've did a test on model 9000 elements and 6000 nodes. And it turns out that my method is slow. I suspect it can happen, because I'm looking for coordinates for each element in big array - 6000 nodes. 

 

When I run second method it turns to be very quick. It takes me about 1-2 seconds to get all centroids. It strange because I do 18000 calls to Femap. 

I've update my script and attached. Can you try to use it and check if you have same performance using second method?

 

My first required optimization to be quick. Let me know if second method is slow and I will try to optimize first method (that use coordinates). If you performance is ok for you we will leave it as it is.

 

Yarko

 

Re: Get element centroid by array

Creator
Creator

Reply to message 06-07-2016 09:55 AM

 

Hi Yarko,

 

I've located it myself already!

 

    Dim ElementNodes(19) As Long

1) Should be declared as Long instead of integer

 

Function CalculateCentroid(ByVal ElementNodes As Variant, ByVal NumNode As Long, ByVal NodesIDs As Variant, ByVal Xyz As Variant) As Double()

2) NumNode should be Long instead of integer

3) i and j weren't declared yet

 

The code runs slower due to the lookup list in the second function, with respect to my original one. However, I'm thinking of tuning it to list the nodes and elements in 2 arrays, by which the function can be calculated linear and hopefully faster.

 

Preliminary code now:

Option Explicit
Public Sub Main()

'Start Stopwatch timer; not actual used, only for optimizing code processing time
Dim sw As Stopwatch
Set sw = New Stopwatch
sw.StartTimer

    Dim App As Object:                  Set App = GetObject(, "femap.model")

    'Get all elements
    Dim Element As femap.Elem
    Set Element = App.feElem
    Dim feSet As femap.Set:             Set feSet = App.feSet
    Dim n As femap.Node
    Set n = App.feNode

    Dim i As Long
    Dim j As Long

    Dim NumNode As Long
    Dim nodeID As Variant
    Dim Xyz As Variant
    Dim ElementNodes(19) As Long
    Dim centroid() As Double
    
    Dim numElem As Long
    Dim entID As Variant
    Dim propID As Variant
    Dim elemType As Variant
    Dim topology As Variant
    Dim Lyr As Variant
    Dim color As Variant
    Dim formulation As Variant
    Dim orient As Variant
    Dim offset As Variant
    Dim release As Variant
    Dim orientSET As Variant
    Dim orientID As Variant
    Dim Nodes As Variant
    Dim connectTYPE As Variant
    Dim connectSEG As Variant
    
    Dim msg As VbMsgBoxResult
    Dim rc As zReturnCode
    
    rc = feSet.Select(FT_ELEM, True, "Select elements for sea pressure application")    'Select by user input in query box
    'rc = feSet.AddGroup(FT_ELEM, 41)    'Select with pre defined group number
    If rc = FE_CANCEL Or rc = FE_NOT_EXIST Then Exit Sub

    rc = Element.GetAllArray(feSet.ID, numElem, entID, propID, _
    elemType, topology, Lyr, color, formulation, orient, offset, _
    release, orientSET, orientID, Nodes, connectTYPE, connectSEG)

    'Get coordinates of all nodes
    rc = n.GetCoordArray(0, NumNode, nodeID, Xyz)

    'Loop through all elements and calculate centroid for each
    For i = 0 To numElem - 1
        'copy elements nodes to separate array
        For j = 0 To 19
            ElementNodes(j) = Nodes(j + i * 20)
        Next

        'calculate centroid based on nodes coordinates. Quick Method.
        centroid = CalculateCentroid(ElementNodes, NumNode, nodeID, Xyz)
        'printing a message will slow down the tool. Use only for testing
        'App.feAppMessage(FCM_NORMAL, "Method1. Element ID = " & entID(i) & "  x = " & centroid(0) & ", y = " & centroid(1) & ", z = " & centroid(2))

        'calculate centroid using Element.GetCentroid. For testing purpose
        'Dim centroid() As Double
        'centroid = CalculateCentroidUsingFemapFunction(Element, entID(i))
        'App.feAppMessage(FCM_NORMAL, "MEthod2. Element ID = " & entID(i) & "  x = " & centroid2(0) & ", y = " & centroid2(1) & ", z = " & centroid2(2))
    Next
    
    msg = MsgBox("Load application completed!" & vbNewLine & _
    "That took: " & Format(sw.EndTimer / 1000, "#,#0.0") & "seconds")
    Application.StatusBar = ""


End Sub

Function CalculateCentroid(ByVal ElementNodes As Variant, ByVal NumNode As Long, ByVal NodesIDs As Variant, ByVal Xyz As Variant) As Double()
    Dim mycentroid(2) As Double
    Dim count As Integer
    Dim i As Long
    Dim j As Long

    count = 0
    For i = 0 To 19
        If ElementNodes(i) <> 0 Then
            For j = 0 To NumNode - 1
                If (NodesIDs(j) = ElementNodes(i)) Then
                    mycentroid(0) = mycentroid(0) + Xyz(j * 3)
                    mycentroid(1) = mycentroid(1) + Xyz(j * 3 + 1)
                    mycentroid(2) = mycentroid(2) + Xyz(j * 3 + 2)
                    count = count + 1
                End If
            Next
        End If
    Next

    mycentroid(0) = mycentroid(0) / count
    mycentroid(1) = mycentroid(1) / count
    mycentroid(2) = mycentroid(2) / count

    CalculateCentroid = mycentroid
End Function

 

 

 

Re: Get element centroid by array

Creator
Creator

Hi Yarko,

 

Yes, I've tested it on a selection with 15.000 elements / 16.000 nodes

Whole model contains: 96.000 elements / 68.500 nodes

 

Speed on testing the selection of elements & nodes:

Method 2: 17,0 seconds

Method 1: not tested

Original method: 16,7 seconds (similar functions as your method 2, but code written different)

 

I would be delighted to have a optimized method 1, since I'll have to perform the operation multiple times..

 

Thanks in advance!

Casper

Re: Get element centroid by array

Legend
Legend

Dear Casper,

 

 

Today in the evening I will do some optimization then for method 1.

Short question: you say you will run this method few times. Maybe it is possible to call it once and put into some matrix and use later from this matrix?

 

Yarko