Scripting in Abaqus with abapys

Usage of abapys v0.6 library with code examples

Dominik Zobel

version: January 2021

1 Introduction

This document focuses on the Abaqus-Python library named abapys1 and its usage. abapys was created to support and ease controlling of many Abaqus operations with Python, especially automation of output processing (odb-files) and model creation. Since it was developed alongside work at the Institute of Geotechnical Engineering at the Hamburg University of Technology, its focus is on soil mechanics and soil-structure interaction problems. With the current version of abapys it is easier to do tasks such as create simple geometric bodies and layered soil models, select elements or geometries based on arbitrary functions, transfer output states from one model to another and create or save plots and output values. Many functions encourage a more optimized scripting workflow, e. g. for model parametrization or postprocessing big parameter studies.

To get the most out of this documentation and understand how to use this library, basic knowledge in working with Abaqus and basic programming knowledge in Python is assumed. If you have no or very little knowledge about Python there are some very good tutorials available online. You may also want to have a look at the official Python homepage https://www.python.org/. There are other introductions to scripting in Abaqus available online, but basic Python knowledge should suffice to understand the remainder of this document.

Abaqus uses Python 2 internally. Although compatibility with Python 3 has been a goal for abapys and the following example, some Python 2 specific code might still be used.

In this document, all hints are highlighted like this.

If you have no or very little knowledge about Abaqus, you may want to have a look at some tutorials (and the resulting Python code) to get started. In addition to many other tutorials provided in the web, a introductory tutorial named ”Pile jacking example in Abaqus“ is recommended.2

Interested readers may also want to look at the official »Abaqus Scripting User’s Guide« from Dassault Systèmes. Especially for german speaking readers it is also recommended to have a look at the abapys function documentation, e. g. at https://d-zo.github.io/abapys/abapys.html.3 Documentation and/or source code should always be consulted when questions about an abapys function and/or its function arguments need to be clarified.

The abapys function documentation should also be accompanying the abapys Python library.

This document provides a short overview of scripting in Abaqus in general (chapter 2) and the basics to include and work with abapys (chapter 3). Afterwards the most relevant functions are discussed by topic: Chapter 4 deals with viewport manipulation and chapter 5 with selections. Functions and code snippets for model creation are presented in chapter 6. The transfer of output data to new models and the initialisation of states is discussed thereafter (chapter 7). Chapter 8 introduces some possibilities to query data from models. Chapter 9 is about processing and visualizing output database results. Although using abapys can only contribute to some extent when creating a full model creation script, a possible and general Python template for model creation is presented in chapter 10.

Author’s remark:

I am grateful for all constructive feedback to this document, especially to my colleague Francisco.

2 Scripting in Abaqus

2.1 Scripting Resources in Abaqus

When working in Abaqus/CAE, almost all actions are logged in a replay-file (abaqus.rpy). Additionally, when a model file <model>.cae is saved, all actions relevant to the model are saved in an accompanying journal file <model>.jnl. Both files contain Python commands and can be run as script (partially or whole).4

A short note on notation: Text within pointy brackets like <name>.cae resembles a placeholder. So a model with the name cpt would replace the placeholder to yield cpt.cae.

In this document, scripts for model creation and for processing output databases will be differentiated since different dependencies (imports) are necessary. In general, when creating a custom script, it is recommended to start with a reference to the file encoding and an import section. Usually, only some of the imports shown in code 2.1 and 2.2 are needed to load all required funcions, access modules and internal constants for model creation/manipulation. But there is usually no harm in loading all dependencies.

After loading the corresponding modules, all required dependencies, internal states and data should be available (and modifiable unless restricted by Abaqus). A comprehensive reference for all Abaqus-specific commands can be found in the official »Abaqus Scripting Reference Guide«.

In Abaqus, the internal data is saved in a nested structure called the Abaqus object model. A small excerpt of the structure is given by the following figure 2.1, but more in-depth knowledge can e. g. be found in the official »Abaqus Scripting User’s Guide«.

Code 2.1: Imports for working with a model database.

# -*- coding: utf-8 -*-
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 *

Code 2.2: Imports for working with an output database.

# -*- coding: utf-8 -*-
from odbAccess import *
from odbMaterial import *
from odbSection import *
from abaqusConstants import *
from visualization import *
import displayGroupOdbToolset as myodbtoolset
Figure 2.1: Excerpt from the Abaqus object model structure.

2.2 Accessing Objects in Abaqus

To query an object or its structure/children in Abaqus/CAE, simply type

print <object>

in the Abaqus command line at the bottom of the Abaqus/CAE window and press Enter. Child objects can be accessed by their parent name, a dot and their name. Elements of a list can be accessed by their index in square brackets. Members of a repository can be listed with the keys()-command and accessed by their name in square brackets. If the existance of a member of an repository object is not known, it can be checked with an has_key()-command (see e. g. code 2.3). Abaqus also has an auto-completion feature which allows browsing through members of the current repository or function arguments by pressing TAB.

Code 2.3: Example commands to access the Abaqus object model.

print session.spectrums        # Show all available spectrum objects of this session
print session.spectrums.keys() # Since spectrums is a Repository (object) of spectrum objects,
                               # the keys()-function is better suited to list all members
firstspectrum = session.spectrums.keys()[0]  # Save the name of the first spectrum in a variable
                                             # The result of keys() is always a list
print session.spectrums[firstspectrum]       # Use this variable to access the first spectrum
                                             # without the need to use the following equivalent
print session.spectrums[session.spectrums.keys()[0]]

if (session.spectrums.has_key('Black to white')):
   print('Yes');
else:
   print('No');

2.3 Useful Initial Commands when Accessing the Abaqus Object Model

When processing models or postprocessing output databases, familiarity with the Abaqus object model is extremely helpful to get good results. Although the availability of some objects is problem dependent, some general commands are used more frequently than others, e. g. when accessing the current viewport, model or output database. Four basic commands to work with the Abaqus object model are shown in code 2.4.

Code 2.4: Useful commands when interacting with the Abaqus object model.

# Make a reference for the current (active) viewport
myviewport = session.viewports[session.currentViewportName];

# Print the currently displayed object (its name can be accessed by appending ".name")
print myviewport.displayedObject

# List of all available models
print mdb.models.keys();

# List of all available output databases
print session.odbs.keys();

To shorten subsequent access commands, variables can be assigned to create a reference to an object.

When an output database is opened, results are available in session.odbs[<name>] while some structural data is only available in session.odbData[<name>] (see also code 2.5).

Code 2.5: Commands to access output information and data.

stepnames = session.odbData[<name>].steps.keys();              # Name of steps
histnames = session.odbData[<name>].historyVariables.keys();   # Name of HistoryOutput variables

# Unlike HistoryOutputs, FieldOutputs are only available in requested frames
fieldnames = session.odbs[<name>].steps[stepnames[0]].frames[0].fieldOutputs;

2.4 Saving and Running a Script in Abaqus

Whenever a sequence of commands should be reused as a script, those commands can be pasted in a new text document (after the appropriate import header as described in section 2.1). The new script can be saved as <filename>.py since the ending py refers to a Python (script) file.

To run a script in Abaqus, choose File->Run Script from the main menu and select the Python script file (see figure 2.2). It is also possible to run a script without the Graphical User Interface (GUI). This might be handy when an input file should be generated or results should be read/saved without the need for examination or user interaction. To do so, open a shell (Linux) or command window (Windows), navigate to the desired working directory and execute the following command:5

abaqus cae noGUI=<scriptname.py>
Figure 2.2: Abaqus main window with Python scripting console (>>>-tab in bottom area). To run a script, select File->Run Script as shown.

3 Working with abapys

3.1 How to Include abapys in a Script

First, the abapys library has to be available on the system. Since Abaqus uses its internal Python interpreter, it is sufficient to download the abapys archive, extract the files and put them in any folder accessible by Abaqus. One suitable folder is the temporary work directory of Abaqus.

In the Abaqus command line or any script using abapys, code 3.1 can be used to import the abapys functions and initialise them.

It is recommended to use InitialisiereAbapys() in every script after abapys import. Since some settings and commands changed for different Abaqus versions, abapys needs to know which version is used to achieve the intended results. If this function is not called, some functions might not behave as expected.

Code 3.1: Initialisation of abapys.

abapys_dir = r'<X:\path\to\abapys>';   # Adjust path (Windows, starts with r before string) or
#abapys_dir = '</path/to/abapys>';      # adjust path (Linux)
sys.path.insert(0, abapys_dir);

from abapys import *
InitialisiereAbapys(session=session, version=version, pfad=abapys_dir);

InitialisiereAbapys() is tuning internal parameters of abapys with respect to the operating system and the Abaqus version (using the session and version variables provided by Abaqus). It has an additional pfad argument which should point to the directory of abapys6 (two optional scaling arguments for InitialisiereAbapys() are introduced in section 4.2).

To get an overview of all available (default) arguments and detailed function information, also have a look at the abapys function documentation (in german). The most recent version is stored with the abapys module or available online at https://d-zo.github.io/abapys/abapys.html. It is also possible to get information about any abapys function in Abaqus after the module is loaded. To query the doc-string of any abapys-function, enter the following command in the Abaqus command:

print <function_name>.__doc__

3.2 Overview of abapys Functions

Currently the abapys module provides more than 90 functions for model creation, output processing or both. Some of them might only be useful for very special purposes, others are needed in most scripts. The following list provides some categorization with the most important functions. All of the listed functions are covered in this document.

Multi-purpose or auxiliary functions:

Functions to aid model creation:

Functions for output processing:

Note on Adjusting abapys Functions

Abaqus loads all internal modules at startup and all other Python modules on their first import. If any changes are made to the files afterwards, this won’t have any effect until the modules are actually read again (i. e. by restarting Abaqus). This does not only apply to abapys but to all module imports (and therefore all custom functions and used libraries).

4 Viewport Manipulation

4.1 Opening an Output Database

First load a model or an output database into your current session and display it. This can be done manually by File -> Open or in code. For a output database named odbname the code is simply session.openOdb(name=odbname).

For many scripting scenarios it is recommended to check if the database is already open before trying to open it again (see code 4.1). It is useful to assign the resulting object to a variable and display it in the viewport afterwards (line 10).

Code 4.1: Accessing an output database.

odbname = <odbname>;                   # Adjust full name/path

if session.odbData.has_key(odbname):
   myodb = session.odbs[odbname];
else:
   myodb = session.openOdb(name=odbname);
#

myviewport = session.viewports[session.currentViewportName];
myviewport.setValues(displayedObject=myodb, displayMode=SINGLE);

To show the effect of some commands, a simple model is used throughout this tutorial. The model consists of a quarter of an open-ended pile which is positioned above a soil volume (cf. figure 4.1). The soil instance is called instBoden and the pile instance is called instPfahl. There is nothing special about this model—any model can be used.

Figure 4.1: Model used in multiple examples. A quarter of a cylindrical shell is positioned above a soil volume. The partitions of the soil volume and its dimension can also be seen.

4.2 Adjusting the Viewport Size

The new viewport size in terms of pixels can be specified by ViewportGroesseAendern() with the required arguments viewport and the new size bildgroesse.

Code 4.2: Changing the viewport size.

ViewportGroesseAendern(viewport=myviewport, bildgroesse=[800, 800]);

ViewportGroesseAendern() is a typical function which may yield different results with different Abaqus versions. Remember to always use InitialisiereAbapys() before any other abapys function calls to prevent this. It is strongly recommended to adjust code 3.1 to your needs and always use it before calling any other abapys function.

According the official documentation, Abaqus’ internal values represent the width/height of the viewport in millimeters. With a density of 96 dpi (and 25.4 mm/inch), a factor can be calculated for transforming the internal length values into pixel values (and vice versa). But depending on the environment Abaqus is run in, Abaqus seems to use additional scaling factors for the resulting width/height. Therefore, if the output images do not match the intended pixel size in the current session, try passing two custom correction factors to the optional arguments xSkalierung and ySkalierung of InitialisiereAbapys().

Getting the Custom Correction Factors

One way to get the correction factors for xSkalierung and ySkalierung is to start Abaqus and restore the viewport, so it has a border and title bar (e. g. by myviewport.restore()). Then the current size can be obtained by

Code 4.3: Querying the current dimension of the viewport.

print([myviewport.currentWidth, myviewport.currentHeight])

Go to File->Print, select a rasterized file output like png and make sure to have Show viewport decorations (if visible) checked. For later output it can be checked/unchecked at will, but for obtaining the correction factors a windowed viewport in Abaqus with viewport decorations is needed.7 Either go to the png options () and read the current width and height in pixels or save the image to the hard disk and inspect its width and height. Use the Python command interpreter or any calculator to calculate

Code 4.4: Obtaining the current scaling factors.

ppi = 96.0/25.4;
xSkalierung = <image_width_in_pixels> / (myviewport.currentWidth*ppi);
ySkalierung = <image_height_in_pixels> / (myviewport.currentHeight*ppi);

Both factors should be close to 1 (usually between 0.998 and 1.001). The same factors (accurate to about six digits) should be obtainable, if the size of the viewport window is changed graphically.8

4.3 Saving the Current Viewport as an Image

To save the current viewport, you can either click on File -> Print or use the code

Code 4.5: Abaqus function for viewport printing.

session.printToFile(fileName=<dateiname>, format=PNG, canvasObjects=(myviewport, ));

Alternatively, the abapys function BildSpeichern() can be used. It calls the Abaqus function session.printToFile() internally but provides additional possibilities.

Code 4.6: Printing the viewport.

myviewport = session.viewports[session.currentViewportName];
ViewportGroesseAendern(viewport=myviewport, bildgroesse=[600, 600]);
BildSpeichern(dateiname='initialisieren', session=session);

The function has the following optional arguments with the given default values

The result for a simple model is shown in figure 4.2.

Figure 4.2: After loading the model into the viewport.

4.4 Adjusting the Viewport Style

In the default view, there are a text blocks and various symbols in the viewport. These viewport annotations can either be modified (e. g. larger font, different line width) or deactivated. Either go to the menu (View -> Viewport Annotation Option) or inspect the following structure to change the default settings.

Code 4.7: Displaying the viewport annotation options.

print myviewport.viewportAnnotationOptions

Additionally, the viewport annotation options as well as many other visual settings can be changed with the general purpose beautifying function ViewportVerschoenern(). This function applies many subjective improvements and can be influenced by its optional parameters. The basic call is

Code 4.8: Applying abapys default viewport settings.

ViewportVerschoenern(session=session);

The function has the following optional arguments with the given default values

Calling this function and using an additional command results in figure 4.3.

Figure 4.3: Adjusted viewport style.

4.5 Adjusting the Model View

The view on the model can be adjusted by different built-in views like Top, Right and Front as well as rotating, zooming and panning (cf. figure 4.4). Adjusting the basic view orientation can also be achieved by using Modellansicht().

This function accepts a default view name passed to the ansicht argument. Even more control is possible by explicitly defining which axis is pointing to the right/top by using the arguments rechts and oben respectively. The principal axes are defined as 1 (xx), 2 (yy) and 3 (zz). So if the xx-axis should point to the right and the zz-axis to the top, rechts=1 and oben=3 can be used as shown in the following code (negative numbers can be used to have an axis point to the left or bottom respectively).

Afterwards, further viewport manipulation can be done with view.rotate(), view.zoom() and view.pan() (also shown in code 4.9).

Code 4.9: Adjusting the view on the model.

myviewport.view.setValues(projection=PARALLEL);       # Use orthographic instead of perspective view
Modellansicht(session=session, rechts=1, oben=3);
myviewport.view.rotate(xAngle=15, yAngle=0, zAngle=15, mode=MODEL);
myviewport.view.zoom(zoomFactor=0.9, mode=ABSOLUTE);
myviewport.view.pan(xFraction=0.0, yFraction=0.1);
Figure 4.4: Adjusted model view.

4.6 Polishing a Viewport for Presentation

Some situations might benefit from more polished viewports, i. e. for presenting models. One simple method to improve the visualization of the model is to assign a different color to distinct instances/sets (cf. figure 4.5). For this example, the two instances instPfahl and instBoden of a model database are adjusted (the instances and the assigned colors have to be passed as a list to the argument zuweisungen).

Code 4.10: Colorizing instances of the model.

zuweisungen = [['instPfahl', '#495366'], ['instBoden', '#A79B80']];
BauteileEinfaerben(viewport=myviewport, zuweisungen=zuweisungen, odb=False);

The function has the following optional arguments with the given default values

Internally the color definitions of viewport.colorMappings['Set'] (if working with a model database/mdb) or viewport.colorMappings['Internal Set'] (if working with an output database/odb) respectively are modified with the Abaqus function viewport.setColor().

Figure 4.5: Colored model.

Also, measurements can be added to the viewport as annotations with ViewportBemassung(). This function requires either the model database object mdb or the the output database user data session.odbs[<name>].userData passed to the argument db and the current viewport object. The measurement itself has to have a label (bezeichnung) for internal reference and two points (punkt1 and punkt2), between which the distance should be measured. Using a non-zero offset in one or two dimensions can improve the visibility of the annotation from different angles.

Code 4.11: Adding three measurements to the viewport.

marker1 = ViewportBemassung(db=mdb, viewport=myviewport, bezeichnung='Bodenlaenge',
   offset=(0.0, -0.15, -0.15), punkt1=(0.0, 0.0, -1.5), punkt2=(1.5, 0.0, -1.5));
marker2 = ViewportBemassung(db=mdb, viewport=myviewport, bezeichnung='Bodenbreite',
   offset=(-0.15, 0.0, -0.15), punkt1=(0.0, 0.0, -1.5), punkt2=(0.0, 1.0, -1.5));
marker3 = ViewportBemassung(db=mdb, viewport=myviewport, bezeichnung='Bodenhoehe',
   offset=(0.15, -0.15, 0.0), punkt1=(1.5, 0.0, -1.5), punkt2=(1.5, 0.0, 0.0));

The function has the following optional argument with the given default value

The resulting model is shown at the beginning of this chapter in figure 4.1.

4.7 Additional Image Saving Functions

abapys has three special purpose functions enhancing the functionality of BildSpeichern(). They can be used to either save a high resolution image, a video sequence or both.

4.7.1 Printing High Resolution Images

There are at least two restrictions when saving the current viewport as a rasterized image: The image dimensions usually depend on the viewport size on screen and the maximum viewport size is restricted by Abaqus. One way to get high resolution images is to save multiple images by zooming and panning the viewport and fuse the seamless tiled images together afterwards. This can be done with MultiBildSpeichern() by adjusting the view of the resulting image in the viewport and specifying the number of tiles (images) in horizontal and vertical direction.

Code 4.12: Saving the viewport with tiled images.

MultiBildSpeichern(dateiname='spannung_ende_mul', session=session, anordnung=[3, 3], autozoom=True);

This function has the following optional arguments with the given default values

The images will be saved rowwise with an appended letter starting from a. Fusing a 3×33\times 3 grid can be done e. g. with ImageMagick (example from Bash in Linux). The resulting image compared to a normal viewport saving with BildSpeichern() can be seen in figure 4.6.

convert \( spannung_ende_mula.png spannung_ende_mulb.png spannung_ende_mulc.png +append \) \
   \( spannung_ende_muld.png spannung_ende_mule.png spannung_ende_mulf.png +append \) \
   \( spannung_ende_mulg.png spannung_ende_mulh.png spannung_ende_muli.png +append \) \
   -append spannung_ende.png

Figure 4.6: Saving the viewport with BildSpeichern() (left hand side) and MultiBildSpeichern() (right hand side). Note that since the images are fused together on the right hand side, the legend is too small and should also be adjusted.

4.7.2 Creating Videos

One of the simplest ways to create a video is by saving multiple (subsequent) images. If an output database is loaded to the current viewport containing multiple result frames, each frame can be saved in increasing order with VideobilderSpeichern().

Code 4.13: Saving a sequence of images.

VideobilderSpeichern(dateiname='video_spannung', session=session, odbname='Pfahl03std.odb');

This function has the following optional arguments with the given default values

Additional programs can be used to create a video out of the images, e. g. FFmpeg (example from Bash in Linux).

ffmpeg -framerate 10 -f image2 -i video_spannung%03d.png -c:v libx264 -vf "fps=24,format=yuv420p" \
   -y video_spannung.mp4;

There is also a function called MultiVideobilderSpeichern() combining the features (and some options) of both MultiBildSpeichern() and VideobilderSpeichern().

5 Selecting Objects

A common task is selecting objects (like nodes, elements or cells) and either creating a set of this selection (for easier reference later on) or directly applying loads, boundary conditions or other constraints. Whereas selecting objects in the Graphical User Interface (GUI) of Abaqus is straigtforward, using the command line is even more powerful but requires some understanding about how Abaqus stores and retrieves objects.

Additionally, (using the default settings) selections in Abaqus are stored in the journal/replay file as masked sequences (commands using getSequenceFromMask()). Those masked sequences might not help understanding the selection process, but essentially represent a sequence of all selected objects by their current indices.10

5.1 Elements and Sequences

Selecting elements usually means creating a sequence of certain elements. Using a simple Python example (cf. code 5.1), liste represents a certain object (here a list) and elem a single element of that object. In general, elem has a different type than liste but the sequence sequenz has the same type as liste and contains a subset of its elements (liste[0:1] would only contain elem). Sequences share their behaviour with the objects they are derived from.

Code 5.1: Extracting elements and sequences.

liste = ['1', 'second', ['c', 4]];
elem = liste[0];
sequenz = liste[0:2];

Many Abaqus objects support the concatenation of sequences with a simple +. Some loops might benefit from using an empty sequence like liste[0:0] and iteratively adding other sequences to it. Although this approach seems to work as intended, Abaqus sometimes issues the following warning when empty sequences are used.

RuntimeWarning: tp_compare didn't return -1, 0 or 1

5.2 Labels and Indices

Most geometric objects have an index and a label represented by integers.11 The label is a unique identifier for that object while the index represents the current position of that object in the collection of objects of that type.

The index of geometric objects might change when objects of that type are added or removed. (e. g. by partitioning or changing the order of operations in a script). This is the main reason to never use them explicitly for element selection.

In a model database, geometric elements like nodes and elements have a direct relation between label and index: label = index + 1. Since Abaqus is usually rearranging the order of objects defined in the model database to optimize access during the simulation procedure, the indices of geometric objects in the output database are most probably different.

Geometric objects like nodes and elements have the same label in the model database and the output database, but usually a different index.

It is therefore useful to work with labels rather than indices. To find objects by their label and access them as easily as with indices, the function ErstelleLabelsortierteGeomlist() can be used. The function has one required argument geomliste which can be any object containing elements with a label attribute.

Example: Selection by Label

The following examples use a simple model (introduced in section 4.1). It is assumed that the model is loaded and displayed in the viewport. The elements of the model can be accessed by elemente and the nodes by punkte (similar to code 5.2, also cf. figure 2.1).

Code 5.2: Accessing nodes and elements.

instBoden = session.odbs[<odbname>].rootAssembly.instances[<instname>];
punkte = instBoden.nodes;
elemente = instBoden.elements;
labelliste = [265, 266, 267, 268];   # Some (arbitrary) element labels used in the selection example

Additionally, it is assumed that the labels of the elements to be selected are stored in a variable named labelliste. A straightforward approach to select elements by their label is to iterate over all elements. In code 5.3 all ”selected“ elements will be visualized with the highlight() function (a simple visualisation can be seen in figure 5.1). For quick checks, element information can be queried by using Tools -> Query and choosing Element.

Code 5.3: Highlighting elements by their label.

for elem in elemente:
   if (elem.label in labelliste):
      highlight(elem);
Figure 5.1: Selected elements of the example model with code presented in section 5.2.

However, each time the code is used in a script/command line, it will iterate over all the elements (again) and become inefficient. A better approach is using the function ErstelleLabelsortierteGeomlist() introduced in the last section. Only one iteration over all elements is needed to create a dictionary (labels to indices) of these elements. Afterwards, simply input the requested label to the dictionary to get the index of the corresponding element.

Code 5.4: Highlighting elements with a label-index mapping.

label_zu_idx_elemente = ErstelleLabelsortierteGeomlist(geomliste=elemente);

for label in labelliste:
   highlight(elemente[label_zu_idx_elemente[label]]);

There is also an abapys function LabelAuswahl() which essentially does the same thing. However, when using LabelAuswahl() the output of ErstelleLabelsortierteGeomlist() can be passed as an optional argument elementhilfsliste to be more efficient.

Code 5.5: Highlighting elements after selecting them by label first.

label_zu_idx_elemente = ErstelleLabelsortierteGeomlist(geomliste=elemente);
elems = LabelAuswahl(elemente=elemente, labelliste=labelliste,
   elementhilfsliste=label_zu_idx_elemente);
highlight(elems);

5.3 Position and Coordinates

Usually, a selection is based on a geometric constraint like ”beneath a certain height“ or ”in a certain coordinate range“. Thus a selection can be done by extracting coordinates of all applicable objects and check if they satisfy the constraint.

Nodes simply have a coordinate attribute, which can be used. (Mesh) elements do not have a coordinate attribute, but a connectivity attribute listing references to all the nodes defining them. So by accessing its nodes, the coordinates of elements can also be obtained.

In a model database, the connectivity attribute of an element refers to the indices of the nodes, in an output database the connectivity attribute refers to the labels of the nodes!

Different from nodes, construction elements created in the part module (vertices, edges, faces and cells) have a pointOn attribute to retrieve their position. The return value of a pointOn only matches the exact position for vertices. For all other elements, the return values is just somewhere on the edge/face/within the cell (corners and boundary edges included). Abaqus provides a findAt() function to query which geometry an entitiy at the given location belongs to (a simple example is given in section 6.2.2).

5.4 Conditional Selection

One of the most important conditional selection functions is BedingteAuswahl(). It needs a collection of objects passed to elemente and a condition passed as a logical expression (bedingung). Additional variables can be passed as a list to the optional argument var and might be referenced in the conditions by var[0], var[1], …

Only certain keywords are allowed as condition and each single member of elemente is referenced as elem.12 Code 5.6 can be used to select all nodes from punkte in a given volume x<0.3x < 0.3, y<0.3y < 0.3 and z>0.25z > -0.25 (cf. figure 5.2).

Code 5.6: Conditional selection of nodes.

ausgewaehlte_punkte = BedingteAuswahl(elemente=punkte,
   bedingung='(elem.coordinates[0] < 0.3) and (elem.coordinates[1] < 0.3) and \
              (elem.coordinates[2] > -0.25)');
highlight(ausgewaehlte_punkte);

Although BedingteAuswahl() can obtain direct attributes, elements have no coordinates attached to them. Therefore a special function ElementAuswahl() can be used to select elements of a part/instance based on coordinates attached to their corresponding nodes. Consequently, the function needs a list of the nodes for those elements passed to punktliste.13 Elements will be selected only if all nodes of that element fullfill the given condition. The same selection as above but for elements instead of nodes inside the given volume can be done with code 5.7.

Code 5.7: Conditional selection of elements by geometric constraints.

ausgewaehlte_elemente = ElementAuswahl(elemente=elemente, punktliste=punkte,
   bedingung='(punkt.coordinates[0] < 0.3) and (punkt.coordinates[1] < 0.3) and \
              (punkt.coordinates[2] > -0.25)');
highlight(ausgewaehlte_elemente);

Figure 5.2: Conditional selection of a certain volume (here x<0.3x < 0.3, y<0.3y < 0.3 and z>0.25z > -0.25). Left hand side: selection of nodes with BedingteAuswahl(). Right hand side: selection of elements with ElementAuswahl().

The selection of labels as discussed in section 5.2 can also be done with BedingteAuswahl() and a suitable condition.

Code 5.8: Conditional selection of elements by label.

elems = BedingteAuswahl(elemente=elemente, bedingung='elem.label in var', var=labelliste);
highlight(elems);

Another possible use of a variable list is shown in code 5.9 with a tolerance for selection (in this case abapys_tol = 10610^{-6} is used).

Code 5.9: Another conditional selection using variables.

zellen = BedingteAuswahl(elemente=partBoden.cells, bedingung='(elem.pointOn[0][1] < var[0]+var[1])',
   var=[sand_hoehe, abapys_tol]);

5.5 Sorting by Direction

Some tasks might require a list of elements (or their labels/indices) sorted by a coordinate direction. By default, elements and other objects are not sorted (in any direction). abapys provides two functions KnotenAuswahlLabelliste() and ElementAuswahlLabelliste() for sorting labels of nodes or elements respectively based on a given coordinate direction.

Both functions have the following optional arguments with the given default values

KnotenAuswahlLabelliste() requires a sequence of nodes passed as knoten whereas the function ElementAuswahlLabelliste() requires elements and nodes (passed as elemente and punktliste respectively).

In the following example a column of elements is selected with ElementAuswahl() and the selected elements are sorted in descending zz-order with ElementAuswahlLabelliste() (see also figure 5.3). The result is a list of the element labels elem_sortiert sorted by vertical position of their elements.

Code 5.10: Selecting elements and sorting by coordinate direction.

elems = ElementAuswahl(elemente=elemente, punktliste=punktliste,
   bedingung='(punkt.coordinates[0] > 0.5) and (punkt.coordinates[0] < 0.75) and ' \
   '(punkt.coordinates[1] > 0.1) and (punkt.coordinates[1] < 0.4)');

label_zu_idx_elemente = ErstelleLabelsortierteGeomlist(geomliste=elemente);
elem_sortiert = ElementAuswahlLabelliste(elemente=elems, punktliste=punktliste, sortierung=2,
   aufsteigend=False);
for einzelelem in elem_sortiert:
   highlight(elemente[label_zu_idx_elemente[einzelelem]]);
   print(einzelelem);
Figure 5.3: Example of a column of elements which might need to be sorted from top to bottom. The labels of the elements selected by ElementAuswahl() are in increasing label order by default. ElementAuswahlLabelliste() can be used to sort them by their (vertical) position.

5.6 Direction Based Edge Selection

When meshing the model, some areas might need a finer mesh than others. Therefore, it may be useful to define an increasing/decreasing amount of seeds along certain edges. So the selected edges should also be differentiated based on the their direction (defined by the order of the end nodes). abapys provides a function called ZweifachbedingteKantenAuswahl() to select edges and sort them. In addition to elemente (which should be a collection of edges) and an optional var=[] (same as in BedingteAuswahl()) three conditions can be provided:

All three conditions can access variables passed in var by their index (as the condition in BedingteAuswahl()). In the following example all edges along the yy-direction on a part referenced by partBoden are selected, if their xx coordinate is more than entfernung away from the origin. All selected edges pointing inward (xx-coordinate of the start node is greater than the xx-coordinate of the end node) are returned in the first list, all pointing outward in the second list.

Code 5.11: Selecting edges and sorting them by direction.

kanten_innen, kanten_aussen = ZweifachbedingteKantenAuswahl(elemente=partBoden,
   bedingung1='(edge.pointOn[0][0] > var[0]-var[1])',
   bedingung2='(abs(vert1.pointOn[0][1]-vert2.pointOn[0][1]) < var[1])',
   bedingung3='(vert1.pointOn[0][0] > vert2.pointOn[0][0])',
   var=[entfernung, abapys_tol]);

6 Model Creation Support

6.1 Model Access and Initialisation

Similar to opening and accessing an output database (see section 4.1), model databases can be created and accessed. Accessing an existing model database can be done by entering

Code 6.1: Assigning model database to variable.

mymodel = mdb.models[<modelname>];

To create a new model, choose a suitable model name and calling the Abaqus function mdb.Model(). Afterwards, the model can be referenced by its name as shown in code 6.2.

Code 6.2: Create and access a new model database.

modelname = '<modelname>';
mdb.Model(name=modelname, modelType=STANDARD_EXPLICIT);
mymodel = mdb.models[modelname];

6.2 Creating a Part

abapys provides functions to create simple geometric parts, (screw) piles and a soil body.

6.2.1 Simple Predefined Parts

abapys has three functions to create very simple parts, namely

All three functions require a model reference modell, a name and geometric values like laenge, breite, hoehe and radius. They also need a basic mesh size gittergroesse and a type of material (usually materialtyp=DEFORMABLE_BODY). The functions return a reference on the created part and instance. Three example calls are shown in code 6.3.

Code 6.3: Creating simple geometric parts.

[partRad, instRad] = Zylinder(modell=mymodel, name='Rad', radius=0.6, hoehe=0.3,
   materialtyp=DEFORMABLE_BODY, gittergroesse=0.2);
[partBasis, instBasis] = Quader(modell=mymodel, name='Basis', laenge=4.0, breite=2.0, hoehe=1.5,
   materialtyp=DEFORMABLE_BODY, gittergroesse=0.2);
[partGelenk, instGelenk] = Kugel(modell=mymodel, name='Gelenk', radius=0.3,
   materialtyp=DEFORMABLE_BODY, gittergroesse=0.2);

Quader() has the following optional arguments with the given default values

The Zylinder() function has an additional optional argument r_innen=[]. A filled cylinder will be created by default, but if a value is given to r_innen (instead of an empty list) a hollow cylinder with a that inner radius is created.

Kugel() has the optional arguments rp=False, extrasets=False and r_innen=[]. While both first arguments have the same effect es described for Quader(), the last argument is similar to the optional argument of Zylinder() but would create a hollow sphere instead.

6.2.2 Parts from Sketches

Usually, it is necessary to create more complicated parts. If a part can be described by a simple drawing/sketch, Abaqus can extrude or revolve a part based on the geometries defined in that sketch.

In code 6.4 the creation of the sketch is done in line 6 and the extrusion to a part in line 14 (see also figure 6.1). Everything in between is defining the sketch geometry. As shown, it might be preferable to define all necessary parameters beforehand (here line 1–4) to build a fully parametrized model.

Although Abaqus has some rather straightforward commands to draw a single straight or curved line segment, abapys provides some improved commands for easier sketching (and the possibility to automatically add measurements): Linie(), Linienzug(), Rechteck(), Kreis(), KreisbogenWinkel() and KreisbogenPunkte(). They can be combined in any meaningful way to create a closed sketch.

Code 6.4: Creating a part based on a custom sketch.

laenge = 0.4;  # [m]
breite = 0.3;  # [m]
hoehe  = 0.25; # [m]
h_aussparung = 0.05; # [m]

zeichnung = mymodel.ConstrainedSketch(name='_profil_', sheetSize=max(laenge, breite));
Linienzug(zeichnung=zeichnung, punkte=[
   (0.0, 0.0),
   (breite/2.0, -h_aussparung),
   (breite/2.0, hoehe-h_aussparung),
   (-breite/2.0, hoehe-h_aussparung),
   (-breite/2.0, -h_aussparung)], geschlossen=True);
partBauteil = mymodel.Part(dimensionality=THREE_D, name='Bauteil', type=DEFORMABLE_BODY);

partBauteil.BaseSolidExtrude(depth=laenge, sketch=zeichnung);
del zeichnung;
Figure 6.1: The sketch resulting from code 6.4.

All of abapys’ sketch commands have an optional argument bemasst=True to add a measurement to the drawing. Linienzug() also has an optional argument geschlossen=False which can be set to True to connect the first given point in punkte to the last. KreisbogenWinkel() and KreisbogenPunkte() require a direction argument for richtung which can either be CLOCKWISE or COUNTERCLOCKWISE. If the sketch should be revolved instead of extruded, the revolution axis has to be included with a fixed constraint (line 1 and 2 of code 6.5) and the extrusion command has to be replaced with a revolution command.

Code 6.5: Changes to create a part by revolution instead of extrusion.

zeichnung.ConstructionLine(point1=(0.0, -1.0), point2=(0.0, 1.0));
zeichnung.FixedConstraint(entity=zeichnung.geometry.findAt((0, 0),));
# Draw commands for the relevant geometry
partBauteil.BaseSolidRevolve(angle=360.0, flipRevolveDirection=OFF, sketch=zeichnung);

Whenever geometries need to be selected, use the findAt() function instead of trying to access objects by their index number. Indices might change, but positions stay the same.

Example: Parametrised Sketch

As demonstrated in code 6.4, it is recommended to use variables instead of fixed values within the draw commands. Variables with meaningful names or relatable symbols14 are easier to understand than numbers and have to be defined only once in a script (e. g. near the beginning). This can be very helpful when dealing with more complex geometries.

As an example consider the HEA beam in figure 6.2 (left hand side), which is still a rather simple geometry. The geometry can be described by five parameters (as shown in the right hand side of the same figure) and code 6.6. It is assumed that the sketch is referenced by zeichnung as before.

Figure 6.2: Example of an HEA beam. Left hand side: Construction sketch with units in millimetres. Right hand side: Same sketch adjusted for a parametrized model.

Code 6.6: Parametrised sketch for HEA beam.

l = 0.4;   # [m]
b = 0.45;  # [m]
f = 0.02;  # [m]
s = 0.015; # [m]
r = 0.02;  # [m]

Linienzug(zeichnung=zeichnung, punkte=[
   (-s/2.0-r,       -b/2.0+f),
   (-l/2.0,         -b/2.0+f),
   (-l/2.0,         -b/2.0),
   (l/2.0,          -b/2.0),
   (l/2.0,          -b/2.0+f),
   (s/2.0+r,        -b/2.0+f)]);
KreisbogenPunkte(zeichnung=zeichnung,
   mittelpunkt=(s/2.0+r, -b/2.0+r+f),
   punkt1=(s/2.0+r, -b/2.0+f),
   punkt2=(s/2.0, -b/2.0+r+f), richtung=CLOCKWISE);
Linie(zeichnung=zeichnung, punkt1=(s/2.0, -b/2.0+r+f),
   punkt2=(s/2.0, b/2.0-r-f));
KreisbogenPunkte(zeichnung=zeichnung,
   mittelpunkt=(s/2.0+r, b/2.0-r-f),
   punkt1=(s/2.0, b/2.0-r-f),
   punkt2=(s/2.0+r, b/2.0-f), richtung=CLOCKWISE);
Linienzug(zeichnung=zeichnung, punkte=[
   (s/2.0+r,        b/2.0-f),
   (l/2.0,          b/2.0-f),
   (l/2.0,          b/2.0),
   (-l/2.0,         b/2.0),
   (-l/2.0,         b/2.0-f),
   (-s/2.0-r,       b/2.0-f)]);
KreisbogenPunkte(zeichnung=zeichnung,
   mittelpunkt=(-s/2.0-r, b/2.0-r-f),
   punkt1=(-s/2.0-r, b/2.0-f),
   punkt2=(-s/2.0, b/2.0-r-f), richtung=CLOCKWISE);
Linie(zeichnung=zeichnung, punkt1=(-s/2.0, b/2.0-r-f),
   punkt2=(-s/2.0, -b/2.0+r+f));
KreisbogenPunkte(zeichnung=zeichnung,
   mittelpunkt=(-s/2.0-r, -b/2.0+r+f),
   punkt1=(-s/2.0, -b/2.0+r+f),
   punkt2=(-s/2.0-r, -b/2.0+f), richtung=CLOCKWISE);

6.2.3 Special Pile Parts

abapys also includes three functions to create predefined pile parts:

Like the basic part generartion functions described in section 6.2.1, all three functions return a reference on the created part and instance. The (geometric) arguments required for these functions are described visually in figure 6.3 (except rundwinkel). Other required arguments (like modell and name) are the same as for the other basic part generartion functions (gitter_werkzeug corresponds to gittergroesse).

Figure 6.3: The values needed for the pile creation functions. Left hand side: Bohrprofil_VBP(). Center: Bohrprofil_SOBP(). Reft hand side: Bohrprofil_VVBP().

Code 6.7: Example of creating special pile parts.

[partVBP, instVBP] = Bohrprofil_VBP(modell=mymodel, name='Verdraengungsprofil',
   laenge=10.0, r_aussen=0.2, spitzenwinkel=45.0, rundwinkel=0.0, gitter_werkzeug=0.1);

[partSOBP, instSOBP] = Bohrprofil_SOBP(modell=mymodel, name='Schneckenprofil', laenge=10.0,
   r_aussen=0.2, r_innen=0.12, spitzenwinkel=45.0, rundwinkel=0.0, schraublaenge=7.0, ganghoehe=0.3,
   wendeldicke=0.05, gitter_werkzeug=0.1);

[partVVBP, instVVBP] = Bohrprofil_VVBP(modell=mymodel, name='Vollverdraengungsbohrprofil',
   laenge=10.0, r_aussen=0.2, r_innen=0.12, spitzenwinkel=45.0, rundwinkel=0.0, schraublaenge1=0.45,
   profillaenge1=0.2, schraublaenge2=0.25, profillaenge2=0.2, laenge12=0.16, ganghoehe=0.16,
   wendeldicke=0.015, gitter_werkzeug=0.1);

Additionally 00^\circ \leq spitzenwinkel <90< 90^\circ and 00^\circ \leq rundwinkel 902|45\leq 90^\circ-2\cdot|45^\circ-spitzenwinkel|| to have a coherent geometry. The rundwinkel argument can be used to create a concave pile tip (see also figure 6.4) or set to zero for a linear slope to the tip (rundwinkel=0.0).

Figure 6.4: Effect of angles spitzenwinkel and rundwinkel on the shape of the pile tip. Left hand side: different rundwinkel for spitzenwinkel equal to 45^\circ. Center: different spitzenwinkel (no tip for 0^\circ). Right hand side: maximum values of rundwinkel for the four values of spitzenwinkel shown in the center image.

6.3 Constraints with Connectors

Abaqus provides connectors and constraints to control/restrict the movement of certain instances relative to each other. For example a wheel can be restricted to only rotate around an axis by defining a hinge connector on the wheel with regard to this axis with DrehscharnierErstellen(). Therefore, next to a model reference (modell) and a name, a coordinate system kos defining the axis around which rotation is allowed (xx-axis of coordinate system) and two reference points punkt1 and punkt2 are needed. Both reference points should be attached to instances/sets rotating relative to each other.

In the following example the reference point of a part/instance called partBasis/instBasis and a reference point of the assembly (RP_Rad) are only allowed to rotate around the xx-axis of a given coordinate system (called KoordSystemRotation and attached to the same part/instance).

Code 6.8: Constraining geometry to only rotate around a given axis.

DrehscharnierErstellen(modell=mymodel, name='Wire00',
   punkt1=mymodel.rootAssembly.referencePoints[mymodel.rootAssembly.features['RP_Rad'].id],
   punkt2=instBasis.referencePoints[partBasis.features['RP'].id],
   kos=instBasis.datums[partBasis.features['KoordSystemRotation'].id]);

Similarly a translate connector can be defined to allow only movement along one axis (xx-axis) with AxiallagerErstellen().

Code 6.9: Constraining geometry to only move along a given axis.

AxiallagerErstellen(modell=mymodel, name='WireAxiallager',
   punkt1=instBasis.referencePoints[partBasis.features['RP'].id],
   punkt2=instBohrprofil.referencePoints[partBohrprofil.features['RP'].id],
   kos=instBasis.datums[partBasis.features['KoordSystemRotation'].id]);

Note that RP_Rad for DrehscharnierErstellen() in code 6.8 is defined in the assembly while the reference point for the example to AxiallagerErstellen() (code 6.9) is defined in the part itself (both options are available for both functions/arguments).

To define new reference points in the assembly and fix them to any geometry, the function ReferenzpunktErstellenUndKoppeln() can be used. It needs the coordinates of the reference point (punkt), its name and geometry to connect to (flaeche).

Code 6.10: Creating and coupling a reference point.

ReferenzpunktErstellenUndKoppeln(modell=mymodel, punkt=(1.5, 1.0, 0.0), name='Rad',
   flaeche=instRad.sets['setAll']);

In general, the Abaqus commands to simply create a reference point or coordinate system are similar to the following.

Code 6.11: Creating a reference point and a coordinate system.

partBasis.ReferencePoint(point=(0.0, 0.0, 0.0));
partBasis.DatumCsysByThreePoints(coordSysType=CARTESIAN, name='KoordSystemRotation',
   origin=partBasis.referencePoints[partBasis.features['RP'].id],
   point1=(0.0, 1.0, 0.0), point2=(1.0, 0.0, 0.0));

6.4 Soil Body

Most geotechnical simulations are done to investigate a soil-structure interaction. Therefore, a soil body with the most relevant properties has to be modelled at some point. With abapys it is easy to create a simple (cuboid or cylindrical) soil body with different layers of materials and automatically calculate and assign the initial earth pressure for each layer. To achieve a good result, it is necessary to have all used materials present in the material database (the Materialdatenbank_20####.xlsx-file in the abapys directory) and properly define the soil body geometry.

The main function to create a soil body is introduced in section 6.4.2 but most relevant parameters are already listed in code 6.12 (see also figure 6.5) and introduced here:

Code 6.12: Example of how soil body parameters could be defined.

bodentiefe,   voidhoehe,   gitter_vertikal = [
#>0           >0          >0
#[m]          [m]         [m]
#-----------|-----------|------------------|
 1.500      , 0.500     , 0.1
#-----------|-----------|------------------|
];

bodenbereich,                                     gittergroessen = [
#[radius] oder [laenge/2, breite/2]               [konstant] oder [gross, klein]
#[m] (aussen->innen)                              [m] (aussen->innen)
#-----------------------------------------------|--------------------------------------------------|
 [[1.5, 1.0], [1.0, 1.0], [0.25], [0.2]]        , [[0.4], [0.4, 0.1], [0.05], [0.05]]
#-----------------------------------------------|--------------------------------------------------|
];

schichten,     schichtmaterial,            restmaterial = [
#>[0]          >['']                       >''
#[m]
#------------|---------------------------|--------------|
 [0.5, 1.5]  , ['Auffuellung', 'Sand']   , 'Sand'       #
#------------|---------------------------|--------------|
];

Figure 6.5: Left hand side: Side view on soil body. The void volume is only created for an Eulerian soil body and ignored otherwise (like here). Center: Soil body example. Right hand side: Top view on soil body.

6.4.1 Material Assignment

All materials used in schichtmaterial and restmaterial shown in code 6.12 have to be defined and reference to materials in the material database. They link internally chosen names with database entries and take density, saturation with water (concerning density) and used constitutive model into account when calculating the initial earth pressure. Code 6.13 illustrates how two materials can be defined.

Code 6.13: Defining two soil materials.

materialien_boden = [
#   Abaqus-Bez.   Datenbankname        Parameter-Bez.   Saettigung   Lagerungsd.   Stoffgesetz
#   >''           >''                  ''               [0-1]        [0-1]         >''
# |-------------|--------------------|----------------|------------|-------------|----------------|
  ['Auffuellung', 'Standardparameter', ''             , 0.0        , 0.4         , 'Mohr-Coulomb' ],
  ['Sand'       , 'Hamburger Sand'   , ''             , 1.0        , 0.5         , 'Mohr-Coulomb' ],
# |-------------|--------------------|----------------|------------|-------------|----------------|
# Die Lagerungsdichte/Verdichtungsgrad kann bestimmt werden aus
#   a) Anfangsdichte rho_0:     I_r = (rho_0 - rho_min)/(rho_max - rho_min)
#   b) Anfangsporenzahl e_0:    I_e = (e_max - e_0)/(e_max - e_min)
];

The definitions in code 6.12 and 6.13 can be used with the parameters from the material database to define materials with BodenmaterialUndSectionErstellen() (see code 6.14).

Code 6.14: Calling BodenmaterialUndSectionErstellen() loads all relevant material parameters.

verwendeteMaterialien = sorted(set(schichtmaterial + [restmaterial]));
benoetigtUserroutine, verwendeteBodenwerte = BodenmaterialUndSectionErstellen(modell=mymodel,
   verwendeteMaterialien=verwendeteMaterialien, verfuegbareMaterialien=materialien_boden,
   euler=False);

BodenmaterialUndSectionErstellen() has the following optional arguments with the given default values

6.4.2 Soil Body Creation Function

After defining all parameters, they can be passed to the Boden() function to actually create the soil body (see code 6.15). This function also has the following optional arguments:

Code 6.15: Calling function Boden() to create the soil body with custom parameters.

[partBoden, instBoden] = Boden(modell=mymodel, name='Boden', bodentiefe=bodentiefe,
   voidhoehe=voidhoehe, bodenbereich=bodenbereich, gittergroessen=gittergroessen,
   gitter_boden_vertikal=gitter_vertikal, schichten=schichten, schichtmaterial=schichtmaterial,
   restmaterial=restmaterial, euler=False, xypartition=[True, True], partition_durchziehen=True,
   viertel=4);

When changing the parameters, a variety of soil bodies can be produced. Two examples are shown in figure 6.6 with the geometric parameters shown in code 6.16 and 6.17 respectively.

Code 6.16: Parameters for soil body creation (left hand side of figure).

bodentiefe = 10.0;
voidhoehe = 1.5;
bodenbereich = [[4.0, 3.0], [1.0, 1.0],
                [0.25, 0.25]];
gittergroessen = [[1.0], [0.2], [0.1]];
gitter_vertikal = 0.1;
schichten = [0.5, 5.0];
# euler = True;
# partition_durchziehen = True;

Code 6.17: Parameters for soil body creation (right hand side of figure).

bodentiefe = 1.2;
voidhoehe = 0.0;
bodenbereich = [[0.2], [0.05]];
gittergroessen = [[0.08, 0.015], [0.015]];
gitter_vertikal = 0.02;
schichten = [0.8];
# euler = False;

Figure 6.6: Two variations of soil bodies created with Boden() function. The multiple adjustable parameters allow for many possibilities. Left hand side: Created with parameters shown in code 6.16. Right hand side: Created with parameters shown in code 6.17.

After a soil body has been created, each a material has to be assigned to each section. For Lagrange soil bodies, this can be done with code 6.18, for Eulerian soil bodies the section assignment does not require any loops and looks similar to code 6.19.

Code 6.18: Assigning sections on a Lagrangian body.

for materialdaten in materialien_boden:
   partBoden.SectionAssignment(offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE,
      region=partBoden.sets['set' + materialdaten[0]], sectionName='sec' + materialdaten[0],
      thicknessAssignment=FROM_SECTION);

Code 6.19: Assigning sections on a Eulerian body.

partBoden.SectionAssignment(offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE,
   region=partBoden.sets['setAll'], sectionName='secEuler', thicknessAssignment=FROM_SECTION);

If a structure interacting with the soil body is positioned above the soil material, there is no restriction on its geometry. If the structure is already positioned within the soil body (i. e. wished-in-place), either an Eulerian soil volume (with Abaqus/CEL) or an axisymmetric structure with a Lagrangian soil volume (and an appropriate sketch passed to rotationsprofilpunkte) is strongly recommended when using Boden().

To automatically create a discrete field of all elements of an Eulerian body containing no/partial material due to the presence of another geometry, the Eulerian Volume Fraction tool can be used. Either this field or any sets of elements can be used to create empty elements by assigning only material to where it should be.

Wished-in-place non-axisymmetric structures within a Lagrangian soil volume are not supported with Boden() and should be created manually.

6.4.3 Soil Stress

After a soil body is created (e. g. with Boden()), the earth pressure can be calculated automatically for each soil layer depending on the properties of each material, the vertical position and the weight of all layers above this layer. Simply invoke BodenspannungErstellen() as shown in code 6.20. BodenspannungErstellen() automatically creates a stress distribution with the Abaqus function GeostaticStress() for each soil layer. The optional argument verbose=False can be set to True to output a table with the soil parameters.

Code 6.20: Creating soil stress for each layer with BodenspannungErstellen().

BodenspannungErstellen(modell=mymodel, bodenname='Boden', nullspannung=0.0, voidhoehe=voidhoehe,
   schichten=schichten, bodentiefe=bodentiefe, materialschichten=Materialschichtliste,
   verwendeteBodenwerte=verwendeteBodenwerte, verwendeteMaterialien=verwendeteMaterialien);

Although this should cover most situations, if stresses are to be assigned explicitly, a function BodenspannungDirektZuweisen() can be used. This function does not support materialschichten, verwendeteBodenwerte and verwendeteMaterialien but expects a custom list of densities of all soil layers passed to bodendichten and earth pressure coefficients passed to k0Werte.

Both functions are designed to work with the presented workflow and output of Boden(). If they are to be used separately, certain sets are expected to be present in the soil part.

7 Setting and Transfering States

In some cases it might be necessary to set an initial solution for a set of nodes/elements ahead of the simulation. Since initial solutions are not supported in the Abaqus/CAE environment (except in the keyword editor), this is usually done directly in the input file after model creation. The following functions were developed for triangles and quadrilateral elements (2D) as well as three-dimensional elements like tetrahedrons and hexahedrons (other elements should not be expected to work). The following examples use the model described in section 4.1.

7.1 Constant Initial Values for Solution Dependent Variables

With KonstanteAnfangsloesungFuerSet() it is possible to set initial values for solution dependent variables (SDV) before creating an input file. Constant values are applied to the specified set (setname) of the given instance (instname). The following code sets the first SDV to 0.7 and the next nine SDVs to zero for all elements in setAll in instance instBoden.

Code 7.1: Initialising SDVs in a set with constant values.

KonstanteAnfangsloesungFuerSet(modell=mymodel, instname='instBoden', setname='setAll',
   ausgabewerte=[0.7] + [0.0 for x in range(9)]);
Figure 7.1: Constant initial values for the SDV within all elements of the soil volume.

KonstanteAnfangsloesungFuerSet() only works for solution dependent variables (SDV) of user routines.

7.2 Transfer Values from an Output Database

It is more common to transfer selected results of an existing output database as initial solutions to a new model. Abaqus provides functionalities to transfer results if the same model is used.

If a different model with comparable geometry is used (e. g. coarser model, submodel or different meshing) and the geometric positions and dimensions are similar enough, the function Zustandsuebertragung() can be called directly. The following code can be used to transfer the stresses from an instance INSTBODEN in the output database named example.odb to an instance instBoden in the new model.

Code 7.2: Transfering the state from an output database to a model database.

Zustandsuebertragung(session=session, odbname='example.odb', odbinstname='INSTBODEN',
   variablenliste=['S'], modell=mymodel, mdbinstname='instBoden');

By default, the results from the last frame in the last step are used in Zustandsuebertragung(). This can be changed by defining the number of another step/frame with the optional arguments step and frame. The results to transfer are defined in variablenliste (S, SVAVG, U and SDV were tested).

Transformations with Zustandsuebertragung() are not optimized for accuracy. Excessive repeated usage with different meshes might degrade the results.

Occasionally the output database and the similar new model might have a different absolute position/offset. To transfer states between such models a coordinate transformation has to be applied (i. e. by using Koordinatentransformation() and passing the target coordinates to the optional mdbknoten-argument of Zustandsuebertragung()15). Code 7.3 shows an example transformation for the model described above (shifted by 7 units in xx-direction and -2 units in yy-direction).

Code 7.3: Transforming the reference coordinates for the state transfer.

mdbknoten = Knotentransformation(xneu='x+7', yneu='y-2',
   punktliste=mdb.models[<modelname>].rootAssembly.instances['instBoden'].nodes);
Zustandsuebertragung(session=session, odbname='<odb-file>', odbinstname='INSTBODEN',
   variablenliste=['S'], modell=mymodel, mdbinstname='instBoden', mdbknoten=mdbknoten);

Although it is possible to scale/rotate coordinates with Knotentransformation(), this will only affect the position (which results are assigned to which element/node). The values at those positions will NOT be changed in any way which is especially important for tensor values.

Figure 7.2: Initial state transferred from the output database of a similar model.

7.3 Assign Arbitrary Initial Values

It is possible to define initial solutions on a 3D grid (regularly meshed hexahedron) and transfer those values to any model. Therefore, three variables zielkoordinaten, zielelemente and zielwerte have to be provided to assign an arbitrary state with Zustandszuweisung() (most of the other arguments are similar to Zustandsuebertragung()). zielkoordinaten contains all point coordinates and each list in zielelemente referes to the indices of the coordinates for the corresponding element. zielwerte represents the initial values which can be either for elements (if it has the same amount of values as zielelemente) or for the nodes (it must have the same amount of values as zielkoordinaten).

Node values like U or S have to be defined per node and element values like SDV per element (theoretically per integration point but a simplified approach is used here).

An example is shown in code 7.4. Like Zustandsuebertragung(), Zustandszuweisung() also allows for a coordinate transformation (e. g. with Koordinatentransformation()) and passing the transformed target coordinates to the optional mdbknoten-argument. The default variables are SDV as defined in the optional argument variablentyp=['SDV']. The values for zielkoordinaten, zielelemente and zielwerte are expected to be saved in a csv file (with ascending coordinate directions). The csv file is read and parsed with ZielwertquaderEinlesen().

Code 7.4: Assigning an initial state as defined in file.

[knoten, elemente, zielwerte] = ZielwertquaderEinlesen(dateiname='zielwertzuweisung.csv',
   numKoordinaten=3, numVar=20, knotenwerte=False);
Zustandszuweisung(session=session, modell=mymodel, zielkoordinaten=knoten, zielelemente=elemente,
   zielwerte=zielwerte, mdbinstname=instBoden.name);
Figure 7.3: Example of assigning initial values from a 3D grid saved in a csv file.

ZielwertquaderEinlesen() expects numKoordinaten=2 for 2D elements with 4 nodes or numKoordinaten=3 for 3D elements with 8 nodes. numVar represents the amount of initial values (which might be greater than the amount of values stored in the file, but not smaller. Missing values will be initialised as 0). The optional argument knotenwerte=False assumes values defined for elements (i. e. at integration points) or at the nodes (if True).

The csv file for Zustandszuweisung() can be created with any program. abapys provides a function called ZielwertquaderErstellen() to handle all situations where a generator function for all initial values can be stated explicitly. A list of increasing values has to be specified for each coordinate direction (and passed to xwerte, ywerte and zwerte respectively). ZielwertquaderEinlesen() also expects a generator function to be passed to ergebnisfunktion.

Code 7.5: Creating initial values and saving them to a file.

xwerte = [round(5*x)/100.0 for x in range(11)] + [0.56, 0.67, 0.75, 0.84, 0.94] \
   + [round(105 + 10*x)/100.0 for x in range(6)];
ywerte = [round(5*x)/100.0 for x in range(11)] + [0.56, 0.67, 0.75, 0.84, 0.94, 1.05];
zwerte = [-round(5*x)/100.0 for x in range(11)] + [-0.56, -0.67, -0.75, -0.84, -0.94] \
   + [-round(105 + 10*x)/100.0 for x in range(6)];

ZielwertquaderErstellen(dateiname='zielwertzuweisung.csv', xwerte=xwerte, ywerte=ywerte,
   zwerte=zwerte, ergebnisfunktion=Anfangswertverteilung);

For this example the following user defined function Anfangswertverteilung() (shown in code 7.6) is used.

Code 7.6: Example function to calculate initial values based on coordinates.

def Anfangswertverteilung(punktkoordinaten):
   """Erwartet drei Koordinaten, um daraus einen Funktionswert zu berechnen. In dieser oder einer
   gleichwertigen Funktion kann die gewuenschte Verteilung von beliebigen Anfangszustaenden fuer
   ein Modell definiert werden, die mit ZielwertquaderErstellen() in eine Datei geschrieben werden.
   """
   from math import sqrt, exp
   import random
   #
   ausgangswert = 0.7;
   verdichtung = -0.1;
   schwankung = 0.05;
   #
   referenz_punkt = [0.0, 0.0, 0.0];
   referenz_entfernung = 1.0;
   entfernung = sqrt(sum([(punktkoordinaten[idx] - referenz_punkt[idx])**2 \
      for idx in range(len(punktkoordinaten))]));
   #
   funktionswert = random.gauss(mu=ausgangswert + verdichtung*exp(-entfernung/referenz_entfernung),
      sigma=(3.0*schwankung)**2);
   return [round(funktionswert*1000.0)/1000.0];
#

8 Querying Meshed Element Data

Sometimes specific information should be obtained like the volume of elements/parts/instances, which element contains a specified point coordinate and how a point within an element is weighted by the element nodes. Those tasks concerning meshed elements can also be accomplished with abapys. Since it can be of interest for model databases as well as output databases, most of the following functions work for both situations.

8.1 Volume of Elements

The volume of single elements16 can be obtained with ElementVolumen() by passing the point coordinates to the punkte argument (and dimensionen=3 for 3D elements or dimensionen=2 for 2D elements). By iterating over all elements (and getting their node coordinates with PunktkoordinatenVonElement()) the cumulative volume of all elements can be determined (as shown in code 8.1).

PunktkoordinatenVonElement() requires a list of all nodes passed to knoten, the investigated element containing its node labels (element) and a label-index mapping either with ErstelleLabelsortierteGeomlist() (for output databases) or with a simple generated list for model databases passed to listenhilfe. instBoden is an instance reference of either a model database or an output database.17

Code 8.1: Calculating the volume of an instance.

listenhilfe = [idx for idx in range(len(instBoden.nodes))];   # mdb (indices instead of labels)
listenhilfe = ErstelleLabelsortierteGeomlist(geomlist=punkte) # odb
vol = 0.0;
for element in instBoden.elements:
   punkte = PunktkoordinatenVonElement(element=element, knoten=instBoden.nodes,
      listenhilfe=listenhilfe);
   vol += ElementVolumen(punkte=punkte, dimensionen=3);

For parts in a model database the whole volume can easily be determined with PartVolumen() as shown in code 8.2.

Code 8.2: Calculating the volume of a part.

volBoden = PartVolumen(part=partBoden);

8.2 Element Containing a Certain Point

Sometimes it might be handy to find out which element contains a certain point. abapys provides a function called PunktInElement(), which returns the label of the element. All relevant elements have to be passed as argument to elemente, their nodes to knoten and the coordinates of the point of interest to referenzpunkt.

A simple example is shown in code 8.3 and the result in figure 8.1. If PunktInElement() is called more than once, a label-index mapping should be provided to the optional argument listenhilfe=[].

Code 8.3: Finding and highlighting an element containing a given point.

refpunkt = (0.4, 0.4, -0.4);
elemente = session.odbs[<odbname>].rootAssembly.instances[<instname>].elements;
knoten = session.odbs[<odbname>].rootAssembly.instances[<instname>].nodes;

label_zu_idx_elemente = ErstelleLabelsortierteGeomlist(geomliste=elemente);
label_zielelement = PunktInElement(elemente=elemente, knoten=knoten, referenzpunkt=refpunkt);
ziel_element = element[label_zu_idx_elemente[label_zielelement]];
highlight(ziel_element);
Figure 8.1: Highlighted element containing the given reference coordinates.

If the reference coordinate is exactly on the surface/edge/node between two or more elements, only one of the matching elements is returned.

8.3 Weighting of Element Nodes on a Point

The weighting of a point can be determined by KnotengewichtungInElement() if the element containing the point is known. The function returns the nodes of the given element and the weighting (0 to 1) i. e. how much node is contributing to this point. The center point has a weighting of 1/(number of nodes) for each node, e. g. eight times 0.125 for hexahedral elements with eight nodes. The following example uses the variables/results from the last code snippet (if called more than once, label-index mapping should be provided to the optional argument listenhilfe=[].)

Code 8.4: Determining the weighting of element nodes on a point.

zielknoten, zielgewichtung = KnotengewichtungInElement(element=ziel_element, referenzpunkt=refpunkt,
   knoten=knoten);

If coordinates of the nodes are already known/given, it is also possible to use a similar function KnotengewichtungPunktInPunktkoordinaten(). Neither element nor node list is required since all coordinates are given as a list to punkte. Continuing from the previous example, the coordinates can be extracted as shown in code 8.5.

Code 8.5: Extracting the coordinates of an elements' nodes.

label_zu_idx_knoten = ErstelleLabelsortierteGeomlist(geomliste=knoten);
punktkoordinaten = PunktkoordinatenVonElement(element=ziel_element, knoten=knoten,
   listenhilfe=label_zu_idx_knoten);

Now the weighting can be determined with KnotengewichtungPunktInPunktkoordinaten() as shown in code 8.6.

Code 8.6: Determining the weighting of point coordinates on a point.

zielgewichtung = KnotengewichtungPunktInPunktkoordinaten(punkte=punktkoordinaten,
   referenzpunkt=refpunkt, dimensionen=3);

9 Output Processing

Output processing is a common task after each simulation and abapys provides functions for visualizing and exporting simulation results. Opening an output database is already described in section 4.1 and basic output printing in section 4.

9.1 Select Output Variables

Field Output variables can be selected by using the appropriate Abaqus commands. Code 9.1 can be used to display the output variable SVAVG33 (line 2–3) as contours on the deformed geometry (see also figure 9.1). The values of interest are set to the interval [500.0,0.0][-500.0, 0.0]. Since those Abaqus commands are simple and easy to adjust there are no substitute abapys functions.

Code 9.1: Selecting output variables.

myviewport.odbDisplay.display.setValues(plotState=(CONTOURS_ON_DEF, ));
myviewport.odbDisplay.setPrimaryVariable(variableLabel='SVAVG', refinement=(COMPONENT, 'SVAVG33'),
   outputPosition=INTEGRATION_POINT, );
myviewport.odbDisplay.contourOptions.setValues(minAutoCompute=OFF, minValue=-500.0,
   maxAutoCompute=OFF, maxValue=0.0);

Figure 9.1: Left hand side: Viewport after opening an odb file and setting the displayed output variable SVAVG33. Right hand side: Additional adjustments like ViewportVerschoenern() (described in section 4) and code 9.1.

9.2 Plot Output

In contrast to variable selection, plotting FieldOutput or HistoryOutput variables can be more cumbersome. There are some restrictions as hinted below, but much can already be achieved with the PlotOutput() function.

Plots can either be created for distinct elements by specifying their label(s) or whole element sets. When only defining yy-values, the variable is plotted over simulation time. An example for plotting the HistoryOutput is shown in code 9.2 and in figure 9.2.18 It is also possible to plot one OutputVariable over another (see code 9.3).

Code 9.2: Plotting HistoryOutput over time.

PlotOutput(session=session, odbname=odbname, yvar=(['instPfahl', 'RF3']),
   ylabel='Kraft in [kN]', xlabel='Zeit in [s]', titel='Krafteinleitung',
   legendeneintraege=['Pfahlkraft']);

Figure 9.2: Plotting an OutputVariable does the job (left hand side) but is visually not as appealing as the resuluts produced with PlotOutput(). It is also straightforward to plot one or multiple curves over each other with PlotOutput().

Although multiple data points can be chosen as yy-values, only one data set for xx-values is allowed.

Code 9.3: Plotting FieldOutput over another FieldOutput.

PlotOutput(session=session, odbname=odbname, yvar=(['instPfahl', 'RF3']),
   xvar=('U', 'U3'), xvarposition=('INSTPFAHL.SETRP'), xlabel='Weg in [m]',
   titel='Kraftverlauf', posxdir=False, legendeneintraege=['Pfahlkraft']);

Abaqus requires the xx-values to be monotonic increasing when plotting one OutputVariable over another.

It is possible to beautify the plot by specifying label descriptions, title and legend entries (to xlabel/ylabel, titel and legendeneintraege respectively). Defining custom legend entries internally calls PlotLegendeFormatieren(), which in turn applies additional modifications (line thickness and legend position). So it is recommended to always use custom legend entries for readability and consistent style.

PlotOutput() requires a reference to the current session, the name of the output database (odbname) and a definition of the variable to plot passed to yvar.

To plot FieldOutput data yvar has to be defined as (<variable_name>, <component>) and either yvarposition=(<odb_set_name>) (like in code 9.3) to plot the FieldOutput for all set elements or yvarposition=(<instance_name>, [<label(s)>]) (like in code 9.4) have to be provided. If xvar is also used as a FieldOutput, xvarposition has to be defined similar to yvarposition.

To plot HistoryOutput data, either use yvar=([<instance_name>, <variable_name>]) if the correct HistoryOutput can be identified or use yvar=(<output_name>) by specifying the complete output name. No position should be passed when using HistoryOutput data (i. e. yvarposition=[] if yvar represents a HistoryOutput). This also applies to xvar if used as a HistoryOutput.

PlotOutput() also has the following optional arguments with the given default values

Example: Plotting by Label

To plot the vertical stresses S33 of an element (with label 1108) of instance instBoden, the following PlotOutput() call can be used. Additionally, a label to the xx-axis and the yy-axis, a title and a legend entry are added. The result is shown in figure 9.3.

Code 9.4: Plot vertical stress at one element over time.

PlotOutput(session=session, odbname=odbname, yvar=('S', 'S33'), yvarposition=('instBoden', [1108]),
   ylabel='Spannung in [kN/m^2]', xlabel='Zeit in [s]', titel='Vertikalspannungsverlauf',
   legendeneintraege=['Element mit Label 1108']);
Figure 9.3: Stress over time on element labelled 1108.

9.3 Process Output Data

This section focuses on two different kinds of output data: FieldOutput/HistoryOutput data and XY data. FieldOutput/HistoryOutput data is created and saved in the output database if requested in the model definition. XY data can be generated by one or more FieldOutput/HistoryOutput data sets present in the output data base (and may contain less points in time and/or space).

Often FieldOutput/HistoryOutput data and/or XY data should be gathered and exported to continue processing it in another program (like Octave or Matlab). There are two different functions implemented to save FieldOutputs: FieldOutputSpeichern() directly takes a FieldOutput chunk for all defined output nodes/elements in a specified step and frame (see code 9.5).

Code 9.5: Save FieldOutput for all elements at one step/frame.

FieldOutputSpeichern(dateiname='spannung_eindruecken_10', session=session, odbname=odbname,
   fieldOutput='SVAVG', step=1, frame=10);

Alternatively a subset of the FieldOutput can be saved by generating XY data first. Using FieldOutputVorbereiten(), data from a FieldOutput can be collected for all steps and frames for certain points of interest (this is usually the right choice when the behaviour during the whole simulation is of interest). XYDatenSpeichern() can be used to save existing XY data. It requires the name of the XY data (e. g. output of FieldOutputVorbereiten()) passed to xydatenname as shown in code 9.6.

Code 9.6: Save FieldOutput at certain elements/sets for all steps/frames.

spannung_element = FieldOutputVorbereiten(session=session, odbname=odbname,
   var=['SVAVG', 'SVAVG33'], varposition=('INSTBODEN', [9951]));
XYDatenSpeichern(dateiname='spannung_9951', session=session, xydatenname=spannung_element[0].name);

There is an additional function XYDatenAnElementen() to extract XY data from certain elements. As a preparation, elements are selected by a coordinate range, sorted by zz-direction and their labels saved in elem_sortiert as described in code 5.10.

Now the relevant XY data can be extracted for one point in time with XYDatenAnElementen(). In code 9.7 the vertical stresses S33 at the beginning (zeitpunkt=0.0) and after 1.1 s are extracted. The second extracted XY data is saved in a csv file with XYDatenSpeichern().

Code 9.7: Extracting and saving XY data.

xydaten_anfang = XYDatenAnElementen(session=session, odbname=odbname, odbinstname=instname,
   labelliste=labelliste, zeitpunkt=0.0, var=['S', 'S33'], name='Vertikalspannung Anfang');
xydaten_ende = XYDatenAnElementen(session=session, odbname=odbname, odbinstname=instname,
   labelliste=labelliste, zeitpunkt=1.1, var=['S', 'S33'], name='Vertikalspannung Ende');
XYDatenSpeichern(dateiname='Ausgabe_S33', session=session, xydatenname=xydaten_ende.name);

Both extracted XY data sets (representing a spatial distribution of stresses at on point in time) are plotted with PlotXYDaten() as shown in code 9.8 and figure 9.4.

Code 9.8: Plotting XY data.

PlotXYDaten(session=session, xyListe=[xydaten_anfang, xydaten_ende],
   xlabel='Nummer vertikales Element', ylabel='Vertikalspannung',
   legendeneintraege=[xydaten_anfang.name, xydaten_ende.name]);
Figure 9.4: XY data for vertical stresses S33 for a selection of elements extracted at two points in time.

Example: Working with XY Data

Two examples of generating XY data from FieldOutputs at one point in time for a selection of nodes or elements can be seen in code 9.9 and 9.10. The resulting XY data can also be saved with XYDatenSpeichern() or plotted with PlotXYDaten() as shown before.

Code 9.9: Creating XY data for predefined nodes in ascending zz-direction.

instBoden = myodb.rootAssembly.instances['INSTBODEN'];
zielKnoten = BedingteAuswahl(elemente=instBoden.nodes,
   bedingung='(elem.coordinates[0] > var[0])', var=[0.2]);
sortierte_knotenlabels = KnotenAuswahlLabelliste(knoten=zielKnoten, sortierung=2, aufsteigend=True);
xydaten_knoten = XYDatenAnElementen(session=session, odbname=odbname, odbinstname='INSTBODEN',
   labelliste=sortierte_knotenlabels, zeitpunkt=0.05, var=('U', 'U3'), name='U3-Label');

Code 9.10: Creating XY data for predefined elements in descending yy-direction.

listenhilfe = ErstelleLabelsortierteGeomlist(geomliste=instBoden.nodes);
zielElemente = ElementAuswahl(elemente=instBoden.elements, punktliste=instBoden.nodes,
   listenhilfe=listenhilfe, bedingung='(punkt.coordinates[0] > punkt.coordinates[1])');
sortierte_elemlabels = ElementAuswahlLabelliste(elemente=zielElemente, punktliste=instBoden.nodes,
   sortierung=1, aufsteigend=False, listenhilfe=listenhilfe);
xydaten_elemente = XYDatenAnElementen(session=session, odbname=odbname, odbinstname='INSTBODEN',
   labelliste=sortierte_elemlabels, zeitpunkt=0.05, var=('SVAVG', 'SVAVG33'), name='SVAVG33-Label');

In some cases it might even be of use to create new FieldOutput data by combining existing ones. There is a special function called SkalarenFieldOutputErstellen() which needs a FieldOutput and operates on its data. As an example code 9.11 shows how to save the trace of the stress tensor as a FieldOutput variable.19

Code 9.11: Create a new FieldOutput by combining existing results.

SkalarenFieldOutputErstellen(name='Spur des Spannungstensors', session=session, odbname=odbname,
   referenzAusgabe='SVAVG', bedingung='data11+data22+data33');

10 Model Creation Template

In this chapter, a model similar to the one introduced in the »Abaqus Pile Jacking Tutorial« will be created. In contrast to the other tutorial, the focus here will be on starting with a template, i. e. general purpose structure for scripting, using exemplary code snippets when possible and adjusting them afterwards for a specific model. The resulting template is intended to be usable for various soil-structure interaction problems using a Coupled Eulerian Lagrangian approch in Abaqus/CAE.

This procedure might look burdensome when applied to a specific model. But the benefit of this structure will be more obvious when applied to more than one model.

Each part of the template is presented in detail in the following ten sections, which are divided like the structure of the template:

  1. Imports – might always be chosen as suggested in code 10.1,
  2. Parameter Definitions – should contain all modifiable parameters in one place to control the model. Usually, this will be the only part where changes are necessary in the script later on,
  3. Material and Contact Processing – for all used materials and contacts,
  4. Parts – Geometry, sets, meshes and section assignments for all parts,
  5. Assembly and Contact Applications – Positioning of the whole model,
  6. Initial and Boundary Conditions – Definition of the complete initial situation,
  7. Steps – Change in loads/movement divided in single steps,
  8. Postprocessing – Late adjustments of the model definition,
  9. Job – Job creation and
  10. Keyword Adjustments – Manual editing input file keywords.

10.1 Imports

Since the imports were already discussed in section 2.1 and 3.1, the beginning of the model creation template in code 10.1 should already look familiar.

Code 10.1: Imports and loading abapys for model creation.

# -*- coding: utf-8 -*-
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 *

abapys_dir = r'<X:\path\to\abapys>';

sys.path.insert(0, abapys_dir);
from abapys import *

InitialisiereAbapys(session=session, version=version, pfad=abapys_dir);

10.2 Parameter Definitions

The next part should contain all modifiable parameters for this model. Usually, all geometric variables, soil and material parameters, simulation parameters and version numbers can be found here. Since a model similar to the pile jacking model should be created, a soil part and a pile part are needed.

The relevant parameters/geometries for both parts are defined in code 10.210.4.20 For convenience, parameter unpacking can be used to easily change multiple values. If the hash sign in line 35 of code 10.2 would be removed (uncommented) and line 34 would be commented, all values for the variables in line 29 would be changed at once.21

Code 10.2: Soil geometry definition block (first part).

bodentiefe,   voidhoehe,  bodenbereich,             gittergroessen,      gitter_vertikal = [
#>0           >0          [radius] oder             [konstant] oder      >0
#                         [laenge/2, breite/2]      [gross, klein]
#[m]          [m]         [m] (aussen->innen)       [m] (aussen->innen)  [m]
#-----------|-----------|-------------------------|--------------------|-----------------|
 4.0        , 2.0       , [[2.0, 2.0], [1.0, 1.0]], [[0.4], [0.1]]     , 0.1             #
#5.0        , 3.0       , [[2.5, 2.5], [1.5, 1.5]], [[0.5], [0.1]]     , 0.1             #
];

All values in this code regarding the soil geometry are used later on by a function Boden() (see code 10.11) to create the soil body. Since this function is designed to create cuboid or cylindric soil bodies/partitions, all governing parameters can and should already be adjusted here. Each partition/entry in bodenbereich needs a corresponding entry in gittergroessen to define its mesh size (see also section 6.4 for an explanation of the parameters).

A model can have multiple soil layers. They can be defined as shown in code 10.3. Every depth entry in schichten must have a corresponding material entry in schichtmaterial. If the lowest layer is not equal to the height of the model (bodentiefe), the remaining material is filled with restmaterial.

Code 10.3: Soil geometry definition block (second part).

schichten,  schichtmaterial,  restmaterial = [
#>[0]       >['']             >''
#[m]
#---------|-----------------|--------------|
 [4.0]    , ['Sand']        , 'Sand'       #
#---------|-----------------|--------------|
];

In code 10.4 all pile geometry parameters are defined. All materials (used) are defined in code 10.5. After using a general purpose elastic steel material, all material names used in the soil layer definitions in line 42 must be declared in materialien_boden.

Code 10.4: Pile geometry definition block.

pfahllaenge,    pfahlbreite,   pfahlhoehe,    gitter_pfahl = [
#>0 oder        >0 oder        >0 oder        >0 oder [radius, hoehe]
#[start, ende]  [start, ende]  [start, ende]  oder [xwert, ywert, zwert]
#[m]            [m]            [m]            [m]
#-------------|--------------|--------------|--------------|
 1.0          , 1.0          , 10.0         , 1.0          #
#-------------|--------------|--------------|--------------|
];

The database names of all used materials have to be present Materialdatenbank_20####.xlsx (material database file), which will be read automatically later on. This file should be in the abapys directory or the directory specified with the pfad argument in InitialisiereAbapys().

Code 10.5: Material definitions block.

stahl_dichte = 7.87; # [kN/m^3]
#                   E-Modul   Querdehnz.
#                   E [kPa]   nu [-]
#                 |---------|------------|
stahl_elastisch = [ 210e6   , 0.3        ];

materialien_boden = [
#   Abaqus-Bez.   Datenbankname        Parameter-Bez.   Saettigung   Lagerungsd.   Stoffgesetz
#   >''           >''                  ''               [0-1]        [0-1]         >''
# |-------------|--------------------|----------------|------------|-------------|----------------|
  [ 'Sand'      , 'Standardparameter', ''             , 0.0        , 0.6         , 'Mohr-Coulomb' ],
# |-------------|--------------------|----------------|------------|-------------|----------------|
# Die Lagerungsdichte/Verdichtungsgrad kann bestimmt werden aus
#   a) Anfangsdichte rho_0:     I_r = (rho_0 - rho_min)/(rho_max - rho_min)
#   b) Anfangsporenzahl e_0:    I_e = (e_max - e_0)/(e_max - e_min)
];

Independent variables like the simulation parameters are also defined here (code 10.6).

Code 10.6: Definition of simulation parameters.

viertel = 2;

# Reibung
kontakt_mit_reibung = False;
reibungskoeffizient = 0.22;

# Bewegung
pfahlgeschwindigkeit = -1.0;

# Simulationszeiten und -ausgabefrequenz
secondsperoutput = 0.05; # [s]

The step parameters concerning step duration and scaling factors for each step are defined next (explicit analysis). As shown in code 10.7 multiple parameters are defined in a list. In case parameters between different steps are not subject to change, individual parameter assignment might also be reasonable.

Code 10.7: Explicit steps parameter block.

schritt_schwerkraft = [
#   timePeriod   scaleFactor   linearBulkViscosity   quadBulkViscosity
#   [s]          [-]           ? -?                  ? -?
# |------------|-------------|---------------------|-------------------|
    0.1        , 1.0         , 0.48                , 1.2               #
# |------------|-------------|---------------------|-------------------|
];

schritt_eindruecken = [
#   timePeriod   scaleFactor   linearBulkViscosity   quadBulkViscosity
#   [s]          [-]           ? -?                  ? -?
# |------------|-------------|---------------------|-------------------|
    1.0        , 1.0         , 0.48                , 1.2               #
# |------------|-------------|---------------------|-------------------|
];

At the end of the parameter definitions it is recommended to use a version history (see code 10.8). Changes can be documented here and with the use of a designated variable saved within the model name. It is also reasonable to define a suitable model name here, possibly with more adjustments by previously defined variables. Line 122 of code 10.8 does the actual creation of the model.

Code 10.8: Additional variable definitions.

modellversion = 1;
#
# 1: Erstellung und erste Tests


modelname = 'PileJackingModel' + str(modellversion).zfill(2);
mdb.Model(name=modelname, modelType=STANDARD_EXPLICIT);
mymodel = mdb.models[modelname];

10.3 Material and Contact Processing

After all parameters are defined, the material processing can commence. An elastic steel material is created in code 10.9 with the previously defined parameters. All materials of the soil body (as defined above) are processed in MaterialUndBodensectionErstellen(). This abapys function calculates earth pressure and earth pressure coefficients of all soil layers by using the data of a material database file Materialdatenbank_20####.xlsx. The database name as definded in line 70 (code 10.5) must match the name of the corresponding line/entry in the material database to successfully load the material parameters. Line 136 is used to remove duplicate entries, if the same material is used for different layers (since the material parameters for the same material must only be read once).

Code 10.9: Material processing.

g = 9.81;
mymodel.Material(name='Stahl');
mymodel.materials['Stahl'].Density(table=((stahl_dichte, ), ));
mymodel.materials['Stahl'].Elastic(table=((stahl_elastisch[0], stahl_elastisch[1]), ));
mymodel.HomogeneousSolidSection(material='Stahl', name='secStahl', thickness=None);

verwendeteMaterialien = sorted(set(schichtmaterial + [restmaterial]));
benoetigtUserroutine, verwendeteBodenwerte = BodenmaterialUndSectionErstellen(modell=mymodel,
   verwendeteMaterialien=verwendeteMaterialien, verfuegbareMaterialien=materialien_boden);

Although surface-to-surface contact can be more efficient, using the general contact formulation for interactions in a model is easy to set up and can provide a realistic contact behaviour. The template uses normal contact by default but allowes to decide, if friction behaviour should be used as well. Code 10.10 specifies all necessary commands for normal and tangential contact behaviour (in the whole model), which is applied later in code 10.14.

Code 10.10: Contact initialisation.

mymodel.ContactProperty('Kontakt');
mymodel.interactionProperties['Kontakt'].NormalBehavior(allowSeparation=ON,
   constraintEnforcementMethod=DEFAULT, pressureOverclosure=HARD);
if (kontakt_mit_reibung):
   mymodel.interactionProperties['Kontakt'].TangentialBehavior(
      dependencies=0, directionality=ISOTROPIC, elasticSlipStiffness=None,
      formulation=PENALTY, fraction=0.005, maximumElasticSlip=FRACTION,
      pressureDependency=OFF, shearStressLimit=None, slipRateDependency=OFF,
      table=((reibungskoeffizient, ), ), temperatureDependency=OFF);
else:
   mymodel.interactionProperties['Kontakt'].TangentialBehavior(formulation=FRICTIONLESS);

10.4 Parts

Next, the soil and pile part are to be created. For generating the soil body, the Boden() function is used in line 162–165 of code 10.11. Usually, all parameters defined in section 10.2 are just inserted in the function as shown here. Afterwards the part is referenced by the variable partBoden in line 166 and a section is assigned to it.

Code 10.11: Soil part.

Boden(modell=mymodel, name='Boden', bodentiefe=bodentiefe, voidhoehe=voidhoehe,
   bodenbereich=bodenbereich, gittergroessen=gittergroessen, gitter_boden_vertikal=gitter_vertikal,
   schichten=schichten, schichtmaterial=schichtmaterial, restmaterial=restmaterial,
    viertel=viertel, xypartition=[False, False], partition_durchziehen=True);
partBoden = mymodel.parts['Boden'];
partBoden.SectionAssignment(offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE,
   region=partBoden.sets['setAll'], sectionName='secEuler', thicknessAssignment=FROM_SECTION);

For the pile part a cuboid is used in this example (Quader() function in line 175–177). abapys provides other functions to create simple geometries, (screw) pile profiles or extruding/revolving a sketch (see section 6.2 for a list of those functions and their usage).

The pile part is also referenced by a variable (partPfahl in line 178) and a section is assigned.

Code 10.12: Pile part.

Quader(modell=mymodel, name='Pfahl', laenge=pfahllaenge, breite=pfahlbreite,
   hoehe=pfahlhoehe, materialtyp=DEFORMABLE_BODY, gittergroesse=gitter_pfahl, rp=True,
   xypartition=[False, False], viertel=viertel);
partPfahl = mymodel.parts['Pfahl'];
partPfahl.SectionAssignment(offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE,
   region=partPfahl.sets['setAll'], sectionName='secStahl', thicknessAssignment=FROM_SECTION);

10.5 Assembly and Contact Applications

If a model consists of rather simple parts, they can all be created and instantiated as shown in the previous section and then assembled as shown in code 10.13. The regeneration command in line 187 may be not needed in this example, but is necessary whenever parts or their properties are changed after they have been instantiated.22 For assigning boundary conditions later on, instBoden and instPfahl are referenced in line 183 and 184 and the pile geometry is translated.

Code 10.13: Assembly of the model.

mymodel.rootAssembly.regenerate();
instBoden = mymodel.rootAssembly.instances['instBoden'];
instPfahl = mymodel.rootAssembly.instances['instPfahl'];
mymodel.rootAssembly.translate(vector=(0.0, 0.0, -bodentiefe), instanceList=('instBoden', ));

As we started working in the assembly, we apply the contact formulation defined previously as shown in code 10.14.

Code 10.14: Explicit General Contact application.

mymodel.ContactExp(createStepName='Initial', name='Allgemeinkontakt');
mymodel.interactions['Allgemeinkontakt'].includedPairs.setValuesInStep(
   stepName='Initial', useAllstar=ON);
mymodel.interactions['Allgemeinkontakt'].contactPropertyAssignments.appendInStep(
   assignments=((GLOBAL, SELF, 'Kontakt'), ), stepName='Initial');

10.6 Initial and Boundary Conditions

In almost any soil-structure interaction problem, the soil has to have an initial soil pressure. The different material dependent earth pressures and earth pressure coefficients were already calculated in the MaterialUndBodensectionErstellen() function used in code 10.9. In code 10.15 a geostatic stress condition is applied within BodenspannungErstellen() for every soil layer defined.

Code 10.15: Initial soil pressure distribution.

Materialschichtliste = schichtmaterial + [restmaterial];
BodenspannungErstellen(modell=mymodel, bodenname='Boden', nullspannung=0.0, voidhoehe=voidhoehe,
   schichten=schichten, bodentiefe=bodentiefe, materialschichten=Materialschichtliste,
   verwendeteBodenwerte=verwendeteBodenwerte, verwendeteMaterialien=verwendeteMaterialien);

Next, all materials within an Eulerian body have to be assigned to it. Code 10.16 first assigns all materials used (line 209–211) and then assignes the list to the soil instance in line 213–214.

Code 10.16: Assign Eulerian materials.

assignmentList = [];
for idxMaterial, tempMaterial in enumerate(verwendeteMaterialien):
   assignmentList += ((instBoden.sets['set' + tempMaterial],
      Einheitsvektor(len(verwendeteMaterialien), idxMaterial)), );

mymodel.MaterialAssignment(assignmentList=assignmentList,
   instanceList=(instBoden, ), name='Materialzuweisung', useFields=False);

Any other conditions (like rigid body for the steel pile) are defined here as shown in code 10.17.

Code 10.17: Rigid body definition for pile.

mymodel.RigidBody(bodyRegion=instPfahl.sets['setAll'], name='StarrerPfahl',
   refPointRegion=instPfahl.sets['setRP']);

Now all boundary conditions have to be created and assigned to properly defined geometry sets. If a cylindric soil body should generated, the Boden() function automatically creates a set setMantelflaeche for the curved lateral faces. Also when either a cuboid body should be generated or just half/a quarter of a cylinder, sets will be created of all outer faces (named by the normal of those faces). Therefore, the sets setXFlaeche and/or setYFlaeche might also be present. The bottom set setUnterseite is always created by Boden(). As shown in code 10.18 from line 219 onward, this template checks the existance of those sets and applies all eligible boundary conditions for the soil body. The boundary condition controlling the pile (by using the reference point or more precisely its set) is defined in line 235–237.

Code 10.18: Boundary conditions for soil body and pile.

if (instBoden.sets.has_key('setMantelflaeche')):
   mymodel.VelocityBC(amplitude=UNSET, createStepName='Initial', distributionType=UNIFORM,
      fieldName='', name='bcBodenMantel', region=instBoden.sets['setMantelflaeche'],
      localCsys=None, v1=0.0, v2=0.0, v3=UNSET, vr1=UNSET, vr2=UNSET, vr3=UNSET);
if (instBoden.sets.has_key('setXFlaeche')):
   mymodel.VelocityBC(amplitude=UNSET, createStepName='Initial', distributionType=UNIFORM,
      fieldName='', name='bcBodenX', region=instBoden.sets['setXFlaeche'],
      localCsys=None, v1=0.0, v2=UNSET, v3=UNSET, vr1=UNSET, vr2=UNSET, vr3=UNSET);
   mymodel.VelocityBC(amplitude=UNSET, createStepName='Initial', distributionType=UNIFORM,
      fieldName='', name='bcBodenY', region=instBoden.sets['setYFlaeche'],
      localCsys=None, v1=UNSET, v2=0.0, v3=UNSET, vr1=UNSET, vr2=UNSET, vr3=UNSET);

mymodel.VelocityBC(amplitude=UNSET, createStepName='Initial', distributionType=UNIFORM,
   fieldName='', name='bcBodenUnten', region=instBoden.sets['setUnterseite'],
   localCsys=None, v1=UNSET, v2=UNSET, v3=0.0, vr1=UNSET, vr2=UNSET, vr3=UNSET);

mymodel.VelocityBC(amplitude=UNSET, createStepName='Initial',
   distributionType=UNIFORM, fieldName='', localCsys=None, name='bcPfahlSteuerung',
   region=instPfahl.sets['setRP'], v1=0.0, v2=0.0, v3=0.0, vr1=0.0, vr2=0.0, vr3=0.0);

10.7 Steps

Now all initial and boundary conditions are set and the simulation steps can be defined. Code 10.19 shows a typical example of an explicit dynamics step with parameters from the list defined in the parameter definition part in code 10.6.

Code 10.19: Definition of explicit step ”Schwerkraft“.

mymodel.ExplicitDynamicsStep(name='Schwerkraft', previous='Initial',
   timePeriod=schritt_schwerkraft[0], scaleFactor=schritt_schwerkraft[1],
   linearBulkViscosity=schritt_schwerkraft[2], quadBulkViscosity=schritt_schwerkraft[3]);

In the first custom defined step, all output requests can be set up. Here the standard output requests are renamed and their time interval is adjusted (line 248–252 of code 10.20). Additionally two new field output requests are created in line 261–267 and a history output request afterwards. Depending on the usage of an external subroutine for materials, SDV is also added to the list of requested output variables in line 254–259.

Code 10.20: FieldOutput and HistoryOutput requests.

mymodel.fieldOutputRequests.changeKey(fromName='F-Output-1', toName='FieldOutAll');
mymodel.historyOutputRequests.changeKey(fromName='H-Output-1', toName='HistoryOutAll');
mymodel.fieldOutputRequests['FieldOutAll'].setValues(variables=('U', ),
   timeInterval=secondsperoutput);
mymodel.historyOutputRequests['HistoryOutAll'].setValues(timeInterval=secondsperoutput);

if (benoetigtUserroutine):
   userroutineDatei = userroutine;
   variables = ('A', 'SVAVG', 'EVF', 'SDV');
else:
   userroutineDatei = '';
   variables = ('A', 'SVAVG', 'EVF');

mymodel.FieldOutputRequest(createStepName='Schwerkraft', name='FieldOutBodenIn',
   timeInterval=secondsperoutput, rebar=EXCLUDE, sectionPoints=DEFAULT, variables=variables,
   region=instBoden.sets['setAll']);

mymodel.FieldOutputRequest(createStepName='Schwerkraft', name='FieldOutPfahl',
   timeInterval=secondsperoutput, rebar=EXCLUDE, sectionPoints=DEFAULT, variables=('S', ),
   region=instPfahl.sets['setAll']);

mymodel.HistoryOutputRequest(createStepName='Schwerkraft', name='HistoryOutPfahl', rebar=EXCLUDE,
   region=instPfahl.sets['setRP'], sectionPoints=DEFAULT, timeInterval=secondsperoutput,
   variables=('RF1', 'RF2', 'RF3', 'RM1', 'RM2', 'RM3'));

Loads like gravity can be applied in any step after the initial conditions (see code 10.21).

Code 10.21: Assignment of gravity force.

mymodel.Gravity(comp3=-g, createStepName='Schwerkraft', distributionType=UNIFORM,
   field='', name='Schwerkraft');

Similar to code 10.19 other steps can be defined as shown in code 10.22.

Code 10.22: Definition of explicit step ”Eindruecken“.

mymodel.ExplicitDynamicsStep(name='Eindruecken', previous='Schwerkraft',
   timePeriod=schritt_eindruecken[0], scaleFactor=schritt_eindruecken[1],
   linearBulkViscosity=schritt_eindruecken[2], quadBulkViscosity=schritt_eindruecken[3]);

Loads and boundary conditions can be changed in steps after their definition. In this example we are interested in the soil stresses and pile reaction forces when driving a pile with constant speed into the soil body. The constant speed is defined as a (change of the) boundary condition as can be seen in code 10.23.

Code 10.23: Modifications of boundary conditions.

mymodel.boundaryConditions['bcPfahlSteuerung'].setValuesInStep(stepName='Eindruecken',
   v3=pfahlgeschwindigkeit);

10.8 Postprocessing

This section becomes important when transfering states from an output database to a new model. In general everything needing a fully created model will but not yet a written out job should be handled here. An example for transfering states with Zustandsuebertragung() or applying initial solutions with one of the other available abapys functions can be seen in section 7.

10.9 Job

At this point the model is almost finished. To conduct the simulation, a job has to be created. The commands shown in code 10.24 can be adjusted (e. g. adjust numCpus or numDomains if more CPUs or domains are to be used respectively) but should already provide reasonable default values for small models.

Code 10.24: Job creation.

mdb.Job(activateLoadBalancing=False, atTime=None, contactPrint=OFF,
    description='', echoPrint=OFF, explicitPrecision=DOUBLE_PLUS_PACK,
    historyPrint=OFF, memory=90, memoryUnits=PERCENTAGE, model=modelname,
    modelPrint=OFF, multiprocessingMode=DEFAULT, name=modelname,
    nodalOutputPrecision=FULL, numCpus=2, numDomains=2,
    parallelizationMethodExplicit=DOMAIN, queue=None, resultsFormat=ODB,
    scratch='', type=ANALYSIS, userSubroutine=userroutineDatei, waitHours=0, waitMinutes=0);

myjob = mdb.jobs[modelname];

10.10 Keyword Adjustments

There are some cases, when manual intervention after model and job creation is still necessary, because Abaqus/CAE doesn’t provide the desired functionality (yet). By accessing the keyword block, all input file entries can be read, modified and extended. To find a certain pattern, all keyword entries are investigated. A match with a certain phrase like in line 307 of code 10.25 allows modification of the matched block. Figure 10.1 shows the model created with the template and its default parameters presented in this chapter.

Code 10.25: Keyword changes.

mymodel.keywordBlock.synchVersions(storeNodesAndElements=False);
for idx, text in enumerate(mymodel.keywordBlock.sieBlocks):
   if (text == '*Contact, op=NEW'):
      mymodel.keywordBlock.replace(idx, '*Contact');

myjob.writeInput(consistencyChecking=OFF);
Figure 10.1: Model created with code and parameters described in this chapter.

11 Closing Remark on a Graphical User Interface to Create Scripts

This document does not cover all functionalities of abapys but it provides an overview over its most important fuctions and contains some ideas on model creation and output processing. Using and adapting the presented functions in a different context requires some knowledge about Python scripting in Abaqus (i. e. a basic understanding of Python and functions/structures in Abaqus). So it is recommended to create your own scripts, experiment with the given examples and learn when and how to use them.

An additional way to get started with the basics of generating scripts for model creation or output processing (using some abapys function) is provided by the abapys_front template for the SimpleScriptGenerator (found at https://github.com/d-zo/SimpleScriptGenerator).

SimpleScriptGenerator is a graphical frontend to interactively create scripts based on modifiable code templates. The abapys_front template allowes to create ready-to-use Python scripts for Abaqus (e. g. the model presented in section 10 can be reproduced graphically). The resulting scripts can be run in Abaqus to see their effect and investigated in a text editor to understand the code. Adjusting parameters in the graphical frontend and inspecting its effects hopefully makes it easier to understand scripting in Abaqus and to start exploring what abapys has to offer.


  1. Composed of ABAqus PYthon Scripting. There is another project named abapy which has a similar scope. This library is not related to that project in any way.↩︎

  2. HTML-version: https://d-zo.github.io/abapys/pilejackingexample.html (for a pdf-version change the suffix to .pdf).↩︎

  3. Currently abapys and its documentation are written in german.↩︎

  4. This statement has to be constrained for at least two reasons: Selection in Abaqus is depending on the amount/order of created elements and therefore the command has to be adjusted to be reliable. And since the exact same behaviour (and errors) will be reproduced when run again, some commands should also be adjusted or removed for a successful and uninterrupted execution.↩︎

  5. It might be necessary to adjust the path/command first (e. g. adding .exe and/or navigating to the right folder).↩︎

  6. Essentially this should be the directory containing the files gewichtung.dll, gewichtung.so and Materialdatenbank_20####.xlsx.↩︎

  7. The windowed viewport will have a border and titlebar, adding 10 px horizontally and 31 px vertically (which will be subtracted internally).↩︎

  8. The advantage of doing it graphically is that the viewport window will be resized in steps of pixels. Therefore the scaling factor can be obtained with a good precision in the described manner. If the size is changed directly (e. g. by myviewport.setValues(width=..., height=...)), the accuracy of the scaling factor will be poor, if the values do not represent an exact pixel position.↩︎

  9. A possible timestamp could be (assuming myodb and myviewport to be valid references): zeitstempel = myodb.userData.Text(referencePoint=TOP_RIGHT, anchor=TOP_RIGHT, color='#000000', text='2020-01-05', name='date', font='-*-liberation sans l-bold-r-normal-*-*-180-*-*-p-*-*-*', offset=(0.0, 0.0)); myviewport.plotAnnotation(annotation=zeitstempel);↩︎

  10. Execute session.journalOptions.setValues(recoverGeometry=COORDINATE, replayGeometry=COORDINATE) at startup to log the actual sequences instead of the masked sequences.↩︎

  11. Some objects like odb result data have nodeLabel or an elementLabel attributes (instead of a normal label).↩︎

  12. BedingteAuswahl() also supports a small set of mathematical expressions (if needed): sqrt(), sin(), cos(), tan(), asin(), acos() and atan().↩︎

  13. The nodes just have to be in the list, so it is perfectly fine to pass all nodes of the instance to punkteliste.↩︎

  14. Make sure that the meaning of each variable is easy to understand, if only single characters and no descriptive names are chosen.↩︎

  15. It is also possible to transform the source coordinates with the optional odbknoten-argument, but the default way is to use mdbknoten.↩︎

  16. The introductory note in section 7 on supported/tested element types also applies here.↩︎

  17. See also code 5.2 and figure 2.1 for accessing instances in a model database or output database.↩︎

  18. When saving plots with BildSpeichern(), it is recommended to use hintergrund=True.↩︎

  19. While many variations are possible with this functions, the trace of a stress tensor has its own function SpannungsSpurAlsFieldOutput().↩︎

  20. When combining all code snippets in this chapter, some lines might seem to be missing (like line 23–28 of modelcreation.py). Those lines are either empty or have structural comments.↩︎

  21. This approach can be very useful if a parameter study is to be conducted.↩︎

  22. The regeneration command is also necessary when a new part is the created and instantiated from the assembly of multiple simpler parts.↩︎