joneseyeng,
DMoreno's comments are correct and may be helpful. In addition, I would like to offer you an API that I created to automatically make bolt elements by selecting all the curves in an area of the model. It looks for "loops" which make up holes, so if you pick some extra curves, it shouldn't matter. This version actually asks if you want to use an existing Beam property, CBUSH property (If so, creates coincident node CBUSH elements), or enter a bolt radius. If you enter a bolt radius, all elements created will use the same Property.
Please post any questions, comments or concerns about this API on this thread and I will do my best to answer them. In addition, if you already have "hundreds" of API fastener properties, I could also write a "property consolidation" API for the fastener elements which may be helpful. Please let me know.
Thanks,
Andy - FEMAP development
Code starts here:
Sub Main
Dim App As femap.model
Set App = feFemap()
Dim Cu As femap.Curve
Set Cu = App.feCurve
Dim cuSet As femap.Set
Set cuSet = App.feSet
Dim CircSet As femap.Set
Set CircSet = App.feSet
Dim CircSubSet As femap.Set
Set CircSubSet = App.feSet
Dim FinSet As femap.Set
Set FinSet = App.feSet
Dim PointSet As femap.Set
Set PointSet = App.feSet
Dim IndNdSet As femap.Set
Set IndNdSet = App.feSet
Dim feNode As femap.Node
Set feNode = App.feNode
Dim nodeSet As femap.Set
Set nodeSet = App.feSet
Dim feElem As femap.Elem
Set feElem = App.feElem
Dim DeleteSet As femap.Set
Set DeleteSet = App.feSet
Dim El As femap.Elem
Set El = App.feElem
Dim P As femap.Prop
Set P = App.feProp
Dim pset As femap.Set
Set pset = App.feSet
Dim listtype(0) As Long
Dim entityID(0) As Long
Dim FaceID(0) As Long
Dim Expanded(0) As Long
Dim BoReg(0) As Long
Dim BoVal(0) As Double
Dim Preload As Double
Dim BeamElID As Long
Dim PropID As Long
Dim BeamID As Long
Dim RAD As Double
Dim cuCenter As Variant
Dim cuNormal As Variant
Dim cuStartPt As Variant
Dim cuEndPt As Variant
Dim cuAngle As Double
Dim cuRadius As Double
Dim nd1loc(2) As Double
Dim nd2loc(2) As Double
Dim dist As Double
Dim DistCurrent As Double
Dim pt1 As Long
Dim pt2 As Long
Dim nodeCount As Long
Dim nodeX As Double
Dim nodeY As Double
Dim nodeZ As Double
Dim nodeID As Long
Dim elemID As Long
Dim vNodeArray As Variant
Dim BeamArea As Double
Dim Vec(3) As Variant
Dim X As Double
Dim Y As Double
Dim Z As Double
Dim X1 As Double
Dim Y1 As Double
Dim Z1 As Double
Dim VecP As Variant
Dim feMatl As femap.Matl
Set feMatl = App.feMatl
Dim matID As Long
rc = App.feAppMessageBox (3, "Select Existing Beam or Spring-CBUSH Property (No=Enter Bolt Radius)")
If rc=2 Then
GoTo Done
End If
If rc=0 Then
matID = App.Info_ActiveID( FT_MATL )
If matID = 0 Then
Msg = "No active material for fastener, please activate a material and re-run."
rc = MsgBox( Msg, vbOkOnly, "No Material" )
GoTo Done
End If
rc = feMatl.Get( matID )
'Section Values A
Radagain:
rc = App.feGetReal ("Enter Bolt Radius", 0.000000001, 1000000, RAD)
If rc = 2 Then
GoTo Done
End If
PropID = P.NextEmptyID
P.title = "API Fastener"
P.type = FET_L_BEAM
P.flagI(1) = 5
P.pval (40) = RAD
P.ComputeShape (False, False, True)
P.matlID = matID
P.Put(PropID)
Proptype = P.type
Else
again:
rc = pset.SelectID (FT_PROP, "Select Beam Property for Bolt", PropID)
If rc = 2 Then
GoTo Done
End If
P.Get (PropID)
proptype = P.type
If proptype <> 5 Then
If proptype = 6 Then
isBush = P.flag(3)
If isBush = True Then
refCSYS = P.flag(0)
If refCSYS = True Then
GoTo MoveOn
Else
rc = App.feAppMessageBox (1, "Selected Spring Property does not have Orientation CSys defined. Select Different Property?")
If rc = 2 Then
GoTo Done
Else
GoTo Again
End If
End If
Else
rc = App.feAppMessageBox (1, "Selected Spring Property is not set to CBUSH. Select Different Property?")
If rc = 2 Then
GoTo Done
Else
GoTo Again
End If
End If
Else
rc = App.feAppMessageBox (1, "Selected Property is not a Beam or Spring Property. Select Different Property?")
If rc = 2 Then
GoTo Done
Else
GoTo Again
End If
End If
End If
End If
MoveOn:
cuSet.Select (FT_CURVE, True, "Select Curves for Bolts")
cuID = cuSet.First
While cuID > 0
Cu.Get(cuID)
rc = Cu.ArcCircleInfo( cuCenter, cuNormal, cuStartPt, cuEndPt, cuAngle, cuRadius )
If rc = FE_OK Then
CircSet.Add (cuID)
End If
cuID = cuSet.Next
Wend
CircID = CircSet.First
While CircID > 0
Cu.Get(CircID)
Cu.EndPoints (pt1, pt2)
CircSubSet.AddRule (pt1, FGD_CURVE_BYPOINT)
CircSubSet.AddRule (pt2, FGD_CURVE_BYPOINT)
CircSubID = CircSubSet.First
While CircSubID > 0
Cu.Get(CircSubID)
Cu.EndPoints (pt1, pt2)
CircSubSet.AddRule (pt1, FGD_CURVE_BYPOINT)
CircSubSet.AddRule (pt2, FGD_CURVE_BYPOINT)
CircSubID = CircSubSet.Next
Wend
FinSet.AddCommon (CircSet.ID, CircSubSet.ID)
CuNum = FinSet.Count
PointSet.AddSetRule (FinSet.ID, FGD_POINT_ONCURVE)
PtNum = PointSet.Count
If CuNum <> PtNum Then
GoTo Skip
End If
' Get a set of nodes for this rigid element
rc = nodeSet.AddSetRule( FinSet.ID, FGD_NODE_ATCURVE )
If nodeSet.Count = 0 Then
App.feAppMessageBox (0, "No nodes exist on a selected curve(s). Exiting...")
GoTo Jumping_Out
End If
'Lets see how many nodes were selected
nodeCount = nodeSet.Count()
If nodeCount > 0 Then
' Walk the nodes and find the average
rc = nodeSet.Reset()
nodeID = nodeSet.Next()
ReDim nodeArray(nodeCount) As Long
Dim passCount As Long
passCount = 0
Do While nodeID <> 0
nodeArray(passCount) = nodeID
passCount = passCount + 1
rc = feNode.Get(nodeID)
nodeX = nodeX + feNode.x
nodeY = nodeY + feNode.y
nodeZ = nodeZ + feNode.z
nodeID = nodeSet.Next()
Loop
vNodeArray = nodeArray
nodeID = feNode.NextEmptyID
feNode.ID = nodeID
feNode.x = nodeX / nodeCount
feNode.y = nodeY / nodeCount
feNode.z = nodeZ / nodeCount
rc = feNode.Put(nodeID)
IndNdSet.Add (nodeID)
If rc = -1 Then 'return code FE_OK
' create the element
elemID = feElem.NextEmptyID
feElem.type = FET_L_RIGID
feElem.topology = FTO_RIGIDLIST
feElem.Node(0) = nodeID 'Independent Node
feElem.release(0, 0) = 1
feElem.release(0, 1) = 1
feElem.release(0, 2) = 1
feElem.ID = elemID
rc = feElem.PutNodeList(0, nodeCount, vNodeArray, Null, Null, Null)
rc = feElem.Put(elemID)
nodeCount = 0
rc = nodeSet.Clear()
nodeX = 0#
nodeY = 0#
nodeZ = 0#
End If
End If
Skip:
PointSet.Clear
CircSet.RemoveSet (FinSet.ID)
FinSet.Clear
CircSubSet.Clear
CircID = CircSet.Next
Wend
If IndNdSet.Count = 1 Then
Nd1 = IndNdSet.First
DeleteSet.AddRule (nd1, FGD_ELEM_BYNODE)
feElem.Delete (DeleteSet.First)
feNode.Delete (Nd1)
GoTo Jumping_Out
End If
nd1 = IndNdSet.First
While nd1 > 0
feNode.Get(nd1)
nd1loc(0) = feNode.x
nd1loc(1) = feNode.y
nd1loc(2) = feNode.z
DistCurrent = 1000000.0
nd2 = IndNdSet.Next
IndNdSet.Remove (nd1)
While nd2 > 0
feNode.Get(nd2)
nd2loc(0) = feNode.x
nd2loc(1) = feNode.y
nd2loc(2) = feNode.z
App.feMeasureDistance (nd1loc, nd2loc, dist)
If DistCurrent > dist Then
DistCurrent = dist
nd2Keep = nd2
End If
nd2 = IndNdSet.Next
Wend
IndNdSet.Remove (nd2keep)
IndNdSet.Reset
BeamElID = El.NextEmptyID
If Proptype = 5 Then
El.type = FET_L_BEAM
Else
El.type = FET_L_SPRING
End If
El.Node (0) = nd1
El.Node (1) = nd2Keep
rc = feNode.Get (nd1)
X = feNode.x
Y = feNode.y
Z = feNode.z
rc = feNode.Get (nd2Keep)
X1 = feNode.x
Y1 = feNode.y
Z1 = feNode.z
Vec(0) = X - X1
Vec(1) = Y - Y1
Vec(2) = Z - Z1
rc = App.feVectorPerpendicular (Vec, VecP)
El.orient(0) = VecP(0)
El.orient(1) = VecP(1)
El.orient(2) = VecP(2)
El.propID = PropID
El.Put (BeamElID)
If proptype = 6 Then
newx = (X + X1) / 2
newy = (Y + Y1) / 2
newz = (Z + Z1) / 2
feNode.x = newx
feNode.y = newy
feNode.z = newz
rc = feNode.Put(nd1)
rc = feNode.Put(nd2Keep)
End If
If IndNdSet.Count = 1 Then
Nd1 = IndNdSet.First
DeleteSet.AddRule (nd1, FGD_ELEM_BYNODE)
feElem.Delete (DeleteSet.First)
feNode.Delete (Nd1)
GoTo Jumping_Out
End If
nd1 = IndNdSet.First
Wend
Jumping_Out:
Call App.feViewRegenerate(0)
Done:
End Sub