generalContactFrictionTests.py

You can view and download this file on Github: generalContactFrictionTests.py

  1#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2# This is an EXUDYN example
  3#
  4# Details:  test friction of spheres and spheres-trigs
  5#
  6# Author:   Johannes Gerstmayr
  7# Date:     2021-12-06
  8#
  9# Copyright:This file is part of Exudyn. Exudyn is free software. You can redistribute it and/or modify it under the terms of the Exudyn license. See 'LICENSE.txt' for more details.
 10#
 11#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 12
 13import exudyn as exu
 14from exudyn.utilities import * #includes itemInterface and rigidBodyUtilities
 15import exudyn.graphics as graphics #only import if it does not conflict
 16
 17import numpy as np
 18
 19useGraphics = True #without test
 20#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 21#you can erase the following lines and all exudynTestGlobals related operations if this is not intended to be used as TestModel:
 22try: #only if called from test suite
 23    from modelUnitTests import exudynTestGlobals #for globally storing test results
 24    useGraphics = exudynTestGlobals.useGraphics
 25except:
 26    class ExudynTestGlobals:
 27        pass
 28    exudynTestGlobals = ExudynTestGlobals()
 29#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 30useGraphics = False
 31
 32SC = exu.SystemContainer()
 33mbs = SC.AddSystem()
 34
 35nGround = mbs.AddNode(NodePointGround(referenceCoordinates=[0,0,0]))
 36
 37#isPerformanceTest = exudynTestGlobals.isPerformanceTest
 38#useGraphics = False
 39# isPerformanceTest = True
 40# tEnd = 0.1
 41# if isPerformanceTest: tEnd *= 0.5
 42
 43#%%+++++++++++++++++++++++++++++++++
 44#sphere-sphere with coordinate constraints, prestressed; fixed torque on one side, linear increasing torque on other side
 45#sphere on ground, rolling
 46#cube on ground, sliding (f=[f, f*mu*t, 0]), tangential force changing
 47#cube on ground with initial velocity
 48#cube-cube contact (meshed)
 49
 50
 51L = 1 #surrounding
 52a = 0.1 #base dimention of objects
 53r = 0.5*a #radius
 54t = 0.25*a #thickness
 55
 56#contact coefficients:
 57mu = 0.8      #dry friction
 58m = 0.025     #mass
 59k = 1e3       #(linear) normal contact stiffness
 60d = 2*1e-4*k  #(linear) contact damping
 61gFact = 10
 62g = [0,0,-gFact]
 63
 64gContact = mbs.AddGeneralContact()
 65gContact.verboseMode = 1
 66#gContact.sphereSphereContact = False
 67gContact.frictionProportionalZone = 1e-3
 68#gContact.excludeDuplicatedTrigSphereContactPoints = False
 69fricMat = mu*np.eye(2)
 70fricMat[0,1] = 0.2
 71fricMat[1,0] = 0.2
 72gContact.SetFrictionPairings(fricMat)
 73gContact.SetSearchTreeCellSize(numberOfCells=[4,4,4])
 74
 75#%% ground
 76p0 = np.array([0,0,-0.5*t])
 77color4wall = [0.9,0.9,0.7,0.5]
 78addNormals = False
 79gFloor = graphics.Brick(p0,[L,L,t],graphics.color.steelblue,addNormals)
 80gFloorAdd = graphics.Brick(p0+[-0.5*L,0,a],[t,L,2*a],color4wall,addNormals)
 81gFloor = graphics.MergeTriangleLists(gFloor, gFloorAdd)
 82gFloorAdd = graphics.Brick(p0+[ 0.5*L,0,a],[t,L,2*a],color4wall,addNormals)
 83gFloor = graphics.MergeTriangleLists(gFloor, gFloorAdd)
 84gFloorAdd = graphics.Brick(p0+[0,-0.5*L,a],[L,t,2*a],color4wall,addNormals)
 85gFloor = graphics.MergeTriangleLists(gFloor, gFloorAdd)
 86gFloorAdd = graphics.Brick(p0+[0, 0.5*L,a],[L,t,2*a],color4wall,addNormals)
 87gFloor = graphics.MergeTriangleLists(gFloor, gFloorAdd)
 88
 89bb = 0.75*a
 90bh = 0.25*a
 91p1 = np.array([0.5*L,0.5*L,0])
 92gFloorAdd = graphics.Brick(p1+[-bb*3, -bb, 0.5*bh],[bb,bb,bh],color4wall,addNormals)
 93gFloor = graphics.MergeTriangleLists(gFloor, gFloorAdd)
 94gFloorAdd = graphics.Brick(p1+[-bb*2, -bb, 1.5*bh],[bb,bb,bh],color4wall,addNormals)
 95gFloor = graphics.MergeTriangleLists(gFloor, gFloorAdd)
 96gFloorAdd = graphics.Brick(p1+[-bb*1, -bb, 2.5*bh],[bb,bb,bh],color4wall,addNormals)
 97gFloor = graphics.MergeTriangleLists(gFloor, gFloorAdd)
 98
 99gDataList = [gFloor]
100
101
102nGround = mbs.AddNode(NodePointGround(referenceCoordinates=[0,0,0] ))
103mGround = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nGround))
104mGroundC = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=nGround, coordinate=0))
105
106[meshPoints, meshTrigs] = graphics.ToPointsAndTrigs(gFloor)
107#[meshPoints, meshTrigs] = RefineMesh(meshPoints, meshTrigs) #just to have more triangles on floor
108# [meshPoints, meshTrigs] = RefineMesh(meshPoints, meshTrigs) #just to have more triangles on floor
109gContact.AddTrianglesRigidBodyBased(rigidBodyMarkerIndex=mGround, contactStiffness=k, contactDamping=d, frictionMaterialIndex=0,
110    pointList=meshPoints,  triangleList=meshTrigs)
111
112if True: #looses color
113    gFloor = graphics.FromPointsAndTrigs(meshPoints, meshTrigs, color=color4wall) #show refined mesh
114    gDataList = [gFloor]
115
116evalNodes = [] #collect nodes that are evaluated for test
117#%%++++++++++++++++++++++++++++++++++++++++++++
118#free rolling sphere:
119gList = [graphics.Sphere(point=[0,0,0], radius=r, color= graphics.color.red, nTiles=24)]
120omega0 = -4.*np.array([5,1.,0.])
121pRef = [-0.4*L,-0.4*L,r-0*m*gFact/k]
122RBinertia = InertiaSphere(m, r)
123dictMass = mbs.CreateRigidBody(
124                      inertia=RBinertia,
125                      nodeType=exu.NodeType.RotationRotationVector,
126                      referencePosition=pRef,
127                      initialVelocity=-np.cross([0,0,r], omega0),
128                      referenceRotationMatrix=RotationMatrixX(0.),
129                      initialAngularVelocity=omega0,
130                      # gravity=g,
131                      graphicsDataList=gList,
132                      returnDict=True)
133[nMass, oMass] = [dictMass['nodeNumber'], dictMass['bodyNumber']]
134
135
136nNode0 = nMass
137mNode = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nMass))
138mbs.AddLoad(Force(markerNumber=mNode, loadVector= [0,0,-k*r*0.01])) #==> uz = 2*r*0.01
139exu.Print('expect u0z=',2*r*0.01)
140gContact.AddSphereWithMarker(mNode, radius=r, contactStiffness=k, contactDamping=d, frictionMaterialIndex=0)
141if useGraphics:
142    sNode0 = mbs.AddSensor(SensorNode(nodeNumber=nNode0, storeInternal=True, #fileName='solution/contactNode0.txt',
143                                      outputVariableType=exu.OutputVariableType.Displacement))
144    vNode0 = mbs.AddSensor(SensorNode(nodeNumber=nNode0, storeInternal=True, #fileName='solution/contactNode0Vel.txt',
145                                      outputVariableType=exu.OutputVariableType.Velocity))
146evalNodes += [nMass]
147
148#%%++++++++++++++++++++++++++++++++++++++++++++
149#free rolling sphere at midpoint, many triangles in close contact; slowly go through critical points:
150gList = [graphics.Sphere(point=[0,0,0], radius=r, color= graphics.color.yellow, nTiles=24)]
151omega0 = -1e-12*np.array([1,0.1,0.])
152pRef = [1e-15,-1e-14,r-2*m*gFact/k]
153RBinertia = InertiaSphere(m, r)
154dictMass = mbs.CreateRigidBody(
155                      inertia=RBinertia,
156                      nodeType=exu.NodeType.RotationRotationVector,
157                      referencePosition=pRef,
158                      initialVelocity=-np.cross([0,0,r], omega0),
159                      referenceRotationMatrix=RotationMatrixX(0.),
160                      initialAngularVelocity=omega0,
161                      gravity=g,
162                      graphicsDataList=gList,
163                      returnDict=True)
164[nMass, oMass] = [dictMass['nodeNumber'], dictMass['bodyNumber']]
165
166nNode1 = nMass
167mNode1 = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nMass))
168#mbs.AddLoad(Force(markerNumber=mNode1, loadVector= [0,0,-k*r*0.01])) #==> uz = 2*r*0.01
169#exu.Print('expect u0z=',2*r*0.01)
170gContact.AddSphereWithMarker(mNode1, radius=r, contactStiffness=k, contactDamping=d, frictionMaterialIndex=0)
171
172sNode1 = mbs.AddSensor(SensorNode(nodeNumber=nNode1, storeInternal=True, #fileName='solution/contactNode1.txt',
173                                  outputVariableType=exu.OutputVariableType.Displacement))
174# vNode1 = mbs.AddSensor(SensorNode(nodeNumber=nNode0, storeInternal=True, #fileName='solution/contactNode0Vel.txt',
175#                                   outputVariableType=exu.OutputVariableType.Velocity))
176
177
178#%%++++++++++++++++++++++++++++++++++++++++++++
179#fixed pressure tests:
180pf = np.array([-1.2*L,0,0])
181nGroundF = mbs.AddNode(NodePointGround(referenceCoordinates=pf ))
182mNode = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nGroundF))
183gContact.AddSphereWithMarker(mNode, radius=r, contactStiffness=k, contactDamping=d, frictionMaterialIndex=0)
184gDataList += [graphics.Sphere(point=pf, radius=r, color= graphics.color.grey, nTiles=24)]
185
186gList = [graphics.Sphere(point=[0,0,0], radius=r, color= graphics.color.lightgreen, nTiles=24)]
187
188pRef = pf+[0,2*r,0] #[-0.4*L,-0.4*L,r-m*gFact/k]
189RBinertia = InertiaSphere(m, r)
190dictF = mbs.CreateRigidBody(
191                      inertia=RBinertia,
192                      nodeType=exu.NodeType.RotationRotationVector,
193                      referencePosition=pRef,
194                      referenceRotationMatrix=RotationMatrixX(0.),
195                      graphicsDataList=gList,
196                      returnDict=True)
197[nMassF, oMassF] = [dictF['nodeNumber'], dictF['bodyNumber']]
198
199mC = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=nMassF, coordinate=0))
200mbs.AddObject(CoordinateConstraint(markerNumbers=[mGroundC, mC]))
201
202mNodeF = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nMassF))
203mbs.AddLoad(Force(markerNumber=mNodeF, loadVector= [0,-k*r*0.1,0])) #==> u =  k*r*0.1/(0.5*k) = 2*r*0.1
204exu.Print('expect uFy=',2*r*0.1)
205gContact.AddSphereWithMarker(mNodeF, radius=r, contactStiffness=k, contactDamping=d, frictionMaterialIndex=0)
206if useGraphics:
207    sNodeF = mbs.AddSensor(SensorNode(nodeNumber=nMassF, storeInternal=True, #fileName='solution/contactNodeF.txt',
208                                      outputVariableType=exu.OutputVariableType.Displacement))
209evalNodes += [nMassF]
210
211#%%++++++++++++++++++++++++++++++++++++++++++++
212# sliding between spheres:
213pr = np.array([-1.2*L,0.5*L,0])
214nGroundF2 = mbs.AddNode(NodePointGround(referenceCoordinates=pr ))
215mNode2 = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nGroundF2))
216gContact.AddSphereWithMarker(mNode2, radius=r, contactStiffness=k, contactDamping=d, frictionMaterialIndex=0)
217gDataList += [graphics.Sphere(point=pr, radius=r, color= graphics.color.lightgrey, nTiles=24)]
218
219gList = [graphics.Sphere(point=[0,0,0], radius=r, color= graphics.color.lightred, nTiles=24)]
220
221dRol = r*0.01
222pRef = pr+[0,2*r-2*dRol,0] #force=k*r*0.01
223fRol = k*dRol
224exu.Print('force rolling=', fRol, ', torque=', fRol*mu*r)
225RBinertia = InertiaSphere(m, r)
226dictR = mbs.CreateRigidBody(
227                      inertia=RBinertia,
228                      nodeType=exu.NodeType.RotationRotationVector,
229                      referencePosition=pRef,
230                      referenceRotationMatrix=RotationMatrixX(0.),
231                      graphicsDataList=gList,
232                      returnDict=True)
233[nMassR, oMassR] = [dictR['nodeNumber'], dictR['bodyNumber']]
234
235
236
237
238mC = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=nMassR, coordinate=0))
239mbs.AddObject(CoordinateConstraint(markerNumbers=[mGroundC, mC]))
240mC = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=nMassR, coordinate=1))
241mbs.AddObject(CoordinateConstraint(markerNumbers=[mGroundC, mC]))
242mC = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=nMassR, coordinate=2))
243mbs.AddObject(CoordinateConstraint(markerNumbers=[mGroundC, mC]))
244
245mNodeR = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nMassR))
246
247def UFtorque(mbs, t, loadVector):
248    torque = 10*t*fRol*mu*r
249    if t > 0.3:
250        torque = 0
251    return [torque,0,0]
252mbs.AddLoad(Torque(markerNumber=mNodeR, loadVectorUserFunction=UFtorque,
253                   loadVector= [1,0,0])) #==> u =  k*r*0.1/(0.5*k) = 2*r*0.1
254
255gContact.AddSphereWithMarker(mNodeR, radius=r, contactStiffness=k, contactDamping=d, frictionMaterialIndex=0)
256if useGraphics:
257    sNodeR = mbs.AddSensor(SensorNode(nodeNumber=nMassR, storeInternal=True, #fileName='solution/contactNodeR.txt',
258                                      outputVariableType=exu.OutputVariableType.Rotation))
259    vNodeR = mbs.AddSensor(SensorNode(nodeNumber=nMassR, storeInternal=True, #fileName='solution/contactNodeRvel.txt',
260                                      outputVariableType=exu.OutputVariableType.AngularVelocity))
261
262evalNodes += [nMassR]
263
264#%%++++++++++++++++++++++++++++++++++++++++++++
265#sphere on stairs
266#%%++++++++++++++++++++++++++++++++++++++++++++
267#free rolling sphere at midpoint, many triangles in close contact; slowly go through critical points:
268gList = [graphics.Sphere(point=[0,0,0], radius=0.5*r, color= graphics.color.yellow, nTiles=24)]
269omega0 = np.array([-0.05,-5,0.])
270pRef = [0.5*L-1.45*bb, 0.5*L-1.20*bb, 3*bh+0.5*r-2*m*gFact/k] #[0.5*L-1.45*bb, 0.5*L-1.40*bb, ..] goes to edge
271RBinertia = InertiaSphere(m, 0.5*r)
272dictStair = mbs.CreateRigidBody(
273              inertia=RBinertia,
274              nodeType=exu.NodeType.RotationRotationVector,
275              referencePosition=pRef,
276              initialVelocity=-np.cross([0,0,0.5*r], omega0),
277              referenceRotationMatrix=RotationMatrixX(0.),
278              initialAngularVelocity=omega0,
279              gravity=g,
280              graphicsDataList=gList,
281              returnDict=True)
282[nMassStair, oMassStair] = [dictStair['nodeNumber'], dictStair['bodyNumber']]
283
284nNode3 = nMassStair
285mNode3 = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nMassStair))
286gContact.AddSphereWithMarker(mNode3, radius=0.5*r, contactStiffness=k, contactDamping=20*d, frictionMaterialIndex=0)
287
288if useGraphics:
289    sNode3 = mbs.AddSensor(SensorNode(nodeNumber=nNode3, storeInternal=True, #fileName='solution/contactNode3.txt',
290                                      outputVariableType=exu.OutputVariableType.Displacement))
291evalNodes += [nMassStair]
292
293
294#%%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
295#contact of cube with ground
296tTrig = 0.25*r #size of contact points on mesh ('thickness')
297gCube = graphics.Brick(size=[3*r,2*r,r], color= graphics.color.steelblue,addNormals=addNormals)
298[meshPoints, meshTrigs] = graphics.ToPointsAndTrigs(gCube)
299
300#for tests, 1 refinement!
301[meshPoints, meshTrigs] = RefineMesh(meshPoints, meshTrigs) #just to have more triangles on floor
302# exu.Print("n points=",len(meshPoints))
303# [meshPoints, meshTrigs] = RefineMesh(meshPoints, meshTrigs) #just to have more triangles on floor
304# exu.Print("==> n points refined=",len(meshPoints))
305# refinements give 26,98,386 points!
306
307[meshPoints2, meshTrigs2] = ShrinkMeshNormalToSurface(meshPoints, meshTrigs, tTrig)
308
309#add mesh to visualization
310gCube = graphics.FromPointsAndTrigs(meshPoints, meshTrigs, color=graphics.color.steelblue) #show refined mesh
311gList = [gCube]
312
313#add points for contact to visualization (shrinked)
314for p in meshPoints2:
315    gList += [graphics.Sphere(point=p, radius=tTrig, color=graphics.color.red)]
316
317pRef = [0.5*L-2*r, 0.25*L, 0.5*r+1.5*tTrig]
318v0 = np.array([-2,0,0])
319RBinertia = InertiaCuboid(density=m/(r*2*r*3*r), sideLengths=[3*r,2*r,r])
320dictCube0 = mbs.CreateRigidBody(
321              inertia=RBinertia,
322              nodeType=exu.NodeType.RotationRotationVector,
323              referencePosition=pRef,
324              initialVelocity=v0,
325              initialAngularVelocity=[0,0,0],
326              gravity=g,
327              graphicsDataList=gList,
328              returnDict=True)
329[nMassCube0, oMassCube0] = [dictCube0['nodeNumber'], dictCube0['bodyNumber']]
330
331nCube0 = nMassCube0
332mCube0 = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nMassCube0))
333
334
335gContact.AddTrianglesRigidBodyBased(rigidBodyMarkerIndex=mCube0, contactStiffness=k, contactDamping=d, frictionMaterialIndex=1,
336    pointList=meshPoints,  triangleList=meshTrigs)
337
338for p in meshPoints2:
339    mPoint = mbs.AddMarker(MarkerBodyRigid(bodyNumber=oMassCube0, localPosition=p))
340    gContact.AddSphereWithMarker(mPoint, radius=tTrig, contactStiffness=k, contactDamping=d, frictionMaterialIndex=1)
341
342if useGraphics:
343    sCube0 = mbs.AddSensor(SensorNode(nodeNumber=nCube0, storeInternal=True, #fileName='solution/contactCube0.txt',
344                                      outputVariableType=exu.OutputVariableType.Displacement))
345
346evalNodes += [nMassCube0]
347
348
349#%%++++++++++++++++++++++++++++++++++++++++++++
350
351#add as last because of transparency
352oGround = mbs.AddObject(ObjectGround(visualization=VObjectGround(graphicsData=gDataList)))
353
354#%%+++++++++++++++++++++++++++++++++
355mbs.Assemble()
356
357tEnd = 0.8 #tEnd = 0.8 for test suite
358h= 0.0002  #h= 0.0002 for test suite
359# h*=0.1
360# tEnd*=3
361simulationSettings = exu.SimulationSettings()
362#simulationSettings.linearSolverType = exu.LinearSolverType.EigenSparse
363simulationSettings.solutionSettings.writeSolutionToFile = False
364if useGraphics:
365    simulationSettings.solutionSettings.solutionWritePeriod = 0.001
366    simulationSettings.solutionSettings.writeSolutionToFile = True
367    simulationSettings.solutionSettings.coordinatesSolutionFileName = 'solution/coordinatesSolution.txt'
368else:
369    simulationSettings.solutionSettings.exportAccelerations = False
370    simulationSettings.solutionSettings.exportVelocities = False
371
372simulationSettings.solutionSettings.sensorsWritePeriod = h*10
373simulationSettings.solutionSettings.outputPrecision = 8 #make files smaller
374simulationSettings.timeIntegration.verboseMode = 1
375
376simulationSettings.timeIntegration.newton.numericalDifferentiation.forODE2 = False
377simulationSettings.timeIntegration.newton.useModifiedNewton = False
378
379SC.visualizationSettings.general.graphicsUpdateInterval=0.05
380# SC.visualizationSettings.general.drawWorldBasis = True
381SC.visualizationSettings.general.circleTiling=200
382SC.visualizationSettings.general.drawCoordinateSystem=True
383SC.visualizationSettings.loads.show=False
384SC.visualizationSettings.bodies.show=True
385SC.visualizationSettings.markers.show=False
386
387SC.visualizationSettings.nodes.show=True
388SC.visualizationSettings.nodes.showBasis =True
389SC.visualizationSettings.nodes.drawNodesAsPoint = False
390SC.visualizationSettings.nodes.defaultSize = 0 #must not be -1, otherwise uses autocomputed size
391SC.visualizationSettings.nodes.tiling = 4
392SC.visualizationSettings.openGL.drawFaceNormals = False
393
394SC.visualizationSettings.openGL.multiSampling = 4
395SC.visualizationSettings.openGL.shadow = 0.25
396SC.visualizationSettings.openGL.light0position = [-3,3,10,0]
397
398if useGraphics:
399    SC.visualizationSettings.general.autoFitScene = False
400    exu.StartRenderer()
401    if 'renderState' in exu.sys:
402        SC.SetRenderState(exu.sys['renderState'])
403    mbs.WaitForUserToContinue()
404
405simulationSettings.timeIntegration.numberOfSteps = int(tEnd/h)
406simulationSettings.timeIntegration.endTime = tEnd
407simulationSettings.timeIntegration.explicitIntegration.computeEndOfStepAccelerations = False #increase performance, accelerations less accurate
408mbs.SolveDynamic(simulationSettings, solverType=exu.DynamicSolverType.ExplicitEuler)
409# mbs.SolveDynamic(simulationSettings, solverType=exu.DynamicSolverType.ODE23)
410
411#compute error:
412uSum=0
413for node in evalNodes:
414    u = mbs.GetNodeOutput(node, exu.OutputVariableType.Coordinates)
415    exu.Print('coords node'+str(node)+' =',u)
416    for c in u:
417        uSum += abs(c) #add up all coordinates for comparison
418
419
420exu.Print('solution of generalContactFrictionTest=',uSum)
421exudynTestGlobals.testError = uSum - (10.132106712933348 )
422
423exudynTestGlobals.testResult = uSum
424
425
426if useGraphics:
427    SC.WaitForRenderEngineStopFlag()
428
429    if True:
430        SC.visualizationSettings.general.autoFitScene = False
431        SC.visualizationSettings.general.graphicsUpdateInterval=0.02
432
433        sol = LoadSolutionFile('solution/coordinatesSolution.txt', safeMode=True)#, maxRows=100)
434        print('start SolutionViewer')
435        mbs.SolutionViewer(sol)
436
437    exu.StopRenderer() #safely close rendering window!
438
439if useGraphics:
440
441
442    mbs.PlotSensor([], closeAll=True)
443    mbs.PlotSensor([sNode3]*3, [0,1,2], figureName='node stair')