Abaqus Scripting Overview
Abaqus Scripting Overview
Introduction
Abaqus ships with python 2.7 and allows you to automate, pre- and post-process, streamline parametric studies, and batch-run complex simulations. This page provides a brief overview of its scripting abilities while providing some common snippets.
Official Docs: SIMULIA Documentation Portal
1. Basic Concepts
1.1 What is Abaqus scripting?
Abaqus scripting is the use of Python (with custom Abaqus modules) to programmatically access and manipulate the model database (.cae
) and output database (.odb
). This is done entirely within Abaqus, and enables:
- Model creation and modification
- Job submission and monitoring
- Postprocessing and report generation
- Parametric and batch studies
1.2 Running scripts
There are two standard methods for running a script:
Through command prompt
abaqus cae script=myscript.py
Within Abaqus
File -> Run Script
or “Run Script” on welcome window.
1.3 Imports and modules
A typical Abaqus script imports the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
from part import *
from material import *
from section import *
from assembly import *
from step import *
from interaction import *
from load import *
from mesh import *
from optimization import *
from job import *
from sketch import *
from visualization import *
from connectorBehavior import *
1.4 Broad script types:
An Abaqus model can be divided into two parts, a model database (MBD) that contains the geometry and like, and an output database (ODB) which stores results. While obviously you can make a script that both builds and processes a model, as a generality it is better to split your scripts into ones that automate model creation and ones that postprocess for readability, refactoring, and reusability.
2. Model Building Workflow
A simplified typical modeling flow in scripts mirrors the GUI structure:
- Create part
- Define materials and sections
- Assemble parts
- Create steps
- Define boundary conditions and loads
- Mesh the part
- Submit the job
Example: Basic 2D model setup
1
2
3
4
5
6
7
8
9
10
mdb.Model(name='Model-1')
model = mdb.models['Model-1']
sketch = model.ConstrainedSketch(name='profile', sheetSize=20.0)
sketch.rectangle(point1=(-5.0, -1.0), point2=(5.0, 1.0))
mdb.models['Model-1'].Part(dimensionality=TWO_D_PLANAR,
name='Part-1',
type=DEFORMABLE_BODY)
part.BaseShell(sketch=sketch)
Material and section definition
1
2
3
4
5
6
7
8
model.Material(name='Material-1')
model.materials['Material-1'].Elastic(table=((1e9, 0.3),))
model.HomogeneousSolidSection(name='Section-1', material='Material-1')
faces = part.faces.findAt(((0.0, 0.0, 0.0),))
region = regionToolset.Region(faces=faces)
part.SectionAssignment(region=region, sectionName='Section-1')
Assembly and step creation
1
2
3
4
5
6
assembly = model.rootAssembly
assembly.DatumCsysByDefault(CARTESIAN)
assembly.Instance(name='Part-1-1', part=part, dependent=ON)
model.StaticStep(name='Step-1', previous='Initial',
nlgeom=ON, initialInc=0.1, maxInc=0.1)
Boundary conditions and loads
1
2
3
4
5
6
7
8
9
10
11
12
13
14
edge = part.edges.findAt(((-5.0, 0.0, 0.0),))
part.Set(name='Set-1', edges=edge)
instance = assembly.instances['Part-1-1']
region = instance.sets['Set-1']
model.DisplacementBC(name='BC-1', createStepName='Step-1',
region=region, u1=0.0, u2=0.0)
top_edge = part.edges.findAt(((0.0, 1.0, 0.0),))
part.Surface(name='Surf-1', side1Edges=top_edge)
surface = instance.surfaces['Surf-1']
model.Pressure(name='Load-1', createStepName='Step-1',
region=surface, magnitude=-1e-5)
Mesh and job creation
1
2
3
4
5
6
7
8
9
part.seedPart(size=0.5)
part.setMeshControls(regions=faces, technique=STRUCTURED, elemShape=QUAD)
elemType1 = mesh.ElemType(elemCode=CPS8R, elemLibrary=STANDARD)
elemType2 = mesh.ElemType(elemCode=CPS6M, elemLibrary=STANDARD)
part.setElementType(regions=faces, elemTypes=(elemType1, elemType2))
part.generateMesh()
mdb.Job(name='Job-1', model='Model-1')
mdb.jobs['Job-1'].submit()
All together, a rough template for an entire Abaqus model in script form is:
Template Script Structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from abaqus import *
from abaqusConstants import *
from part import * # ...etc
def build_model():
# create model, part, material, section
pass
def apply_loads_and_bcs():
# create steps, apply BCs and loads
pass
def mesh_and_submit():
# mesh and submit job
pass
if __name__ == '__main__':
build_model()
apply_loads_and_bcs()
mesh_and_submit()
3. Parametric Studies
Rather than manually adjusting the parameters or hyperparameters of a model, these values can be dynamically or programmatically controlled using scripts. Abaqus scripting is particularly well suited to this task, allowing you to loop over geometry, material properties, boundary conditions, loads, or even entire models to systematically generate variations and explore a design space.
This approach is especially useful for parametric sweeps, “what-if” analyses, and optimization routines.
Using the previous modeling structure as a base, a parametric study can be implemented by embedding key variables (e.g., width) into the model definition. The script then loops over a defined range of values, generating and running each variation automatically:
1
2
3
4
5
6
def build_model(width):
# create model, part, material, section
pass
for width in range(widthMin, widthMax):
build_model(width)
As an example, this script automates a study of beam deflection with different widths.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from abaqus import *
from abaqusConstants import *
from part import *
from material import *
from section import *
from assembly import *
from step import *
from load import *
from mesh import *
from job import *
def create_model(width):
model_name = f'Model_{width}'
job_name = f'Job_{width}'
model = mdb.Model(name=model_name)
s = model.ConstrainedSketch(name='beam_profile', sheetSize=200.0)
s.rectangle(point1=(0, 0), point2=(width, 10.0))
part = model.Part(name='Beam', dimensionality=TWO_D_PLANAR,
type=DEFORMABLE_BODY)
part.BaseShell(sketch=s)
model.Material(name='Steel')
model.materials['Steel'].Elastic(table=((210e9, 0.3),))
model.HomogeneousSolidSection(name='Section', material='Steel')
region = part.faces.findAt(((width / 2.0, 5.0, 0.0),))
part.SectionAssignment(region=region, sectionName='Section')
assembly = model.rootAssembly
assembly.DatumCsysByDefault(CARTESIAN)
inst = assembly.Instance(name='BeamInst', part=part, dependent=ON)
model.StaticStep(name='LoadStep', previous='Initial')
edge = part.edges.findAt(((0.0, 5.0, 0.0),))
part.Set(name='FixedEdge', edges=edge)
assembly.Set(name='FixedEdgeInst',
referencePoints=(inst.edges.findAt(((0.0, 5.0, 0.0),))[0],))
model.DisplacementBC(name='FixedBC', createStepName='LoadStep',
region=assembly.instances['BeamInst'].sets['FixedEdge'],
u1=0.0, u2=0.0)
edge = part.edges.findAt(((width, 5.0, 0.0),))
part.Surface(name='LoadSurf', side1Edges=edge)
model.Pressure(name='EndLoad', createStepName='LoadStep',
region=assembly.instances['BeamInst'].surfaces['LoadSurf'],
magnitude=-1e6)
part.seedPart(size=1.0)
part.setMeshControls(regions=part.faces, technique=STRUCTURED)
elemType = mesh.ElemType(elemCode=CPS4R, elemLibrary=STANDARD)
part.setElementType(regions=part.faces, elemTypes=(elemType,))
part.generateMesh()
mdb.Job(name=job_name, model=model_name)
mdb.jobs[job_name].submit()
mdb.jobs[job_name].waitForCompletion()
# Parametric loop
for w in [5.0, 10.0, 15.0, 20.0]:
create_model(width=w)
Notes and best practices
- Use functions: Encapsulate model creation logic in functions for clarity and reuse.
- Wait for job completion before postprocessing with
.waitForCompletion()
. - Make unique jobnames to prevent simple oversights which can cause errors.
- Execute scripts without gui (
abaqus cae noGUI=script.py
) to increase performance. - Clean up models if needed using
del mdb.models[model_name]
to save memory.
4. Postprocessing
Once a model has been executed, its simulation results are stored in an accompanying .odb
file and can be parsed to obtain specific information or to translate data into other formats.
4.1 ODB Structure
An .odb
file has a hierarchical structure:
1
2
3
4
5
6
7
odb
└── steps
└── Step-1
├── frames (output frames over time)
├── fieldOutputs (U, S, etc.)
└── historyRegions
└── Step-2 ...
- Steps represent analysis increments (e.g. load steps)
- Frames contain data snapshots at particular increments/iterations
- Field outputs include spatial fields (e.g. stress, displacement)
- History outputs include scalar values tracked over time (e.g. reaction forces)
4.2 Opening the Output Database
Use the openOdb
function from odbAccess
or session.openOdb()
to load the .odb
.
1
2
from odbAccess import openOdb
odb = openOdb(path='Job-1.odb')
To display it in CAE (optional):
1
session.viewports['Viewport: 1'].setValues(displayedObject=odb)
To close it:
1
odb.close()
4.3 Accessing Field Output
The .odb
object stores simulation results and can be queried in a familiar Pythonic manner. For example, the list of steps in the model can be obtained from:
1
print(odb.steps.keys())
Field outputs are spatial—defined at nodes or integration points with relevant information obtained from the model. The most common ones are stress ('S'
), displacment ('U'
), reaction force ('RF'
), strain ('E'
), and logarithmic strain ('LE'
). These can be found by:
1
2
3
4
5
6
frame = odb.steps['Step-1'].frames[-1]
print(frame.fieldOutputs.keys()) # e.g., 'S'-stress,
# 'U'-displacment,
# 'RF'-reaction force,
# 'E'-strain,
# 'LE'-log. strain
Using displacement as an example, the displacement of the nodes of the model can be found with:
1
2
3
disp = frame.fieldOutputs['U'] # Displacement
for v in disp.values:
print(f'Node {v.nodeLabel}: U1={v.data[0]}, U2={v.data[1]}')
Information pertaining to a specific node can be found with:
1
2
node_disp = [v for v in disp.values if v.nodeLabel == 100][0]
print(node_disp.data)
This data can be used or maniuplated in the typical manor. For example, you can subtract or combine fields using standard operators to perform custom calculations like incremental displacement, stress changes, etc.:
1
2
3
4
5
frame1 = odb.steps['Step-1'].frames[-2]
frame2 = odb.steps['Step-1'].frames[-1]
disp1 = frame1.fieldOutputs['U']
disp2 = frame2.fieldOutputs['U']
delta_disp = disp2 - disp1
One common use is to print the data into a neutral format, such as .csv
:
1
2
3
4
5
with open('displacement_output.csv', 'w') as f:
f.write('Node,U1,U2,U3\n')
for v in disp.values:
u = v.data
f.write(f'{v.nodeLabel},{u[0]},{u[1]},{u[2]}\n')
Integration points are parsed in a similar manor:
1
2
3
4
stress = frame.fieldOutputs['S']
for v in stress.values:
if v.integrationPoint:
print(f'Elem {v.elementLabel}: von Mises = {v.mises}')
4.4 Accessing History Output
History outputs, unlike field data, represent scalar values tracked over time—e.g., force or displacement at specific nodes or regions. Regardless, they are parsed in a similar manor to field objects
Accessing reaction force:
1
2
3
4
5
6
7
region = odb.steps['Step-1'].historyRegions
for key in region.keys():
print(key) # e.g., 'Node PART-1-1.100'
rf = region['Node PART-1-1.100'].historyOutputs['RF2']
for time, val in rf.data:
print(f'Time={time}, RF2={val}')
Exporting to CSV:
1
2
3
4
with open('rf_vs_time.csv', 'w') as f:
f.write('Time,RF2\n')
for time, val in rf.data:
f.write(f'{time},{val}\n')
4.5 Working with Sets and Regions
As an alternative to node definitions, sets can be used to make automation more friendly, but follow a similar pattern:
1
2
part_instance = odb.rootAssembly.instances['PART-1-1']
region = part_instance.nodeSets['SET-1']
Then:
1
disp = frame.fieldOutputs['U'].getSubset(region=region)
You can create sets in your model and access them by name in postprocessing scripts.
4.6 Plotting in CAE
Results can be visualized either as colored contour plots on the model geometry or as XY plots. Personally, I find extracting results to .csv
and using external tools more effective, but both approaches are scriptable in Abaqus.
Contour plot of displacement:
1
2
3
4
session.viewports['Viewport: 1'].odbDisplay.setPrimaryVariable(
field=disp, outputPosition=NODAL)
session.viewports['Viewport: 1'].odbDisplay.display.setValues(
plotState=(CONTOURS_ON_DEF,))
XY plot of displacement over time:
1
2
3
4
5
6
7
8
9
10
11
12
xy_data = xyPlot.XYDataFromHistory(name='Disp_vs_Time',
odb=odb, outputVariableName='U2 at Node 100',
steps=('Step-1',),
region=region['Node PART-1-1.100'],
variable='U2')
session.xyPlots['MyPlot'] = xyPlot.XYPlot('MyPlot')
c = session.xyPlots['MyPlot'].chartNames[0]
chart = session.xyPlots['MyPlot'].charts[c]
chart.setValues(curves=(session.Curve(xyData=xy_data),))
session.viewports['Viewport: 1'].setValues(displayedObject=session.xyPlots['MyPlot'])
4.7 Multiple ODBs
When run in a batch, rather than processing the models in-loop, they can all be run, then accessed after. This process is straightforward and similar to the previous examples:
1
2
3
4
5
6
7
8
9
10
from odbAccess import openOdb
from abaqusConstants import *
for job_name in ["Job_1", "Job_2", "Job_3"]:
odb = openOdb(f'{job_name}.odb')
last_frame = odb.steps['LoadStep'].frames[-1]
disp = last_frame.fieldOutputs['U']
u2_vals = [v.data[1] for v in disp.values]
print(f'Job = {job_name}, Max U2 = {max(u2_vals):.6e}')
odb.close()
4.8 Example: Full Postprocessing Script
This script opens an existing model and prints it to a .csv
file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from odbAccess import openOdb
from abaqusConstants import *
odb = openOdb('Job-1.odb')
step = odb.steps['Step-1']
frame = step.frames[-1]
disp = frame.fieldOutputs['U']
stress = frame.fieldOutputs['S']
with open('results_summary.csv', 'w') as f:
f.write('Node,U1,U2,Mises\n')
for d, s in zip(disp.values, stress.values):
label = d.nodeLabel
u1, u2, _ = d.data
mises = s.mises
f.write(f'{label},{u1},{u2},{mises}\n')
odb.close()