Reading time: 6 min
If you’re into heterogeneous modelling then you might’ve come across constructing a 3D polygon with random vertices. In my context, constructing such a model was to investigate mesoscale effects in a concrete material (made up of coarse and fine aggregates). Regardless, this methodology of using Abaqus and Python to construct a polyhedra is applicable cross-discipline.
To get started, you will need a set of polygon vertex coordinates and its convex hull, which you can download here.
For a bit of background on my workflow, I’ve generated random vertices for the polyhedra and determined the convex hull using Matlab (convhull function).
In this generation script, I’m reading from the csv file, which constitutes to one polyhedra dataset, then using Abaqus methods which can be determined from recording macro to programmatically create the geometry.
from abaqus import * from abaqusConstants import * import numpy as np import os import __main__ import section import regionToolset import displayGroupMdbToolset as dgm import part import material import assembly import step import interaction import load import mesh import optimization import job import sketch import visualization import xyPlot import displayGroupOdbToolset as dgo import connectorBehavior csvPath = r"/path" #path storing the polygon data os.chdir(csvPath) data = ; convexHull = ; data = np.genfromtxt('polygon1.csv', delimiter=',') convexHull = np.genfromtxt('polygon1-hull.csv', delimiter=',')
Once we have imported the Abaqus modules and loaded the polygon data in, we can now start drawing the 3D polygon. The general procedure is as follows:
Now, let’s have a look at the python code to script this.
# create coordinate reference and supress p = mdb.models['Model-1'].Part(name='Part-1', dimensionality=THREE_D, type=DEFORMABLE_BODY) p.ReferencePoint(point=(0.0, 0.0, 0.0)) p.features['RP'].suppress() for item in data: p.DatumPointByCoordinate(coords=(item, item, item)) d1 = p.datums;
In the code above, we created a part with the default name “Part-1”, then created a reference point to initalise a 3D space to sketch in. After that, using DatumPointByCoordinate which is a method which creates datum points, we create all the vertices from the variable data by looping through it.
Next part of the code is to loop through the newly created datum points and join them up using wire lines. To determine which datum points to join in order, we need to refer to the convex hull. The convexHull data is already assigned in the first block of code.
# join datum for edges for hull in convexHull: p.WirePolyLine(points=((d1[int(hull)+1], d1[int(hull)+1]),), mergeType=IMPRINT, meshable=ON) p.WirePolyLine(points=((d1[int(hull)+1], d1[int(hull)+1]),), mergeType=IMPRINT, meshable=ON) p.WirePolyLine(points=((d1[int(hull)+1], d1[int(hull)+1]),), mergeType=IMPRINT, meshable=ON) eg = p.edges
p.wirePolyLine is the method to draw a wireline, and we’re joining datum points for all possible combinations for each row provided in the convex hull data. For example: A triangle is represented as (P1, P2, P3). To draw this triangle, we need a line between P1 and P2, P2 and P3 and finally P1 and P3.
An edge is automatically created for each wire line drawn, and we can access that array of edges by calling p.edges, where p is the model part variable name defined earlier.
Next is to specify the necessary edges to form a face. Since we’re dealing with triangles, we need 3 edges. Now this section took some thinking.
You would think that the order of edges in the edges array corresponds to the order in which the wire lines were created, but it’s not – which means that you can’t simply loop over the edges array (eg) and take every 3 edges sequentially.
The edges array denoted by variable eg returns an array of edges with random ordering of wires and a coordinate that lies on the edge, therefore just using this raw data, you cannot determine which edges you need to construct a face.
Luckily, by using coordinate founds in each object in the edges array eg you can determine which edge it belongs to, by proving its collinearity between two datum points, thus allowing us to loop over the convex hull array and select the right ordering of edges to create a face.
# create faces for p in convexHull: seq =  wireSet =  for edge in eg: point = edge.pointOn if isCollinear(point, d1[int(p)+1].pointOn,d1[int(p)+1].pointOn): if checkWire(1,wireSet): continue else: seq.append(edge) wireSet.append(1) if isCollinear(point, d1[int(p)+1].pointOn,d1[int(p)+1].pointOn): if checkWire(2,wireSet): continue else: seq.append(edge) wireSet.append(2) if isCollinear(point, d1[int(p)+1].pointOn,d1[int(p)+1].pointOn): if checkWire(3,wireSet): continue else: seq.append(edge) wireSet.append(3) if len(seq) == 3: p = mdb.models['Model-1'].parts['Part-1'] p.CoverEdges(edgeList = seq, tryAnalytical=True) break
Once you have defined all faces that is fully enclosed, you can simply convert the shell faces into a solid.
p = mdb.models['Model-1'].parts['Part-1'] f = p.faces p.AddCells(faceList = f)
There you have it, I have described the entire Abaqus to construct a 3D polygon programmatically using Python scripting. You can now extend this to the creation of many more 3D polygons for more advanced models.
Note: I previously have found that the construction of faces from edges not working for datum points / edges points that are really close together (in less than 1mm scale) . To ensure it works properly, I would try avoid polygon vertices that are clustered too close together.
Submit a form to get to touch