Skip to content

Instantly share code, notes, and snippets.

@jphungcode
Created June 8, 2019 13:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jphungcode/9d3117a68cb1b906b66b362872a8c594 to your computer and use it in GitHub Desktop.
Save jphungcode/9d3117a68cb1b906b66b362872a8c594 to your computer and use it in GitHub Desktop.
generatePolyhedra.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "generatePolyhedra.ipynb",
"version": "0.3.2",
"provenance": [],
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/jphungcode/9d3117a68cb1b906b66b362872a8c594/generatepolyhedra.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "0JVIJl31IN6O",
"colab_type": "text"
},
"source": [
"If you’re into heterogeneous modelling then you might’ve come across some issues with constructing a 3D polygon with random vertices using Abaqus. 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.\n",
"![Abaqus Screenshot](https://i.ibb.co/C8mXRXg/abaqus-screenshot.png)\n",
"---\n",
"\n",
"## Getting Started\n",
"You will need a set of polygon vertice coordinates and its convex hull, which you can download [here](https://https://github.com/jphungcode/abaqus-polyhedra). Simply download the polygon.csv and polygon-hull.csv.\n",
"\n",
"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).\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"metadata": {
"id": "M3A5ZHJ0I92n",
"colab_type": "code",
"colab": {}
},
"source": [
"from abaqus import *\n",
"from abaqusConstants import *\n",
"import numpy as np\n",
"import os\n",
"import __main__\n",
"\n",
"import section\n",
"import regionToolset\n",
"import displayGroupMdbToolset as dgm\n",
"import part\n",
"import material\n",
"import assembly\n",
"import step\n",
"import interaction\n",
"import load\n",
"import mesh\n",
"import optimization\n",
"import job\n",
"import sketch\n",
"import visualization\n",
"import xyPlot\n",
"import displayGroupOdbToolset as dgo\n",
"import connectorBehavior\n",
"\n",
"csvPath = r\"/path\" #path storing the polygon data\n",
"os.chdir(csvPath)\n",
"\n",
"data = [];\n",
"convexHull = [];\n",
"data = np.genfromtxt('polygon1.csv', delimiter=',')\n",
"convexHull = np.genfromtxt('polygon1-hull.csv', delimiter=',')"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "grn51Lj_SVge",
"colab_type": "text"
},
"source": [
"## General Procedure\n",
"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:\n",
"\n",
"1. Create datum points which represent each polygon vertex (Part > Datum Point)\n",
"2. Join datum points with a wire line (Shape > Wire > Point to Point) by following the convex hull configuration\n",
"3. Each wire line represents an edge, and 3 edges which forms a triangle can be converted to a face. To do this (Tools > Geometry Edit > Face > Cover Edges > select 3 edges that enclose the face)\n",
"4. Convert all wire lines into faces\n",
"5. Select all faces and convert it to a solid (Shape > Solid > From shell > Select all faces)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ZrUhDhMuSwxQ",
"colab_type": "text"
},
"source": [
"## Create Vertices using Datum Points\n",
"Now, let’s have a look at the python code to script this."
]
},
{
"cell_type": "code",
"metadata": {
"id": "lHeJH028S0cQ",
"colab_type": "code",
"colab": {}
},
"source": [
"# create coordinate reference and supress\n",
"p = mdb.models['Model-1'].Part(name='Part-1', dimensionality=THREE_D, type=DEFORMABLE_BODY)\n",
"p.ReferencePoint(point=(0.0, 0.0, 0.0))\t\n",
"p.features['RP'].suppress()\n",
"\n",
"for item in data:\n",
"\tp.DatumPointByCoordinate(coords=(item[0], item[1], item[2]))\n",
"\t\n",
"d1 = p.datums;"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "L9p9TZOeS3-U",
"colab_type": "text"
},
"source": [
"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."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ojn9VFnlS6-f",
"colab_type": "text"
},
"source": [
"##Create Edges using Wires\n",
"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."
]
},
{
"cell_type": "code",
"metadata": {
"id": "Fkj8H-DdS980",
"colab_type": "code",
"colab": {}
},
"source": [
"# join datum for edges\n",
"for hull in convexHull:\n",
"\tp.WirePolyLine(points=((d1[int(hull[0])+1], d1[int(hull[1])+1]),), mergeType=IMPRINT, meshable=ON)\n",
"\tp.WirePolyLine(points=((d1[int(hull[1])+1], d1[int(hull[2])+1]),), mergeType=IMPRINT, meshable=ON)\n",
"\tp.WirePolyLine(points=((d1[int(hull[2])+1], d1[int(hull[0])+1]),), mergeType=IMPRINT, meshable=ON)\n",
"\t\n",
"eg = p.edges"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "xpf0mNNSTAvy",
"colab_type": "text"
},
"source": [
"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.\n",
"\n",
"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."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "YjvjTrJsTCmI",
"colab_type": "text"
},
"source": [
"##Create Faces from Three Edges\n",
"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.\n",
"\n",
"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.\n",
"\n",
"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.\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"metadata": {
"id": "YqSLaVEYTGir",
"colab_type": "code",
"colab": {}
},
"source": [
"# create faces\n",
"for p in convexHull:\n",
"\tseq = []\n",
"\twireSet = []\n",
"\tfor edge in eg:\n",
"\t\tpoint = edge.pointOn[0]\n",
"\t\tif isCollinear(point, d1[int(p[0])+1].pointOn,d1[int(p[1])+1].pointOn):\t\n",
"\t\t\tif checkWire(1,wireSet):\n",
"\t\t\t\tcontinue\n",
"\t\t\telse:\n",
"\t\t\t\tseq.append(edge)\n",
"\t\t\t\twireSet.append(1)\n",
"\t\tif isCollinear(point, d1[int(p[1])+1].pointOn,d1[int(p[2])+1].pointOn):\n",
"\t\t\tif checkWire(2,wireSet):\n",
"\t\t\t\tcontinue\n",
"\t\t\telse:\n",
"\t\t\t\tseq.append(edge)\n",
"\t\t\t\twireSet.append(2)\n",
"\t\tif isCollinear(point, d1[int(p[2])+1].pointOn,d1[int(p[0])+1].pointOn):\n",
"\t\t\tif checkWire(3,wireSet):\n",
"\t\t\t\tcontinue\n",
"\t\t\telse:\n",
"\t\t\t\tseq.append(edge)\n",
"\t\t\t\twireSet.append(3)\n",
"\t\tif len(seq) == 3:\n",
"\t\t\tp = mdb.models['Model-1'].parts['Part-1']\n",
"\t\t\tp.CoverEdges(edgeList = seq, tryAnalytical=True)\n",
"\t\t\tbreak"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "dmZQmR2HTIb_",
"colab_type": "text"
},
"source": [
"##Create a Solid\n",
"Once you have defined all faces that is fully enclosed, you can simply convert the shell faces into a solid."
]
},
{
"cell_type": "code",
"metadata": {
"id": "AOKS2H_JTLDV",
"colab_type": "code",
"colab": {}
},
"source": [
"p = mdb.models['Model-1'].parts['Part-1']\n",
"f = p.faces\n",
"p.AddCells(faceList = f)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "Lb-u9vd7TMwE",
"colab_type": "text"
},
"source": [
"##Summary\n",
"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.\n",
"\n",
"**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."
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment