Analysis of Structures
Transcription
Analysis of Structures
Analysis of Structures Book of Examples 2015 University of Duisburg-Essen Faculty of Engineering Department of Civil Engineering Static and Dynamic of Shell Structures Dr. E. Baeck 23.4.2015 Contents I Programming with Python 3 1 How to get started with Python 5 1.1 What is Python? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2 Python, Packages, Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2.1 Installing the Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2.2 Installing the ComType Package . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2.3 Installing the NumPy Package . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.2.4 Installing the SciPy Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.2.5 Creating Python Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.2.6 Python Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.2.6.1 CPython the Reference Implementation . . . . . . . . . . . . . . . . . 11 1.2.6.2 Jython, let’s go Java . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.2.6.3 IronPython, let’s go .Net . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.2.6.4 PyPy, Python to the Square . . . . . . . . . . . . . . . . . . . . . . . 11 1.3 Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.4 Python Calculator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2 Basics in Python 15 2.1 Code Convention . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.2 Reserved Words . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.3 Packages and Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.3.1 Import of a whole Module or Package . . . . . . . . . . . . . . . . . . . . . . . 16 2.3.2 Import all Names of a Module . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.3.3 Selective Import of Module Names . . . . . . . . . . . . . . . . . . . . . . . . 17 2.3.4 Import with new Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.4.1 Unary Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.4.2 Arithmetic Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.4.3 Bit Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.4.4 Extended Assign Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.4.5 Manipulating Bits and Hexadecimal Numbering System . . . . . . . . . . . . . 20 2.4.6 Comparison Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.4.7 Membership Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.4.8 Identity Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.5 Print and Output Formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.6 Basic Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.4 iii Analysis of Structures - SS 15 Page iv 3 2.7 Code Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.8 Globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.9 Loop for Repetitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.9.1 The Factorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.9.2 Floating Point Precision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.9.2.1 Description of the Application . . . . . . . . . . . . . . . . . . . . . 30 2.9.2.2 Exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.10 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.11 Branches for Decisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.11.1 How to Solve a Quadratic Equation . . . . . . . . . . . . . . . . . . . . . . . . 32 2.11.1.1 A Flow-Chart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.11.1.2 The Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.12 Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.12.1 An Abs-Function with Type-Checking . . . . . . . . . . . . . . . . . . . . . . . 35 2.12.2 The Newton-Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.13 Data Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 2.13.1 Working with Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 2.13.2 Working with Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 2.13.3 Working with Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2.14 Error Handling with Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.14.1 Syntax Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.14.2 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.14.3 Handling Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2.14.4 Raise Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 2.15 Random Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 2.16 Date, Time and Timespan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 2.17 Working with Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 2.17.1 Open a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 2.17.2 Write Data into a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 2.17.3 Close a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.17.4 Read Data from a Text File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.17.5 A Logger-Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 2.18 OOP with Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 2.18.1 Some UML Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 2.18.2 Implementation of Classes in Python . . . . . . . . . . . . . . . . . . . . . . . 55 2.18.2.1 Class Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 2.18.2.2 Class Destructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 2.18.3 Implementation of a Time Stack Class . . . . . . . . . . . . . . . . . . . . . . . 57 Python Projects 61 3.1 Newton, Step2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 3.2 Profiles, Thin Walled Approach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 3.2.1 A General Base Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 3.2.2 A Node Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 3.2.3 Testing the Node Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 E. Baeck CONTENTS II 4 5 III Page v 3.2.4 An Element Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 3.2.5 Testing the Element Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 3.2.6 A General Profile Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 3.2.7 The AList Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 3.2.8 Testing the Profile Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 3.2.9 The U-Profile Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 3.2.10 Testing the UProfile Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 3.2.11 The Profile Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 3.2.12 A Little Profile Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Scripting with Abaqus 91 Some Aspects and Introduction 93 4.1 Aspects of the Abaqus GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 4.2 The Abaqus CAE Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 4.3 A Modeling Chain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 4.4 A little interactive Warm Up Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 4.4.1 Create a Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 4.4.2 Create a Sketch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 4.4.3 Create a Part . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 4.4.4 Create and Assign Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 4.4.5 Create the Instance, Assign the Part . . . . . . . . . . . . . . . . . . . . . . . . 97 4.4.6 Create a Load Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 4.4.7 Create Loads and Boundary Conditions . . . . . . . . . . . . . . . . . . . . . . 97 4.4.8 Create the Mesh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 4.4.9 Create a Job and Submit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Scripts and Examples 99 5.1 3 Trusses Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 5.2 U-Girder Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 5.2.1 System and Automated Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . 106 5.2.2 Scripting and OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 5.2.3 Class InputData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 5.2.4 Class ResultData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 5.2.5 Class Base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 5.2.6 Class UGirder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 5.2.7 Run the UGirder Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 5.2.7.1 Results of the Linear Static Step . . . . . . . . . . . . . . . . . . . . 123 5.2.7.2 Results of the Buckling Step . . . . . . . . . . . . . . . . . . . . . . 124 5.2.7.3 Results of the Frequency Step . . . . . . . . . . . . . . . . . . . . . . 126 Appendix A Some Special Problems 129 131 A.1 Modules and Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 23.4.2015 Analysis of Structures - SS 15 Page vi B Some Theory B.1 Section Properties . . . . . . . . . . . . . . . . . . . . . . . . B.1.1 The Area of a Profile Section . . . . . . . . . . . . . . B.1.2 First Moments of an Area . . . . . . . . . . . . . . . B.1.3 Second Moments of an Area . . . . . . . . . . . . . . B.1.4 Center of Mass . . . . . . . . . . . . . . . . . . . . . B.1.5 Moments of Inertia with Respect to the Center of Mass B.1.6 Main Axis Transformation . . . . . . . . . . . . . . . C Some Python IDEs C.1 The Aptana - IDE . . . . . C.2 The PyCharm - IDE . . . . C.2.1 General Statements C.2.2 A Hello-Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 133 133 133 134 135 135 136 . . . . 137 137 138 138 138 D Conventions 141 D.1 The Java Code Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 E Parallel Computing E.1 Threads . . . . . . . . . . . . . . . E.2 A Multi-Processing Pool . . . . . . E.2.1 A Single Processor Solution E.2.2 A Multi Processor Solution . . . . . 143 143 143 144 145 F Some Special Abaqus-GUI-Features F.1 Viewport Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . F.1.1 The Legend’s Font Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . F.2 Specify View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 147 147 149 E. Baeck . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CONTENTS Page 1 23.4.2015 Page 2 E. Baeck Analysis of Structures - SS 15 Part I Programming with Python 3 1 How to get started with Python 1.1 What is Python? Python is a functional and object orientated computer language. The language’s development was started by Guido van Rossum, who was working with the Centrum voor Wiskunde en Informatica (CWI) in Amsterdam Netherlands, see figure 1.1 Figure 1.1: Guido van Rossum The name Python has nothing to do with the snake of the same name. The name Python was taken from the British surreal comedy group Monty Python (see figure 1.2). Because the Monty Python is hard to symbolize onto an icon, the python snake came into picture and so on all icons and Python symbols today we can see the snake. The Python language is highly dynamic, so the language is able for example to create it’s source code by itself during runtime. This fact and it’s highly portability are reasons for it’s interpreted kind. So Pyhton code like Java or C# code too is converted into a socalled bytecode. The bytecode then is executed on a virtual machine. It’s a program, which can be seen as virtual processor or an emulator. If such a virtual machine is available on a platform, the bytecode can be executed without any adaptions.1 With this advantage of highly portability Python comes with the general disadvantage of interpreted languages, the disadvantage of a really bad performance compared to compiled languages like FORTRAN or C. 1 This is only true, if no platform depended packages like comTypes are used. 5 Figure 1.2: Monty Python Analysis of Structures - SS 15 Page 6 1.2 Python, Packages, Utilities If we start with Python, we should think about the choice of the Python version. Because we will use some additional Python packages, we should be sure, that this packages are available for the desired Python version. In the case of our lecture we will select the Python version 2.6, which is properly stable and furthermore all the needed add-on packages are available. To start from the beginning, we have to download the following packages for windows first. It is recommended to download the windows installer version, if available because this is the easiest kind of installation. The installation procedure should start with the installation of the kernel package. • python-2.6.4.msi The installer of the python kernel system 2.6. • comtypes-0.6.2.win32.exe The installer of Windows types, which are necessary to use Windows API-calls. • numpy-1.4.1-win32-superpack-python2.6.exe The installer of the NumPy package for numerical programming. • scipy-0.8.0-win32-superpack-python2.6.exe The installer of the SciPy package for sientific programming. • matplotlib-0.99.3.win32-py2.6.exe The installer of the MatPlotLib package. This package we need to get the pylab package. • pywin32-214.win32-py2.6.exe The installer of a little nice Python IDE. 1.2.1 Installing the Kernel The python kernel should be the first package, which is to install, because this installation sets up the Python base folder. Within this folder you can find the folder Lib, which contents a lot of libraries and additional packages and besides that a further sub folder called site-packages. Add ons are copied into this folder by their installers or by the install Python script. The start screen of the Python installer shows the Python version. You can select, whether the setup should make Python available for all users or not. After clicking next you’ll get within the second form the possibility to select the base folder of Python. By default Python uses the folder C:\Python26. We overwrite the default and select the Windows standard program folder as starting folder, so we write c:\Programme\Python2623 . The figures 1.3 and 1.4 show the input forms installing the Python kernel. 1.2.2 Installing the ComType Package If you want to use the Python language on a Windows system, it’s recommended to install the ComTypes package. This package will give you a simple access to the Windows resources. It can be understood as 2 3 The discussed installation was performed on a German system. The installation of newer packages is working in the same way. E. Baeck 1.2. PYTHON, PACKAGES, UTILITIES Page 7 Figure 1.3: Start Screen of the Python Installer and Choice of Base Folder Figure 1.4: Selection the Features and Starting the Installation a wrapper layer, an interface layer for the access to Windows DLL-modules. ComTypes can be a help to develop a software with a proper Windows look and feel. The installation of the most Python packages will run very simular to the following installation. The figures 1.5 show the first and second form of the installation procedure. The first form gives a few information to the package. The second form is usually used to select the Python version. Each installed and supported Python version will be listed in the list box of the second form. You can select the desired Python version and can go on with the installation procedure clicking the next button. 1.2.3 Installing the NumPy Package NumPy [2] is the fundamental package for scientific computing in Python. It is a Python library that provides a multidimensional array object, various derived objects (such as masked arrays and matrices), and an assortment of routines for fast operations on arrays, including mathematical, logical, shape manipulation, sorting, selecting, I/O, discrete Fourier transforms, basic linear algebra, basic statistical operations, random simulation and much more. At the core of the NumPy package, is the ndarray object. This encapsulates n-dimensional arrays of 23.4.2015 Analysis of Structures - SS 15 Page 8 Figure 1.5: Start Screen of the Package Installer and Choice of the installed Python Version homogeneous data types, with many operations being performed in compiled code for performance.4 The installation runs like the installation of the ComTypes package (see figure 1.6). Figure 1.6: Start Screen of the Package Installer and Choice of the installed Python Version 4 For more details see NumPy User Guide available on the info.server. E. Baeck 1.2. PYTHON, PACKAGES, UTILITIES 1.2.4 Page 9 Installing the SciPy Package SciPy [3] is a collection of mathematical algorithms and convenience functions built on the Numpy extension for Python. It adds significant power to the interactive Python session by exposing the user to high-level commands and classes for the manipulation and visualization of data. With SciPy, an interactive Python session becomes a data-processing and system-prototyping environment rivaling sytems such as Matlab, IDL, Octave, R-Lab, and SciLab. The additional power of using SciPy within Python, however, is that a powerful programming language is also available for use in developing sophisticated programs and specialized applications. Scientific applications written in SciPy benefit from the development of additional modules in numerous niche’s of the software landscape by developers across the world. Everything from parallel programming to web and data-base subroutines and classes have been made available to the Python programmer. All of this power is available in addition to the mathematical libraries in SciPy.5 The installation runs like the installation of the ComTypes package (see figure 1.7). Figure 1.7: Start Screen of the Package Installer and Choice of the installed Python Version 5 For more details see SciPy Reference Guide available on the info.server. 23.4.2015 Analysis of Structures - SS 15 Page 10 1.2.5 Creating Python Source Code To create Python sources we need at least a simple text editor like Notepad++ or PSPad6 like for every computer language. This source files with the standard extension py, like helloworld.py, can be executed starting the Python interpreter.7 1 c:\pyhton27\python helloworld.py For Windows there is a lightweigth IDE available called PythonWin. PythonWin looks like an extension to the MS-Editor Notepad. To create and check small Python scripts, this IDE seems to be ok, because the overhead we have to overcome is very small compared to really good IDEs. The installation of PythonWin runs like the installation of the ComTypes package (see figure 1.8). Figure 1.8: Start Screen of the PythonWin IDE Installer and Choice of the installed Python Version After having installed the PythonWin IDE it’s recommended to set up a link onto the desktop (see figure 1.9). Figure 1.9: Creating a Link to the pythonwin.exe Figure 1.10 shows, how to create a Hello World application in Python and the application’s execution. Within the editor window, we write the source code. With the start button (black triangle) we can run the Hello World from the PythonWin. The applications screen output is written into the Interactive Window, which also is working as a Python console. 6 7 Notepad++ and PSPad are available on the info.server in /Software/Editors. In this case the Python 2.7 was installed into the Folder c:/Python27. E. Baeck 1.2. PYTHON, PACKAGES, UTILITIES Page 11 Figure 1.10: Creating and Executing a Pyhton Hello within PythonWin 1.2.6 Python Implementations 1.2.6.1 CPython the Reference Implementation There is not only one Python implementation available. The original implementation is written in C. It’s the reference implementation, developed by Guido van Rossum, called CPython too. 1.2.6.2 Jython, let’s go Java Jython (or JPython) is an implementation of the Python language in Java. The interpreter therefor is running on a Java environment and is able to use all Java libraries. 1.2.6.3 IronPython, let’s go .Net IronPython is an implementation for the Common-Language-Infrastructure (CLI), i.e. for the .Net environment on Windows or for the compatible environment Mono on Linux. IronPython is written in C# and is available in a CLI language (like C#) as a script language. IronPython is compatilble to CPython’s version 2.7. 1.2.6.4 PyPy, Python to the Square PyPy is a Python interpreter, which is implemented using the Python language. It’s used as an experimental environment to develop new features. 23.4.2015 Analysis of Structures - SS 15 Page 12 1.3 Hello World Like in every computer language there is a Hello World application in Python also possible. We start dhe PythonWin IDE and create a new file. We save this file as HelloWorld.py. With Ctrl-R the execution form is shown and the execution mode should be selected (see figure 1.11). The following available execution modes are available. • No debugging, execution without debugging. • Step-through in the debugger, the debugger is stated and starts with the first statement. • Run in the debugger, the script is started. Execution is only interrupted at the first breakpoint. • Post-Mortem of unhandled led exceptions, the debugger is started, if the script crashes with a unhandled exception. Figure 1.11: Executing the HelloWorld.py Script If the HelloWorld.py script is executed, the output is written into the Interactive Window, see figure 1.10. E. Baeck 1.4. PYTHON CALCULATOR 1.4 Page 13 Python Calculator One of Python’s advantages is the feature to execute only one statement within the Python shell. The Python shell within the PythonWin IDE is given by the Interactive Window, see figure ??. If we want to calculate the vertical position of a ball thrown up in the air with an initial velocity v0 after a time t we get from the Newton’s law y(t) = v0 · t − 1 · g · t2 2 (1.1) So, if we want to calculate the vertical position for an initial velocity v0 = 5m/s after 0,6 seconds, we can calculate the y(t = 0, 6) with one simple Python call. 1 2 3 >>> print 5*0.6 -0.5*9.81*0.6**2 1.2342 >>> A second version of the calculation can be performed introducing and using variables as follows. So we can check every Python command within the Interactive Window. 1 2 3 4 5 6 7 >>> v0 = 5 >>> g = 9.81 >>> t = 0.6 >>> y = v0*t - 0.5*g*t**2 >>> print y 1.2342 >>> A third version of our calculation of the ball’s height could be a Python script, which we can load and executed within the PythonWin IDE. To comment the code we insert some comments, which starts with the # character. Characters at the right side of the # character are ignored by the Python interpreter. Listing 1.1: Calculation of the Flight Altitude of a Ball 1 2 3 4 5 6 # program for computing the height of a ball thrown up in the air v0 = 5 # initial velocity g = 9.81 # acceleration of gravity t = 0.6 # time y = v0*t - 0.5*g*t**2 # vertical position print y # printing the result 23.4.2015 Page 14 E. Baeck Analysis of Structures - SS 15 2 Basics in Python 2.1 Code Convention In modern programming languages we can use nearly arbitrary names for variables, for functions, classes and packages. Of course the names should be clear, i.e. the should not ambiguous. Sometimes however it’s helpful to select names according to a name convention. If we use a name convention, we can put an additional information into an item’s name, so that a developer can get this information without knowing the details behind the code. One of the first conventions was introduced by Charles Simonyi, a Hungarian software engineer, who worked at Xerox PARC and later with Microsoft. This convention therefore is called Hungarian Notation. The Hungarian Notation inspired from FORTRANs implicit name convention too, introduces some name prefixes, which should show the developer some information about the variable usage. With the emergence of the language Java a new code convention was introduced, which today is used in many applications, so for example in the implementation of the Abaqus Python Interface. This we will discuss in the second part (II). A short extract from this code convention, published by Sun Microsystems, Inc. in 1997, we see in the appendix D.1. From the Java Code Convention we use the following aspects. 1. Variable names will start with small letters. 2. Function names will start with small letters. 3. Class names will start with capital letters. 4. If names consists of several parts, we introduce every part except the first with a capital letter. This is called camelCase, because of the shape of the camel’s back, the camel hump. Of course we do not use white spaces inside the names, because this is not allowed. 15 Analysis of Structures - SS 15 Page 16 2.2 Reserved Words We have seen in section 1.4, starting with a Python calculation within the Python shell is very easy. We can use simple formulas with numbers or we can use symbolic names to make it more readable. In Python like in other programming languages there are some reserved word, which are used to build up the language. This words can not be used as variable names. The reserved words of the Python language are the following. and del from not while as elif global or with assert else if pass yield break except import print class exec in raise continue finally is return def for lambda try If you want to use such a word it’s recommended to extent the name with an underscore, like vor example break_ instead of break. 2.3 Packages and Modules A package within Python is a container of software objects, global variables, functions and objects. In C or Fortran, a package could be termed as library. Packages should be imported into the Python session or into the Python source file, if external feature should be used. A typical package import is the import of the mathematical package. This is necessary if you want to call some basic mathematical functions like trigonometric functions or the square root. If such a package is not imported, it’s objects, especially it’s functions, are unknown and can not be used. Packages are like header includes within the C language. In C as well, external functions, whose headers are not included, are unknown and can not be called. 2.3.1 Import of a whole Module or Package A whole package is imported with the statement "import" module. The following example shows the import of the mathematic package to apply the square root function. With the import statement the module math will be linked. The square root function sqrt will be then available with the usual dot access math.sqrt. 1 2 3 >>> import math >>> math.sqrt(4) 2.0 E. Baeck 2.3. PACKAGES AND MODULES 2.3.2 Page 17 Import all Names of a Module If we only want to import all symbolic name of a module (package), we use the star as a wild card. The following example shows the import of all names of the module math. If we do this, we can use all functions of the module without prefixing it. 1 2 3 4 5 2.3.3 >>> from math import * >>> sqrt(4) 2.0 >>> fabs(-2.) 2.0 Selective Import of Module Names If we only want to import a symbolic name of a module (package), then we can import in a selective way. The nex example shows the selective import of the function sqrt from the module math. If we do this, then the function can be used without the prefixing module name. 1 2 3 4 2.3.4 >>> from math import sqrt >>> sqrt(4) 2.0 >>> Import with new Names If some names of of module should be imported with new names, the statement as can be used within the import statement. The following example shows the import of the trigonometric functions sin and cos with the new names s and c and the constant pi with it’s original name, to calculate the Cartesian ordinates of a 45 point with radius 10. 1 2 3 4 5 6 7 8 9 >>> from math import sin as s, cos as c, pi >>> r = 10 >>> x = r*c(pi/4) >>> y = r*s(pi/4) >>> x 7.0710678118654755 >>> y 7.0710678118654746 >>> You see, that we change the original name of the trigonometric functions with the as key word. Within the formulas the functions can be used with it’s new names. 23.4.2015 Analysis of Structures - SS 15 Page 18 2.4 Operators We have already seen, that Python also has it’s operators calculation the height of a vertical thrown ball. Python uses the same precedence as we know form the mathematics. The power operation has the strongest binding followed by the point operators (products and divisions) followed by the line operators (plus and minus). Unary operators will always be applied first. To change the standard precedence of the operators we use like in mathematics parenthesis to dictate the way a formula should be evaluated. 2.4.1 Unary Operators Unary operators are working only on one value, therefor unary. In Python there are three unary operators available. Operator Comment Example + plus operator a = 2 >>> x = +a >>> +2 - minus operator a = 2 >>> x = -a >>> -2 ˜ bitwise inversion a = 2 >>> x = ˜a >>> -3 The bitwise inversion shows the internal representation of negative numbers. A negative number is represented by the so called b-complement of a number. This is the complement, i.e. the bitwise inverted number plus 1. So we get −a =∼ a + 1 or ∼ a = −(a + 1) 2.4.2 (2.1) Arithmetic Operators Python offers the following arithmetic operators. You should be careful with the usage of data types especially within divisions. If you use integers, the result generally will be truncated.1 Operator Comment Example + sum operator x = 2+3 >>> 5 - substraction operator x = 4-2 >>> 2 * product operator x = 2*4 >>> 8 / division operator x = 9/2 >>> 4 x = 9./2. >>> 4.5 1 ** power operator x = a**2 % modulo operator x = a%2 // integer division operator x = a//2 The exception of the power operator all the arithmetic operators are used with the same symbol like in C. In C there is no power operator available. E. Baeck 2.4. OPERATORS 2.4.3 Page 19 Bit Operators Like in C bit operators can be easily be used to manipulate a number’s bits. The following operators are available2 Operator Comment Example & bitwise AND ˆ bitwise exclusive OR x = 23 ˆ 13 >>> 26 | bitwise OR x = 23 | 13 >>> 31 << left shift of bits x = 4 << 2 >>> 16 >> right shift of bits x = 4 >> 1 >>> x = 23 & 13 >>> 5 2 The left shift of a numbers bit by 1 is equal to a multiplication by 2. The right shift by one is the same as a division by 2. The bitwise AND and OR operator are usually used to set or to clear a number’s bits. The following example shows how to apply the shift operator. We start with the bit 0, which has the value 1. Within a for loop (see section 2.9) the bit is shiftet subsequently to the left. So we create the bits in the range from n to m. After shifting the bit, it’s value is printed into the console window. Listing 2.1: List the Bit’s Values 1 2 3 4 5 6 7 8 2.4.4 # print the value the bits from bit n to bit m # n = 1 m = 10 bit0 = 1 for i in range(n,m+1): bit_i = bit0 << i print "Bit %2d = %6d" % (i,bit_i) Extended Assign Operators An extended assign operator combines the effect of an operator with the assignment to the involved variable. This is inherited from the language C. In the table below we start with a variable x = 2. Operator Comment 2 Example += add and assign x += 1 >>> 3 -= substract and assign x -= 1 >>> 1 *= multiply and assign x *= 2 >>> 4 /= divide and assign x /= 4 >>> 0.5 <<= left shift and assign x <<= 2 >>> 8 <<= right shift and assign x >>= 1 >>> 1 |= or and assign x |= 4 >>> 6 &= and and assign x &= 8 >>> 0 Python’s bit operators are exactly the some as the C bit operators. 23.4.2015 Analysis of Structures - SS 15 Page 20 2.4.5 Manipulating Bits and Hexadecimal Numbering System If we want to manipulate a number’s bits it is obvious more clearly to use the hexadecimal representation of a number as using the elsewhere usual decimal representation. Hexadecimal numbers starts with the literal 0x3 and uses the digits 0-9 and A-F. F is with 15 the largest digit of the hexadecimal numbering system. The hexadecimal numbering system has the advantage, that it packs 4 bits of a number into one hexadecimal digit. So a byte can be represented by 2 hexadecimal digits. If we now be able to translate a hexadecimal digit into a binary number, then we can see even the bits in the largest number without any calculation. In the following example we want to analyze the arbitrary number 27563. The bits are obviously very hidden using the decimal representation. To get a hexadecimal representation we can simple print the number using the X formating. We can see that we obviously use two bytes for this number, because wie get 4 digits (6BAB). Furthermore we can see, that the leading bit is not set, because the largest digit is 6 and the highest bit in a digit has the value 8. 1 2 3 4 >>> a = 27563 >>> "%X" %a ’6BAB’ >>> The binary number representation is easily available from the hexadecimal representation, if we know the binary representation of the hexadecimal digits4 . 616 = 610 = 4 + 2 = 01102 A16 = 1010 = 8 + 2+ = 10102 B16 = 1110 = 8 + 2 + 1 = 10112 So we get assembling the binary digits of 6BAB the following bit sequence. 2756310 = 6BAB16 = 0110|1011|1010|10112 (2.2) If we now want to set the highest bit of the discussed number, we can use the bitwise OR operator | (see section 2.4.4). A number with only the highest bit set we can obtain by shifting the first bit to the desired position within the 2 bytes, i.e. we shift the bit 15 times. Now we can see that we get a hexadecimal number with only the highest digit non vanishing. Within the digit of 8 the forth bit is set, which is the highest of a have byte5 . 1 2 3 4 5 6 >>> b = 1 >>> b = b<<15 >>> b 32768 >>> "%X" % b ’8000’ If we now want to set the highest bit of our original number 27563, we simple can overlay it with the last number 8000. 3 A binary number starts with the literal 0b and uses the digits 0 and 1, like 0b1000 = 810 . The index of the example’s numbers represent the base of the numbering system. 5 A half byte is also called nibble. 4 E. Baeck 2.4. OPERATORS 1 2 3 4 5 6 Page 21 >>> a = 27563 >>> b = a | (1<<15) >>> b 60331 >>> "%X" % b ’EBAB’ After having set the highest bit, we see that the decimal number has changed totally. However the hexadecimal number only changes in the first digit. Instead of 6 we have now E. And E is represented binary with E16 = 1410 = 8 + 4 + 2 = 11102 so we get 6033110 = EBAB16 = 1110|1011|1010|10112 (2.3) Comparing the binary result with the binary result of equation 2.2 we see that obiously only the first bit is set as wanted. How we can now clear a bit of a number? Clearing a bit of a number uses two steps. First we have to create the inverse of the filtering number, having set only the desired bit. And within a second step we use the AND operator & to overlay bitwise the inverse of the filtering number and the number, whose bit should be cleared. In our example we want to clear the highest bit of the first byte. The filtering number we get shifting the 1st bit 7 times. 1 2 3 4 5 6 >>> a = 27563 >>> b = a & (˜(1<<7)) >>> b 27435 >>> "%X" % b ’6B2B’ We also notice, that the decimal representation has changed widely after the clearing of the bit on the contrary to the hexadecimal. 2743510 = EB2B16 = 1110|1011|0010|10112 2.4.6 (2.4) Comparison Operators Boolean operators are used to branch and to make decisions. The comparing operators are identical to the C comparing operators.6 Operator Comment 6 Example < less than x = 23 < <= less equal x = 23 <= 23 >>> True > greater x = 23 > 13 >>> True >= left shift of bits x = 23 >= 23 >>> True == equal x = 23 == 23 >>> True <> not equal x = 23 <> 13 >>> False != non equal x = 23 != 23 >>> False 13 >>> False There are two non equal operators available. ! = is the C version and <> is the Basic version. 23.4.2015 Analysis of Structures - SS 15 Page 22 The result of a boolean expression like above are the boolean values False or True. To combine comparing expressions the following logical operators can be used.7 Operator Comment Example and logical and x = 1 < 2 and 2 < 3 >>> True or logical or True not logical not x = not (1 < 2) x = 1 < 2 or 2 > 3 >>> >>> False The following table shows the truth values of the && and the || operator. Truth tabel of the && operator 2.4.7 Truth tabel of the || operator a b a && b a b a || b true true true true true true true false false true false true false true false false true true false false false false false false Membership Operators With the membership operators you can check whether a value or an object is part of sequence of objects. Operator Comment in is member Example x = 2 in (1,2,3) >>> not in is not a member x = 2 not in (1,2,3) >>> 2.4.8 True False Identity Operators With the identity operators you can check the identity of two objects. Operator Comment is is identical Example x = (1,2) >>> y = x >>> x is y >>> is not is not identical x = (1,2) >>> y = x >>> x is not y >>> 7 True False To make expressions clear parenthesis should be used. A term within a parenthesis is evaluated first and it’s result then is used in further evaluations outside the parenthesis. With parenthesis the order of the evaluation can be set. E. Baeck 2.5. PRINT AND OUTPUT FORMATS 2.5 Page 23 Print and Output Formats If you want to print data into the console window, you have to think about formating. The formating sequences are very similar to the formating sequences of the C printf function. The formating is a so called escape sequence within a string, which is started with the % operator. The most common formats are the following. • formating an integer An integer (independent of the data type) is formated by the escape %d for decimal representation and %x or %X for hexadecimal representation. • formating a float A float is formated by the escapes %f, %e, %E, %g and %G • formating a string A string is formated by the escapes %s A leading number n within a format %nT, with T the type of the format, sets up the width of the output. The following example shows the formating of an integer in decimal and hexadecimal mode. At the hexadecimal format a lower x sets lower digit letter, the capital X sets capital digit letters. 1 2 3 4 >>> "%d,%3d,%6d" % (2,2,2) ’2, 2, 2’ >>> "%x,%3X,%6X" % (31,31,31) ’1f, 1F, 1F’ Formating floats there are two different formats available, the fixed format and the exponential format, which is also called scientific format. The f format sets a non exponential representation. The e or E format sets a exponential format. e uses a small e letter, and E uses a capital E letter. The g or G format sets an optimized representation, i.e. a fixed or an exponential format, depending on the outputs length. The number after the dot sets the number of digits after the comma for f and e format, it sets the number of significant digits for the g format. 1 2 3 4 >>> "%f,%e,%g" % (12.34,12.34,12.34) ’12.340000,1.234000e+01,12.34’ >>> "%.2f,%.2e,%.2g" % (1234567.89,1234567.89,1234567.89) ’1234567.89,1.23e+06,1.2e+06’ 23.4.2015 Page 24 2.6 Analysis of Structures - SS 15 Basic Data Types Recording to the available data types, Python is very different comparing it with common languages like C, Fortran and Basic. Most of the languages offers the programmer data types, which are one by one related to the underlaying hardware. So for example Fortran and C offer 2 and 4 byte integers on 32 bit operating systems by default8 On a 64 bit operating platform a long integer of 8 bytes will be available. On the other hand there are 4 and 8 byte floats available. Python however offers on 32 bit platforms a normal integer of 4 bytes, which is directly related to the hardware, for example 11234, and furthermore a so called long integer, for example 1234L, which is handled by the Python software. The long integer, which is marked by a succeeding L, is only restricted by the computers memory, that means that a really incredible number of digits can be considered. Later we will calculate the factorial of a incredible high number. Furthermore Python as already mentioned offers only one float data type with 8 bytes. The standardized 4 byte float is not supported, for example 1.23 or 1.23e+2. Python also supports a complex arithmetic with an complex data type, consisting of two floats for real and imaginary part of the complex number. The complex unit in Python is called j. Therefor the complex number 1 + 4i will be represented in Python with 1 + 4j. The last data type used in Python is a string consisting of one or more characters. The data type of a variable can be determined using the build in function type, as shown in the following example. Within a first step different variables were created by a simple assignment. The content of the variable determines the type of the variable, no explicit declaration is needed or available, like in C. After having created the variables the type of the variables will be determined by subsequent type calls. To check the data type within a program the following tests can be made. 1. if type(d).__name__ == ’int’ You can check the type with the types __name__ member. 2. if type(d) == int ... or you can check the type with the type class name (discussed later). 1 2 3 4 5 6 7 8 9 10 11 12 13 8 >>> a = 2 >>> b = 3L >>> c = 4.5 >>> d = 6 + 7j >>> e = "Hello World" >>> type (a) <type ’int’> >>> type (b) <type ’long’> >>> type (c) <type ’float’> >>> type(d) <type ’complex’> That means without applying provider depended tricks. E. Baeck 2.7. CODE BLOCKS 14 15 Page 25 >>> type(e) <type ’str’> You see, ’int’ is integer, ’long’ is long integer, ’float’ is float, ’complex’ is complex and ’str’ is string data type. Furthermore there are some sequences in Python available, which combines the mentioned data types in a more or less sophisticated mode. More about that later. 2.7 Code Blocks One very imported feature of Python is, that code blocks are bracketed by an unique indent. The most programming languages uses there specific code parenthesis. There is one opening parenthesis which starts the code block and there is one closing parenthesis, which closes the code block. The following example shows a code block in C. 1 2 3 4 5 6 if (a { c = d = ... } > b) a + b a - b Further Lines of C-Code ... The following example shows a code block in Fortran77. 1 2 3 4 5 if (a c = d = ... endif .gt. b) then a + b a - b Further Lines of Fortran-Code ... Compared with this in Python the code block is bracketed by indent as follows. 1 2 3 4 5 if a > b: c = a + b d = a - b ... Further Lines of Python-Code ... a = b One statement which uses a code block, in this case an if statement, is closed by a colon. After the colon an unique indent for the lines of the code block must be used. If not, it will be a syntax error. The code block is closed, if the last line of the code block is the last line of the whole code, or is closed by a line of code which is indented like the opening statement. In our example the assignment a=b has the same indent as the if statement and so this line will be the first line of code outside our code block. 23.4.2015 Analysis of Structures - SS 15 Page 26 2.8 Globales In Python a variable will be created, if an assignment is done. If so, it is impossible to access a variable, which is introduced elsewhere, i.e. a global variable. If we now want to access such a variable, we have to declare it inside our local function as a global variable. If we do this, the Python interpreter is looking for a variable with such a name inside the name space of the calling function. Example 2.2 shows how to access a global variable and which effect we have, if the value is changed inside the called function. Listing 2.2: Testing Global Variables 1 # usage of global 2 3 4 def doSomething(a): global b 5 print "doSomething...: a = %d, b = %d" % (a,b) 6 7 a = 10 # local variable b = 20 # global variable c = 30 # local variable print "doSomething...: a = %d, b = %d, c = %d" % (a,b,c) 8 9 10 11 12 13 14 15 16 17 18 a = 1 b = 2 c = 3 print "before calling: a = %d, b = %d, c = %d" % (a,b,c) doSomething(a) print "after calling.: a = %d, b = %d, c = %d" % (a,b,c) Listing 2.3: Output from the Testing Example 1 2 3 4 before calling: doSomething...: doSomething...: after calling.: a a a a = = = = 1, b = 2, c = 3 1, b = 2 10, b = 20, c = 30 1, b = 20, c = 3 In the output listing from our little testing example 2.2 we see, that the value of the global variable b is overwritten by the function call, the value of the local variable a only is changed inside the function. After the function call we see, that the variable a remains untouched by the function call. E. Baeck 2.9. LOOP FOR REPETITIONS 2.9 Page 27 Loop for Repetitions Like all programming languages, which make sense, Python also has some implementations of repetitions, of loops. Like in C an explicit loop is available - the for loop - as well as an implicit loop is available - the while loop. The for loop is controlled by an iterated set. One very common variant is the for loop, which is controlled by an iteration counter. The iteration counter will be configured by a range object. The range object has 3 parameters9 . The first parameter sets the start value of the iteration counter, the second parameter sets up the iteration value, which will be the first value that is not performed. The third parameter sets up the increment. 2.9.1 The Factorial The following typical example for the usage of an iterative for loop implements the calculation of the factorial. n! = n Y i (2.5) i=1 The implementation of the factorial is given below. Note the importance of the indent, see section 2.7. 1 2 3 4 5 n = 10 p = 1 for i in range(2,n+1): p *= i print "%3d! = %10d" % (n,p) # # # # # factorial input result variable must be initalized by 1 the counter runs from 2 up to n here we perform the product write the result into the console 6 7 8 >>>... console window ... 10! = 3628800 The second loop type, the while loop is working implicit with a boolean expression, which controls the break condition. If we want to implement the factorial using the while loop we get the following code. 1 2 3 4 5 6 7 n = 10 # factorial input p = 1 # result variable must be initalized by 1 i = 2 # the counter runs from 2 up to n while i <=n: # loop with break condition p *= i # perform the product i += 1 # explicit incrementation print "%3d! = %10d" % (n,p) 8 9 10 >>>... console window ... 10! = 3628800 9 A parameter is a information unit, which is passed to the called object. If more then one parameter is passed, the parameters are separated by commas. 23.4.2015 Page 28 Analysis of Structures - SS 15 The next example shows a nested loop. Very important is the correct code block indent. 1 2 3 for i in range(0,4): # outer loop for j in range(0,2): # inner loop print "i:%2d, j:%2d" % (i,j) # print counter variables 4 5 6 7 8 9 10 11 12 13 >>>... console window ... i: 0, j: 0 i: 0, j: 1 i: 1, j: 0 i: 1, j: 1 i: 2, j: 0 i: 2, j: 1 i: 3, j: 0 i: 3, j: 1 For the detailed controlling of the loops cycles two statements are available. • continue If the continue statement is used a cycle is immediately stopped and the next cycle is started. • break If the break statement is used a cycle is immediately stopped and the loop is exited. The next example shows an application of the continue statement. A loop is performed with the values 0 · · · 4. The cycle with the counter 2 is prematurely canceld. 1 2 3 4 5 6 7 8 >>> for i in range(0,5): ... if i == 2: continue ... print "i=%d" % i ... i=0 i=1 i=3 i=4 One very interesting feature of Python is the long integer arithmetic. So we can calculate incredible large factorials. Figure 2.1 shows the code in the upper window. The console window shows the result. A number with a lot of digits and every digit is exact. The next example shows the calculation of the factorial using a float. The float factorial can only be evaluated up to 170! = 7.25742e+306. In the case of 400! we will get an overflow, because the exponent exceeds the available memory in 8 bytes (see figure 2.2). E. Baeck 2.9. LOOP FOR REPETITIONS Page 29 Figure 2.1: Calculating the Factorial for a long integer Figure 2.2: Calculating the Factorial for a float 23.4.2015 Analysis of Structures - SS 15 Page 30 2.9.2 Floating Point Precision The Precision-Finder is a nice little program, which analysis the relative precision of a given float format. 2.9.2.1 Description of the Application As we know, the computer cannot handle an infinite number of floating-point digits. Therefore it is important to know who many digits are available in a floating-point format (float). Figure 2.3 shows a flow chart of an algorithm which continually reduces the value of a variable. After each reduction the sum of the fixed and the reduced variable is calculated. If now the contribution of the reduced is vanishing, we have reached the total limit. Beyond this limit the contributing information is totally expunged. To get the relative precision, i.e. relating to one, we have to take back the last reduction, if we have reached the limit. 2.9.2.2 Exercise Please implement the algorithm of the Precision-Finder for the Python float format within a little script like the famous Hello World example (see section 1.3). Start Initializing: X1 = 1.; X2 = 1.; D = 2.; Reductionstep: X2 = X2/D; Accumulationstep: S = X1 +X2; yes S > X1 no Resultstep: Precission = X2 * D; Stop Figure 2.3: Flowchart for a Precision Finder E. Baeck 2.10. FUNCTIONS 2.10 Page 31 Functions A function is a callable type. A function starts with the def command. The parameter list succeeds the function name and consists of names separated by commas. The return of return values is optional. If more then one return value should be given, the return values are separated by commas and the calling code will get a tuple containing all the return values. A single return value is not returned in a tuple. A nice example to study cases of a solution and their specific return values is discussed in section 2.11. 1 2 3 def <name> (Parameter list): Code Block return <Return Object list> Listing 2.4 shows two functions with two parameters. The first function returns 3 values in a tuple, the second function returns only one value. This value is returned without a tuple. Listing 2.4: Functions with Parameters and Return Values 1 2 3 # function with 2 parameters and 3 returns def myFunction1(a,b): return a, b, a**b 4 5 6 7 # function with 2 parameters and 1 return def myFunction2(a,b): return a**b 8 9 10 11 # function with 3 parameters and 1 return in a tuple def myFunction3(a,b): return (a**b,) 12 13 14 15 ret = myFunction1 (2,3) print "return of myFunction1... : ", ret print "length of returned tuple : ", len(ret) 16 17 18 ret = myFunction2 (2,3) print "return of myFunction2... : ", ret 19 20 21 22 ret = myFunction3 (2,3) print "return of myFunction3... : ", ret print "length of returned tuple : ", len(ret) Runing the code of listing 2.4 will print the following output. 1 2 3 4 5 return length return return length of of of of of myFunction1... returned tuple myFunction2... myFunction3... returned tuple : : : : : (2, 3, 8) 3 8 (8,) 1 23.4.2015 Analysis of Structures - SS 15 Page 32 2.11 Branches for Decisions Decisions are made by the usage of the if statement. The if statement is defined as follows. 1 2 3 4 5 6 if [boolean expression 1]: code block 1 elif [boolean expression 2]: code block 2 elif [boolean expression 3]: code block 3 7 8 9 10 ... else: code block else 2.11.1 How to Solve a Quadratic Equation The calculation of roots of a quadratic equation is a nice and simple example to show the application of the if statement. The quadratic equation is given by a · x2 + b · x + c = 0 (2.6) If we want to solve the quadratic equation, we have to analyze the available cases. A general solution must handle the following cases. • constant case a=b=c=0 • linear case a = 0, b 6= 0 • quadratic case a 6= 0 • no solution case a = b = 0 and c 6= 0 E. Baeck 2.11. BRANCHES FOR DECISIONS 2.11.1.1 Page 33 A Flow-Chart The following flow chart shows all the case, which we have to handle. The algorithm is given for a real arithmetic, i.e. no complex data types are used. The relevant source code will be developed within the next section. Start a=0 yes no c=0 no no x = − cb d = b2 − 4 · a · c d<0 yes b=0 yes x1,2 = Stop √ −b±i −d 2·a no solution yes infinit solutions Stop Stop Stop no x1,2 = 2.11.1.2 √ −b± d 2·a Stop The Implementation The implementation uses a real as well a complex arithmetic importing the module math and cmath. The solver of the quadratic equation is implemented in a function called QuadSolve. The function analyzes the different cases and returns in the constant case only a comment, in the linear case the solution value and a comment. In the quadratic case 2 values and a comment were returned. All return values are packed into a tuple. The case can be identified form the calling program using the function len, which returns the number of items of a tuple. To branch in the real respectively the complex quadratic case, we have to check the type of the returned result values using the Python function type. This we see in line 75. If the type is complex, we have to extract the real and the imaginary part of the number, to print it using the print statement. From line 44 on, we can see that using the complex square-root function cmath.sqrt automatically we get a complex result. So it’s possible too, to return in any quadratic case a complex results. If so, we don’t need to branch. To avoid the testing for zero, which would produce numerical problems in principle, we set the relative precision to 10−15 . An absolute value less then this relative precision is treated as zero value. Listing 2.5: Implementation of the Quadratic-Equation-Solver 1 2 3 4 5 6 ’’’ solution of a quadratic equation - handling all special cases ’’’ # import the used functions from math import fabs as abs, sqrt 23.4.2015 Analysis of Structures - SS 15 Page 34 7 import cmath 8 9 def quadSolve(a,b,c): 10 p = 1.e-15 11 12 # precision of float, is used to # avoid to test for zero with == 13 # case: a=0 if abs(a) < p: 14 15 16 # case: b=0 if abs(b) < p: 17 18 19 # case: c=0 if abs(c) < p: return ("Infinit number of solutions.",) 20 21 22 23 # case: c!=0 else: return ("No solution possible.",) 24 25 26 27 # case b != 0 (linear case) else: x = -c/b return (x, "Linear case found.") 28 29 30 31 32 # case a != 0 (quadratic case) else: d = b**2 -4.*a*c 33 34 35 36 # real case if d >= 0.: s = sqrt(d) x1 = 1./(2.*a)*(-b +s) x2 = 1./(2.*a)*(-b -s) return (x1,x2,"Real case of the quadratic problem.") 37 38 39 40 41 42 43 # complex case else: s = cmath.sqrt(d) x1= 1/(2.*a)*(-b +s) x2= 1/(2.*a)*(-b -s) return (x1,x2,"Complex case of the quadratic problem.") 44 45 46 47 48 49 50 51 52 53 54 55 # # a b c -------- main program ------------------input section = 1. = 0. = 4. 56 57 58 # call of QaudSolve function result = quadSolve(a,b,c) 59 60 61 62 # check the result tuple, to select the found case values = len(result) print "%d values found in return" % values E. Baeck 2.12. FUNCTION EXAMPLES Page 35 63 64 65 66 67 # format the found result case # no or infinit solution(s) if values == 1: print result[0] 68 69 70 71 # linear case elif values == 2: print "x = %f, info: %s" % result # (result[0],result[1]) 72 73 74 75 76 77 78 79 80 81 # quadratic case elif values == 3: if type(result[0]) == complex: print "x1 = %f+(%fi), x2= %f+(%fi), info: %s" \ % (result[0].real,result[0].imag, result[1].real,result[1].imag, result[2]) else: print "x1 = %f, x2 = %f, info: %s" % result 2.12 Function Examples In this section we will discuss a new abs function and the Newton algorithm using a function as parameter. 2.12.1 An Abs-Function with Type-Checking The following code shows a special version of the abs function. The data type is checked first. Only int, long or float type are senseful supported. We check the type with the type function. The type function returns a type object. Within the function we check the object member __name__. If the type is not supported, a error string is returned. If the type is supported, the return value is return. The calling program checks the return values type using the object name (int, long and float). Listing 2.6: Abs Function with Type Checking 1 2 # declaring our version of an abs function def myAbs(x): 3 4 5 6 7 8 9 # process only sensful data types # here we use the type class member __name__ # to check the data type t = type(x).__name__ if t != ’int’ and t != ’long’ and t != ’float’: return "Error: Type ’%s’ is not allowed." %t 10 11 12 13 # if data type ok change sign if necessary if x < 0.: return -x return x 14 15 16 17 # input section y = -4 # y = "hello" # test 1 # test 2 18 23.4.2015 Analysis of Structures - SS 15 Page 36 19 20 # function call z = myAbs(y) 21 22 23 24 25 26 27 # get return type t = type(z) if t == str: print z else: print "Absolute Value of %f = %f" % (y,z) 2.12.2 # second version to check the type # print error message # print result The Newton-Algorithm The following example shows, how to pass a function as a functions parameter. Within the Newton’s algorithm a root of an equation should be calculated. So we have to specify the function of interest. This function can be considered as an input parameter. This function name is passed to the derivative calculator and to the newton main routine. Further we see, that it’s recommended to use standard parameter, to configure the algorithm. We introduce the precision parameter e, which sets up the threshold for a zero compare. Further we need the step width to calculate the derivative of the function of our interest. Figure 2.4: Scheme of the Newton Algorithm The derivative - it’s called fs in the code - is calculated numerical as follows. f 0 (x) = df ≈ dx h h f (x + ) − f (x − ) /h 2 2 (2.7) The Newton scheme can be described as follows. xi+1 = xi − f (x) f 0 (x) (2.8) There are three possible situations to handle within the iteration loop. • The function value is vanishing with respect to our selected precision. The iteration loop will be broken and the found result is passed back to the caller. • The slope of the function is vanishing. This situation can not be handled by the simple iteration scheme. The iteration will be broken with an error message. • During the iteration each cycle is counted. So the iteration loop will be broken, if the maximum available iterations are reached. The actual values and an error message is passed bake to the caller. E. Baeck 2.12. FUNCTION EXAMPLES Page 37 The following flow chart is given for a simple implementation of Newton’s algorithm. Start Initializing: x = x0 ; i = 0; i ≥ imax yes NO ROOT! Stop ROOT: x Stop NO SLOPE! Stop no F x = f (x) |F x| ≤ yes no F s = f 0 (x) |F s| ≤ yes no x = x− Fx Fs i = i+1 Figure 2.5: Flow Chart for a Simple Newton Implementation The code consists of the following functions. • myF, the function of our interest. • fs, the function which calculates the slope of a given function numerically. • newton, implements the newton scheme. 23.4.2015 Analysis of Structures - SS 15 Page 38 Listing 2.7: Implementation of Newton’s-Alogrithm 1 from math import fabs as abs # import the fabs as abs 2 3 4 5 # implementation of the function of interest def myF(x): return x**2 +1. 6 7 8 9 10 11 # calculating the derivative def fs(f,x,h=1.e-6): h = float(h) x = float(x) return (f(x+0.5*h) - f(x-0.5*h))/h 12 13 14 # implementation of a newton algorithm def newton(f,x0,e=1.e-10,h=1.e-6,imax=100): 15 16 error = None # initialize the error code with None h = float(h) e = float(e) x1= float(x0) # we need some floats i = 1 while True: # iteration counter 17 18 19 20 # x to interate 21 22 23 24 f0 = f(x1) # function’s value if abs(f0) < e: break 25 26 27 28 # calculating the derivative f1 = fs(f,x1,h) if abs(f1) < e: error = "*** Error: vanishing derivate!" break 29 30 31 32 33 34 # available iterations exceeded if i >= imax: error = "*** Error: no root found!" break 35 36 37 38 39 # calculating the values for next iteration x1 -= f0/f1 40 41 42 # increment the iteration counter i+=1 43 44 45 # return the actual position, the function value # and the functions slope, the number of performed # iterations and the error code. return (x1,f0,f1,i,error) 46 47 48 49 50 51 52 53 54 # the function newton is called with standard parameters # we pass the function of interest and a supposed start # position res = newton(myF,4.) E. Baeck 2.13. DATA SEQUENCES Page 39 55 56 57 # the returned tuple is printed into the console window print res 2.13 Data Sequences In Python there are some sequential data types available. • Strings, a list of characters. • Tuples are fixed sequences, which are only extensible. The advantage of tuples is the performance. • Lists are changeable sequences but with lower performance. 2.13.1 Working with Tuples The following example shows the creation of a tuple. An empty tuple is declared. We extend the tuple with a one element tuple - note the comma. The second extension extends the tuple with a two element tuple. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 >>> t = () # initialize the tuple >>> t () >>> t += (1,) # append the tuple with a number >>> t (1,) >>> t += (2,3) # append at tuple with a second tuple >>> t (1, 2, 3) >>> t[0] # calling the tuple’s first element 1 >>> t[2] # calling the tuple’s third element 3 >>> t[3] # an error occur, if the index goes out of range Traceback (most recent call last): File "<interactive input>", line 1, in <module> IndexError: tuple index out of range >>> len(t) # the number of elements is given by the len function 3 Note, that tuples are input data for complex string formatings. 1 2 >>> "x1 = %8.2f, x2 = %8.3f" % (12.3456,12.3456) ’x1 = 12.35, x2 = 12.346’ With the function tuple() a tuple can be created from an iterable object like string or list. 1 2 3 4 5 6 >>> T >>> T (1.0, >>> L >>> L [1.0, =tuple([1.,2.]) 2.0) = list(T) 2.0] 23.4.2015 Analysis of Structures - SS 15 Page 40 7 8 9 >>> T = tuple("Hello World") >>> T (’H’, ’e’, ’l’, ’l’, ’o’, ’ ’, ’W’, ’o’, ’r’, ’l’, ’d’) Note, that the data of a list can be converted into a tuple using the function tuple() and reverse into a list using the function list(). 2.13.2 Working with Lists Lists are more flexible as tuples. Creating and extending lists can be coded like in the case of tuples. The only difference is, that lists uses in their declaration the [..] parenthesis. So using lists the code example of the section 2.13.1 can be coded like follows. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> L = {] >>> L [] >>> L += [1,] >>> L [1,] >>> L += [2,3] >>> L [1, 2, 3] >>> L[0] 1 >>> L[2] 3 >>> len(L) 3 # initialize the list # append the list with a number # append at list with a second list # calling the lists first element # calling the list’s third element # the number of elements is given by the len function You can read somewhere, that tuples are much more faster than lists. This is surely not valid for every problem. So it’s recommended to check the performance explicitly before you decide to use tuples or lists. The list object offers a wide set of methods. Some of them are discussed in the following table. Methode Comment Example append(i) Append an item to the list. Same as += operator. L.append(3) count(x) Counts the value x in the list. L.count(1.2) extend(s) Append a list to the list. Same as += operator. L.extend(t) index(x) Evaluates the lowest index of the value x in the list. L.index(1.2) insert(i,x) Inserts the object x before the item i. L.insert(2,1.2) pop() Returns the last item and deletes it from the list. L.pop() remove(x) Removes the first item with the value x. L.remove(1.2) reverse() Invert the order of the items of a list. L.reverse() sort() Sort the items of list in ascending order. L.sort(t) E. Baeck 2.13. DATA SEQUENCES Page 41 A very important data object or container class which is used especially from the compiler to implement function calls is called stack. A very common error situation is the so-called stack-overflow error. This error will occur especially, if functions are called recursively. In this case the return addresses are pushed to the stack to remember the way back. If an address should be read from the stack, the address is poped form the stack, which means, that the last value is read and this last value is removed from the stack.10 A stack is also called LIFO, i.e. Last In First Out. Figure 2.7 shows the history of a stack in use. Onto the empty stack the H is pushed. The the e, the l and the o are pushed. After that the o is poped, that means the element is returned to the caller and removed from the stack. With two further pops the elements l and e are removed from the stack. A further push stores the w on the stack. After two pops the w and the e are removed. The H remains on the stack Figure 2.6: Stack Operations Figure 2.7: Stack Example Now, how can we implement a stack with basic Python? We simply need a list instance with the two functions append(), which implements the Push and pop() which implements the Pop of a stack. 2.13.3 Working with Dictionaries A dictionary is a powerful and very general container, it’s also called map, because a key value is mapped onto the pointer of the stored item. In Python an instance pointers is stored in a dictionary using an arbitrary key strings. So a dictionary is like a list container with a more general access. Because the dictionary commonly hashes the key information onto some smaller index lists, the dictionary commonly has a better performance as a linked list container. The dictionary therefore is used in the abaqus class library (we will discuss it later) to store named instances. A dictionary can be created like a list using curled parenthesis. Each key-value pair is separated by a comma. The key is separated from the value by a colon. 1 2 3 4 5 6 7 8 9 10 11 >>> myDict = {’first item’:1, ’second item’:2} >>> print myDict[’second item’] 2 >>> beatles = {} >>> beatles[’drums’] = ’Ringo’ >>> beatles[’bass’] = ’Paul’ >>> beatles[’vocal’] = ’John, Paul, George, Ringo’ >>> beatles[’guitar’] = ’George, John’ >>> print beatles {’guitar’: ’George, John’, ’vocal’: ’John, Paul, George, Ringo’, ’bass’: ’Paul’, ’drums’: ’Ringo’} 10 If we save data on a stack, it’s called push data onto the stack. If we take data from the stack, it’s called pop data from the stack. 23.4.2015 Analysis of Structures - SS 15 Page 42 2.14 Error Handling with Exceptions There are (at least) two distinguishable kinds of errors: syntax errors and exceptions.11 2.14.1 Syntax Errors Syntax errors, also known as parsing errors, are perhaps the most common kind of complaint you get while you are still learning Python. 1 2 3 4 5 >>> while True print ’Hello world’ File "<stdin>", line 1, in ? while True print ’Hello world’ ˆ SyntaxError: invalid syntax The parser repeats the offending line and displays a little arrow pointing at the earliest point in the line where the error was detected. The error is caused by (or at least detected at) the token preceding the arrow: in the example, the error is detected at the keyword print, since a colon (’:’) is missing before it. File name and line number are printed so you know where to look in case the input came from a script. 2.14.2 Exceptions Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it. Errors detected during execution are called exceptions and are not unconditionally fatal. We will see how to handle them in the next section. Most exceptions are not handled by programs, however, and result in error messages as shown below. 1 2 3 4 5 6 7 8 9 10 11 12 >>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in ? ZeroDivisionError: integer division or modulo by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: name ’spam’ is not defined >>> ’2’ + 2 Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: cannot concatenate ’str’ and ’int’ objects The last line of the error message indicates what happened. Exceptions come in different types, and the type is printed as part of the message: the types in the example are ZeroDivisionError, NameError and TypeError. The string printed as the exception type is the name of the built-in exception that occurred. This is true for all built-in exceptions, but need not be true for user-defined exceptions (although it is a useful convention). Standard exception names are built-in identifiers (not reserved keywords). The rest of the line provides detail based on the type of exception and what caused it. 11 Parts of this section are taken from the Python 2.7 manual. E. Baeck 2.14. ERROR HANDLING WITH EXCEPTIONS Page 43 The preceding part of the error message shows the context where the exception happened, in the form of a stack traceback. In general it contains a stack traceback listing source lines; however, it will not display lines read from standard input. 2.14.3 Handling Exceptions It is possible to write programs that handle selected exceptions. Look at the following example, which asks the user for input until a valid integer has been entered, but allows the user to interrupt the program (using Control-C or whatever the operating system supports); note that a user-generated interruption is signalled by raising the KeyboardInterrupt exception. 1 2 3 4 5 6 7 >>> while True: ... try: ... x = int(raw_input("Please enter a number: ")) ... break ... except ValueError: ... print "Oops! That was no valid number. Try again..." ... The try statement works as follows. • First, the try clause (the statement(s) between the try and except keywords) is executed. • If no exception occurs, the except clause is skipped and execution of the try is finished. • If an exception occurs during execution of the try clause, the rest of the clause is skipped. Then if its type matches the exception named after the except keyword, the except clause is executed, and then execution continues after the try. • If an exception occurs which does not match the exception named in the except clause, it is passed on to outer try; if no handler is found, it is an unhandled exception and execution stops with a message as shown above. A try may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same try. An except clause may name multiple exceptions as a parenthesized tuple. 1 2 ... except (RuntimeError, TypeError, NameError): ... pass In the next example we open a file with the name ’input.dat’. We read one line and strip the leading and trailing blanks. The result will then be converted to an integer. We know that this is not possible in every case, so we want to handle possible errors by our error handler. 1 try: 2 f = open(’input.dat’) # let’s open then input file, s = f.readline() # read the first line and i = int(s.strip()) # strip the white spaces except IOError as e: # catch the io-errors print "I/O error(%d): %s" % (e.errno, e.strerror) except ValueError: # catch conversion errors 3 4 5 6 7 23.4.2015 Analysis of Structures - SS 15 Page 44 8 9 10 print "Could not convert data to an integer." except: # catch the rest print "Unknown error!" 2.14.4 Raise Exceptions If we want to avoid runtime errors, we have to check our data base. In an classical approach, we have to check the data, and break each activity jumping back to the origin of our call. Sometimes especial if the calling hierarchy is extensive, it becomes an evil and tedious work to do this. On the other hand in most modern languages we can use exception handlers to do this work for us. The only thing we have to do is, to raise our own exception and check for it within an except clause. The first listing 2.8 we call our square root function with a negative value. Listing 2.8: Our Unsave mySqrt-Function 1 import math 2 3 4 def mySqrt(x): return math.sqrt(x) 5 6 print mySqrt(-2) The program crashes with the following output. 1 2 3 4 5 6 7 8 9 10 11 Traceback (most recent call last): File "C:\Python27\Lib\site-packages\Pythonwin\pywin\framework\scriptutils.py", line 326, in RunScript exec codeObject in __main__.__dict__ File "C:\UniDue\CM\Cm-AoS\AOS-BookOfExamples\Py\Code\ExceptionEx01.py", line 7, in <module> print Sqrt(-2) File "C:\UniDue\CM\Cm-AoS\AOS-BookOfExamples\Py\Code\ExceptionEx01.py", line 4, in mySqrt return math.sqrt(x) ValueError: math domain error We see that obviously the function sqrt is not able to handle negative numbers. In the next example we check the root’s value and raise an exception to break the function. This exception itself is caught by the main program with an try clause. Listing 2.9: mySqrt-Function with Checking 1 import math 2 3 4 5 6 def mySqrt(x): if x < 0.: raise Exception("error: negative input value %e!" % x) return math.sqrt(x) 7 8 try: 9 print mySqrt(-2) except Exception as e: print e 10 11 E. Baeck 2.15. RANDOM NUMBERS Page 45 In listing 2.9 we see, that if negative values are passed, the function will raise an exception. Using the Exception class we send a string information, which can be caught within the except clause. The output of this version is given below. 1 error: negative input value -2.000000e+00! We see, that the concept of an exception handling is a powerful feature to implement error checking. 2.15 Random Numbers Because a computer is system, which works systematically reproducible, a computer will not be able to create real random numbers. If we start a program twice with exact the same input values, the result also will be the same. But with a little trick, we can produce so called pseudo random numbers. If we use the time as input for the initialization of our random number generator, we will be sure, that it will be extremely improbable, to get twice the same random number set. So every programing language has it’s own random number generator. Python offers us a build-in package, which is called random. Like in C the random number generator is to initialize with a function seed(). This function uses the computer time information to make varying starting conditions. The following example will discuss two features of the Python library. • How can we use the random package to create random numbers or to create a random order of lists, i.e. shuffle the list’s data. • How can we create simple 2d diagram plots using the matplotlib package. The example code consists of three sections. • At the beginning the lists xPos and yPos are filled with random numbers. This random x and y values are interpreted as point coordinates. Two subsequent points are joint by a line. • Within a second step the list’s element are sorted. The result is plotted again. The result is an increasing function. • Within the third step the sorted lists are shuffled by the random package. The random order of the random numbers is plotted again. Figure 2.8 shows the walk for random points (blue line), for sorted points (red line) and for shuffled points (green line). The left figure shows the content of the created png file, the right figure shows the plot within the viewers window. Listing 2.10: Implementation a Kind of Random Walk visualized with two Lists of Random Numbers 1 2 import random import pylab # import the random package # part of matplotlib 3 4 5 6 7 # create random ’count’ numbers in the range [lower:upper] # a list is returned to the caller def getRandomNumbers(lower,upper,count): random.seed() # initialization of random number generator 23.4.2015 Analysis of Structures - SS 15 Page 46 Figure 2.8: Random, Sorted and Shuffled Walk 8 numbers = [] for i in range(count): r = lower + (upper-lower)*random.random() numbers.append(r) return numbers 9 10 11 12 13 14 15 16 17 18 # main program -----------------------------L = -10. # lower bound U = +10. # upper bound xN = 100 # number of randoms 19 20 21 22 # random positions xPos = getRandomNumbers(L,U,xN) yPos = getRandomNumbers(L,U,xN) # ...in x-direction # ...in y-direction 23 24 25 # creating plot of original random values xPos/yPos pylab.plot(xPos,yPos,"b") 26 27 28 29 # sort the random values xPos.sort() yPos.sort() 30 31 32 # creating plot of sorted values xPos/yPos pylab.plot(xPos,yPos,"r") 33 34 35 36 # create a random order random.shuffle(xPos) random.shuffle(yPos) 37 38 39 # creating plot of the shuffeled values xPos/yPos pylab.plot(xPos,yPos,"g") 40 41 42 # save the plot in a figure using an png-format pylab.savefig("RandomWalk.png") 43 44 # plot a figure in the matplotlib viewer E. Baeck 2.16. DATE, TIME AND TIMESPAN 45 Page 47 pylab.show() 46 47 48 49 2.16 # at the end we should close the plot, # to avoid memory effects pylab.close() Date, Time and Timespan Date and time functions in common are very important and therefor Python supports this with a standard package datetime. Within this package you will find some very simple and powerful objects and functions. The package is loaded with the following statement. We load from datetime the subpackage datetime and set the alias name time. The second line shows, how to get the actual time. The datetime object contents from the left to the right the year, the month, the day, the hour, the minutes, the seconds and the microseconds. Because the computer does not support microseconds but milliseconds the measured milliseconds are multiplied with the factor 1000. 1 2 3 >>> from datetime import datetime as time >>> time.now() datetime.datetime(2010, 11, 4, 19, 25, 33, 15000) To implement some performance checking code it is very useful to have a timespan, which is the difference of two datetime objects. The timespan object is called timedelta and is as well an object of the datetime package. If we now want to check the performance of a code snippet, we will implement the following code. First we have to import the datetime package. Then we get the actual time and assign this object to the variable t1. Then the code to check follows. Within a last step we again create a datetime object calling the time.now() function and assigning the object to the variable t2. If we subtract t1 from t2 we will get a timedelta object. The timedelta objects contents the number of days, the number of seconds and the number of microseconds of the time difference. 1 2 3 4 5 6 7 from datetime import datetime as time t1 = time.now() ... our code snippet ... t2 = time.now() t3 = t2 -t1 print t3 >>> datetime.timedelta(0, 12, 906000) If we want to have the time difference in seconds, we have to write a little function, which adds all this time parts in the unit second. If we suppose, that T is a valid timedelta object, we extract the seconds with the seconds attribute and assign it to our seconds variable. The next part handles the microseconds. We divide them by 106 and add the part to the seconds variable. The last part calculates the seconds of the given hours and add this value to our second variable. The value is returned to the caller. 1 2 3 4 5 def getTimeSpanInSeconds(T): secs = float(T.seconds) secs += float(T.microseconds)/1.e6 secs += float(t.days)*24.*60.*60. return secs 23.4.2015 Analysis of Structures - SS 15 Page 48 One very important application of the timedelta object is the measurement of a program’s performance. The next example shows how to investigate the performance of the creation of a tuple and the creation of a list. Here we apply the above discussed function getTimeSpanInSeconds. Listing 2.11: Implementation of a Performance-Checker 1 2 # we need the datetime package from datetime import datetime as time 3 4 5 6 7 8 9 # calculating the timespan in floats def getTimeSpanInSeconds(T): t = float(T.seconds) t+= float(T.microseconds)/1.e6 t+= float(T.days)*24.*60.*60 return t 10 11 12 13 14 nX = 100000 T = () L = [] # number of entities to create # empty tuple # empty list 15 16 17 18 19 20 # create a tuple with nX items t1 = time.now() for i in range(nX): T += (i,) t2 = time.now() 21 22 23 sT = getTimeSpanInSeconds(t2-t1) # and calculate the timespan print "Creation of a tuple with %d items: %.3fs" % (nX,sT) 24 25 26 27 28 29 30 31 # create a list with nX items t1 = time.now() for i in range(nX): L += [i,] t2 = time.now() sL = getTimeSpanInSeconds(t2-t1) # and calculate the timespan print "creation of a list with %d items: %.3fs" % (nX,sL) 32 33 34 35 36 37 38 # convert the list into a tuple t1 = time.now() TL = tuple(L) t2 = time.now() sTL= getTimeSpanInSeconds(t2-t1) # and calculate the timespan print "Create tuple from list: %.3fs" % sTL The console output on a one year old double processor notebook is given below. You see that it is very wasteful to create a tuple with a lot of items concatenating them with a += operator. On the other hand a list with the some number of items is created in no time. And the conversion of a list into a tuple costs nothing. So we can resume the the creation of a very large tuple should be done by creating a list and converting it into a tuple. 1 2 3 4 c:\CM\Cm-AoS\WS1011\Python>Timecheck1.py Creation of a tuple with 100000 items: 33.297s Creation of a list with 100000 items: 0.063s Create tuple from list: 0.000s E. Baeck 2.17. WORKING WITH FILES 2.17 Page 49 Working with Files A file is a sequence of bytes written to an external media like a hard disk, a CD or an USB stick. A file’s bytes are indexed with an integer. So the the fist byte of a file has the index 0 and the second byte the index 1 and so on. A text file is structured with a line break character \n. If a text line should be read, the io12 system reads all the bytes up to the next line break. Python as a lot of functions which are able to handle every situation concerning files. So files can be created. We can read data from a file or write data into a file. We can close a file. We can delete a file or change it’s permissions. 2.17.1 Open a File Within Python a file is an object an has a lot of attributes and methods (see also the next section). The access to a file is initialized with the file constructor. The file constructor needs the name of the file and the type of access. The file access type is like C’s access type of the function fopen13 . The type ’r’ is used to read from a file, the type ’w’ is used to create a new file for writing. An old still existing file will be deleted. The type ’a’ is used to write appending to an existing or a new file. If we want to read from and write into a file the type ’w+’ can be used. 1 2 3 4 5 f = file(<filename>,<access-type>) ... ... example ... ... f = file("myfile.txt", "w") 2.17.2 Write Data into a File The file method write writes a stream of bytes into a file. In contrast to the print function the write method adds no carriage return to the file. If a line of text should be written, the text should be closed with an \n character. 1 2 3 4 5 12 13 #> f.write(<byte-stream>) #> ... #> ... example ... #> ... f.write("This is my first line\n") io means input output. The function fopen is C’s classical function to open a file. The return will be a pointer to a FILE structure. 23.4.2015 Analysis of Structures - SS 15 Page 50 2.17.3 Close a File After the usage of a file, the file should be closed. 1 2 3 4 5 6 7 #> f.close() #> ... #> ... example ... #> ... f.open("log.txt","a") f.write("Write a little log line\n") f.close() 2.17.4 Read Data from a Text File A frequently problem is parsing an input file for some control data. This we can do with a statement which reads the entire file into a list container using the method readlines. If we have read this lines then we can iterate this container using the for statement. Doing this we will get line by line. A line is a string which is terminated by a carriage return character. This string we can split up into pieces using white spaces (blanks and tabs) as separators. Listing 2.12: Read Data from a Text File 1 2 # import the fileinput package import fileinput 3 4 5 h = -1. w = -1. # initialize H value # initialize W value file = open("data.in") lines = file.readlines() file.close() print lines # # # # for line in lines: # iterate all lines 6 7 8 9 10 open the file read entire content close the file check the container 11 12 13 14 words = line.split() # split up into pieces if len(words) < 1: continue if words[0] == "#": continue # ignore blank lines # ignore comment lines if words[0] == "H": h = float(words[1]) # evaluate H key elif words[0] == "W": w = float(words[1]) # evaluate H key 15 16 17 18 19 20 21 22 23 24 25 26 print "h = %8.2f" % h print "w = %8.2f" % w E. Baeck # print H value # print W value 2.17. WORKING WITH FILES Page 51 In our example we want to read and analyze the input file data.in (see listing 2.13). Listing 2.13: Key Value Input Control File 1 2 3 4 # this is a comment # format is key - value like H 100. W 10. If we run the script 2.12 we will get the console output 2.14. In line 1 we can see, that readlines will create a list container (see square brackets). Listing 2.14: Consol Output 1 2 3 [’# this is a comment\n’, ’# format is key - value like\n’, ’H 10.\n’] h = 100.00 w = 10.00 2.17.5 100.\n’, ’W A Logger-Function In many cases it is very useful to have logging, to know, what is going on in an application. A logging function has to flush the data after every write event to the disk to avoid the loss of data. If a write event is executed first the data is written into a buffer memory. So if the program is crashing before the data are written physically to the disk, the data are lost and the effect of such a log is rather suboptimal. One very secure kind of logging is, to open a file for writing in append mode, write the desired data to the file and close the file after having written the data. This is secure but the performance is low. In the next example an implementation of a logging function is given. Later we will implement this function as part of a logger class. Listing 2.15: Implementation of a Logger-Function 1 2 3 # the filename is passed as a first argument, # the string to print ist passed as a second def appendLog(filename,text): 4 5 6 7 t = time.now() # get actual time tstamp = "%2.2d.%2.2d.%2.2d|" % (t.hour,t.minute,t.second) textout = tstamp + text # copy the stamp in front of the comment 8 9 10 11 f = file(filename,"a") f.write(textout + "\n") f.close() # open the log file for appending "a" # write the text into the file with linebreak # close the log, to save the data on disc print textout # write the log to the screen 12 13 23.4.2015 Analysis of Structures - SS 15 Page 52 2.18 OOP with Classes Python is an object orientated programing language14 . Everything within Python is implemented as an object, even a simple integer number. So what is the concept of a class? A class or an object combines data, called attributes, with functions, called methods. This can be described by so called UML15 An instance of a class, that is the realization of the class in memory, is created simply by assigning the class’s name followed by the constructors parameter list to a symbolic name, which than is the pointer or the reference to this instance. Within the class the self pointer to the actual instance is called self. To access member attributes or methods of a class the dot notation is used, i.e. <instance>.<member>. We can remove an instance of a class by calling the del operator (del example). The references to an instance are handled by the python runtime system. If an instance has lost all it’s references, the instance is removed from the memory.16 2.18.1 Some UML Diagrams UML structure diagrams of the emphasize the things that must be present in the system being modeled. Since structure diagrams represent the structure they are used extensively in documenting the architecture of software systems. In our description of the examples we want to implement we use the Class Diagram which describes the structure of a system by showing the system’s classes, their attributes, and the relationships among the classes. A UML class diagram (see figure 2.9) consists of a rectangular box, which is divided into three sections. The fist section contents the class’s name. This name is written centered in bold letters. The second section contents the attribute’s names of the class and the third section contents the method’s names. Class Name Attributes Additional information within a simple Note diagram Methods Figure 2.9: A UML Class and Note Diagram A UML note diagram (see figure 2.9) consists of a stylized note sheet which is filled with some information. MyClass this is my 1st class Attribute1 Attribute2 A UML note diagram (see figure 2.10) will be assigned to an other component of the diagram scene with a simple line. Method1 Method2 Figure 2.10: A UML Note Diagram Assignment 14 Object orientated Programming is often used with the abbreviation OOP. The Unified Modeling Language includes a set of graphic notation techniques to create visual models of software-intensive systems. The Unified Modeling Language is an international standard see [4], UML 2.3 was formally released in May 2010. 16 This is also called Garbage Collector. In contrast to this in poor C or C++ an once allocated object has to be removed explicitly from the memory to avoid so called memory leaks, this are blocked parts of the applications memory, which remain unaccessible until the application is closed. 15 E. Baeck 2.18. OOP WITH CLASSES Page 53 Figure 2.11 shows how to draw diagrams for inheriting classes. An arrow with a white filled arrowhead points from the inheriting class, the special class, to the inherited class, the base class. The attributes and the methods of the Base class are now available in the name space of the inheriting class, i.e. the derived class now has the attributes attributB1, attributB2, attributS1 and attributS2. Base Class Derived Class − attributeB1 + attributeS1 − attributeB2 − attributeS2 − methodB1 + methodS1 + methodB2 + methodS2 Figure 2.11: A UML Inheritance Diagram The prefixes + and − describe the access permissions. + items are accessible from outside. In C++ they get the public attribute. − items are not accessible from outside, only methods of their class are allowed to access. In C++ they get the private attribute. There are to different class items, an item which is related only to a class and an item which is related to an instance. An instance related item needs an instance. So if an instance is created the item is available with this instance pointer. Without an instance the item is not available. So an instance related item can have an arbitrary multiplicity, so every instance has it’s own item. This item is listed without an underlining. Base Class − attributeB1 − attributeB2 − methodB1 + methodB2 On the other hand there are items which are related to the class. This item Figure 2.12: Item Types are accessed using the class name as a prefix and not the instance name. A class item one once exists in the application. A class related item is listed in the UML class diagram with an underlining. Class 1 List A List B Method 1 3..* sorted Class A 1 random Class B Figure 2.13 shows a aggregation and a composition. An aggregation is drawn by a white filled rhombus. An composition is drawn by a black filled rhombus. Aggregation and compositions describe a container or a list of several instances of an object, which are members of a main class. If for example a profile consists of several parts, the parts can be described as an composition, if a part only exists within a profile. If a part exists also without a profile, the parts are described within the profile with an aggregation. At the ends of the connecting lines the multiplicities are noted. The multiplicity gives the range of referenced inAttribute A2 Attribute B2 stances in the form from..to. For the Class A we have 2 Method A Method B up to infinite instances in an composition, therefor at the end of the line we can not have a multiplicity of zero. In Figure 2.13: A UML Diagram for a Composiour example we have exactly one instance of the class 1. tion and an Aggregation On the other hand Class B is referred to Class 1 within an aggregation. In our example on instance of Class B can be reverenced by an undefined number of instances of Class 1. This is shown by the * icon. On the other hand the class 1 references at least on instance of the Class B. Otherwise the number of references is arbitrary. This is also shown by the * icon. Attribute A1 Attribute B1 23.4.2015 Analysis of Structures - SS 15 Page 54 2.18.2 Implementation of Classes in Python Classes are implemented in Python with the keyword class. The class key is followed by the name of the class, which we find in the upper section of the class diagram. If a class inherits from other classes, it’s called subclass. Then the subclass’s name is followed by a comma separated list of classes to inherit from. This classes are called base classes, superclasses or parent classes. So we have the following syntax, if your class ClassZ should inherit from ClassX and ClassY. The implementation of the class is indented like a function’s code. 1 2 3 4 class ClassZ(ClassX, ClassY): .. class implementation .. ClassX ClassY + attributeX1 + attributeY1 − attributeX2 − attributeY2 − methodX1 − methodY1 + methodX2 + methodY2 ClassZ + attributeZ1 − attributeZ2 + methodZ1 + methodZ2 Figure 2.14: Some Classes 2.18.2.1 Class Constructor The constructor of a class is called if an instance is created. A constructor is a function, i.e. method of a class, having a fixed name. The constructor defines the interface to the class, if an instance should be created. The following code shows the constructor of class ClassZ and how to create it’s instance. The name of the constructor is __init__. The leading two underscores set the constructor to private, i.e. this method is hidden and is not allowed to call from outside. We also should notice, that an instance reference in Python is passed with the key self. This parameter, the first in the implementation, is not explicitly passed (see line 9). Only the the real parameters should be passed in the call. A further thing to mention is, that instance attributes are only created, if an assignment is done.17 1 class ClassZ(ClassX, ClassY): 2 def __init__(self,par1,par2): self.attributeZ1 = par1 self.__attributeZ1 = par2 3 4 5 6 .. 7 8 9 c = ClassZ(1,2) 17 In a compiled language like C++ or FORTRAN a variable, an attribute or a method are created in compile time, they are available already at start up. E. Baeck 2.18. OOP WITH CLASSES 2.18.2.2 Page 55 Class Destructor The destructor of a class is called if an instance is deleted with the del command, which means is freed from the memory. The destructor is a functions, i.e. method of a class, having a fixed name. The following code shows the destructor of class ClassZ. The name of the destructor is __del__. The leading two underscores set the destructor to private, i.e. this method is hidden and is not allowed to call from outside. We also should notice, that an instance reference in Python is passed with the key self. The destructor only has one parameter the instance reference. The example shows like before how we create an instance of the class ClassZ and how to free it. If we free this instance, the instance’s method __del__ is executed and gives us a goodby. 1 class ClassZ(ClassX, ClassY): 2 3 ... 4 5 6 def __del__(self): print "goodby!" 7 8 .. 9 10 11 c = ClassZ(1,2) del c We should consider, that destructors can not handle exceptions. 23.4.2015 Analysis of Structures - SS 15 Page 56 2.18.3 Implementation of a Time Stack Class In section 2.16 we have discussed a little program which implements a time checker using a stack container. We can implement this problem in a much more clearer version, if we do it in terms of the OOP approach. The class TimeCheck, which is introduced, encapsulates the time stack list and the log files name as attributes and the following methods. • set reads the actual time with the datetime.now() and pushes it onto the time stack. • get reads the actual time with the datetime.now(), pops the the last time object from the stack and calculates the timespan. The timespan is optionally printed into the console window or into a log file. • getTime calculates the seconds in floats form the passed timedelta object. The main program, which is used to discuss the class follows the steps of the classical implementation of the problem discussed in section 2.16. We first create an empty tuple and an empty list. Than we create the TimeCheck instance18 simple by assigning it’s name to a variable (s = TimeCheck()). To push a time object onto the stack, we call the class’s member function Set. So, if we want to check the performance of a code, we first call the Set method and after having executed the code, we call the Get method. The Get method will print the comment and will return the used seconds of execution time in floats. The simplest kind of an implementation is the implementation in the file of the main program. This you can see in the following example. A UML diagram of the class TimeCheck is given in figure 2.15. TimeCheck + self. log: name of the log file + self. stack: stack list + self. init (..): contructor + self.set(..): push a time The class TimeCheck contents all the data and functions to use a time stack and to get the time-span information in a proper float format. The class is able to present a formated output of the measured time-spans. + self.getTime(..): calculate seconds + self.get(..): pop a time Figure 2.15: UML-Diagram of the TimeCheck Class 18 A physical created object in memory of a class is called instance of a class. E. Baeck 2.18. OOP WITH CLASSES Page 57 Listing 2.16: Implementation of a Performance-Checker-Class 1 2 3 4 5 6 ’’’ Class to investigate the timespan, to measure the performance by implementing a time stack container ’’’ # - module - member alias from datetime import datetime as time # we need the datetime package 7 8 class TimeCheck(): # class name 9 10 11 12 13 # constructor def __init__(self,log = "timestack.log"): self._stack = [] # empty list self._log = log # set the logfile’s name 14 15 16 17 # set up the actual time and push it onto the stack def set(self): self._stack.append(time.now()) 18 19 20 21 22 # calculate the timespan in float def getTime(self,T): s = float(T.microseconds)/1.e6 +float(T.seconds) +float(T.days)*24.*60.*60. return s 23 24 25 # calculate the timespan with a stack object def get(self,text = ""): 26 27 28 # get the stack length items = len(self._stack) 29 30 31 32 33 34 # assert(items > 0) # to assert, that there is a time object # stored in stack (alternative solution) if items < 1: print "*** Error: timestack empty!" return 35 36 37 38 tStart = self._stack.pop() # pop the time object from the stack tEnd = time.now() # get the end time tDel = self.getTime(tEnd -tStart) # calculate the timedelta in floats 39 40 41 42 43 # we have an maximal indent of 6 columns indent = 6 space1 = "" # leading characters for level indent space2 = "" # trailed characters to aling the comment 44 45 46 47 # set up indent text for i in range(items-1): for i in range(indent -items): space1 += "." space2 += " " # fill in dots # fill in white spaces 48 49 50 51 # print comment if given if len(text) > 0: textout = "%s %8.3f%s: %s" % (space1,tDel,space2,text) 52 53 54 # comment to the screen print textout 23.4.2015 Analysis of Structures - SS 15 Page 58 55 # logging the comment f = file(self._log,"a") f.write(textout + "\n") f.close() 56 57 58 59 60 return tDel 61 # return the seconds in floats 62 63 64 65 66 67 # application of the TimeCheck class print ">> Start.." nX = 50000 T = () # empty tuple L = [] # empty list 68 69 70 # create the timecheck object s = TimeCheck() 71 72 73 # check the total performance s.set() 74 75 76 77 78 # check the performance of the tuples s.set() for i in range(nX): T+= (i,) s.get("Creating %d item tuple." % nX) 79 80 81 82 83 # check the performance of the list s.set() for i in range(nX): L+= [i] s.get("Creating %d item list." % nX) 84 85 86 # check the whole performance s.get("total time") If we run the program, we see that two time stamps are indented with one dot. This timestamps are set for inner performance measurements, that is the time-stamp for the creation of the tuple and after that the time-stamp for the the creation of the list. The last time-stamp shows the total execution time and is created by the first push, i.e. the first Set call and the last pop, i.e. the last Get call. 1 2 3 4 >> Start.. . 8.282 . 0.016 8.329 E. Baeck : Creating 50000 item tuple. : Creating 50000 item list. : total time 3 Python Projects 3.1 Newton, Step2 We have discussed the newton’s scheme in section 2.10. Within this section the project’s code will be extended by plot features. The function and it’s derivative as well as the path of iteration will be plotted using the pylab package. The following features are added to the implementation of newton’s scheme. 1. Import the pylab package. 2. The function getFunctionValues creates a list of the function values for the plot routine. 3. The function getFunctionDerivativeValues creates a list of the function’ derivative values for the plot routine. 4. The function newton will be extended by two lists, which should store the values of the iteration path. After each iteration step, the x-value and it’s function value are stored for later drawings. 59 Analysis of Structures - SS 15 Page 60 A disadvantage of the first implementation in section 2.12.2 is, that the algorithm will fail, if a vanishing slope is found. With a simple work around we can solve this problem. We simple step aside as long as a minimum slope value is reached. The flow chart is given in figure 3.1. Start Initializing: x = x0 ; i = 0; i ≥ imax yes NO ROOT! Stop ROOT: x Stop no F x = f (x) |F x| ≤ yes no F s = f 0 (x) |F s| ≤ yes x = x+∆ i = i+1 no x = x− Fx Fs i = i+1 Figure 3.1: Flow Chart for a Slope Insensitive Newton Implementation Listing 3.1: Implementation of Newton’s-Algorithm, plotting the results 1 2 import pylab from math import fabs as abs # plot a little (not used in this step) # import the fabs as abs 3 4 5 6 7 8 # implementation of the function of interest def myF(x): return x**2 -1. # return x**10 -1. # check the extrem case # return 0. # check the constant 9 10 11 12 # calculating the derivative def fs(f,x,h=1.e-6): h = float(h) E. Baeck 3.1. NEWTON, STEP2 13 14 Page 61 x = float(x) return (f(x+0.5*h) - f(x-0.5*h))/h 15 16 17 # implementation of a newton algorithm def newton(f,x0,e=1.e-10,h=1.e-6,imax=1000): 18 19 error = None # initialize the error code with None h = e = x1= f1= # we need some floats 20 21 22 23 24 float(h) float(e) float(x0) None # x to interate # initialize it for return 25 26 27 28 xL= [] yL= [] sL= [] # list for x-values # list for the function values for x # list for the slope values for x i = 0 while True: # iteration counter 29 30 31 32 33 34 35 36 # available iterations exceeded if i >= imax: error = "*** Error: no root found!" break 37 38 39 40 41 42 43 44 45 # checking for roots f0 = f(x1) # xL.append(x1) # yL.append(f0) # if abs(f0) < e: f1 = fs(f,x1,h) sL.append(f1) # break function’s value save x Value save the functions value save the last nslope value 46 47 48 49 # calculating the derivative f1 = fs(f,x1,h) sL.append(f1) # save the slope value 50 51 52 53 54 if abs(f1) < e: x1 += 1.e2*h i += 1 continue # little step aside # avoid dying with a vanishing slope 55 56 57 # calculating the values for next iteration x1 -= f0/f1 58 59 60 # increment the iteration counter i+=1 61 62 63 64 65 66 # return the actual position, the function value # and the functions slope, the number of performed # iterations and the error code. # index 0 1 2 3 4 5 6 7 return (x1,f0,f1,i,error,xL,yL,sL) 67 68 # creating a list of function values for a x-list 23.4.2015 Analysis of Structures - SS 15 Page 62 69 70 71 72 73 def getFunctionValues(xL,f): yL = [] for x in xL: y = f(x) yL.append(y) # yL += [y] 74 return yL 75 76 77 78 79 80 81 82 # create a list of function’ derivative values def getFunctionDerivateValues(xL,f): yL = [] for x in xL: y = fs(f,x) yL.append(y) 83 return yL 84 85 86 87 88 89 # parameters of the problem xl = -5. # lower bound xu = 5. # upper bound xc = 0.5 # increment 90 91 92 93 # visalization of the function and it’s derivative # create the x-List xList = pylab.arange(xl,xu,xc) 94 95 96 97 # create list of function’s values yList = getFunctionValues(xList,Myf) pylab.plot(xList,yList,’b’) 98 99 100 101 # create list of function’s derivative values ysList = getFunctionDerivateValues(xList,Myf) pylab.plot(xList,ysList,’g’) 102 103 104 105 106 107 # the function newton is called with standard parameters # we pass the function of interest and a supposed start # position x0 = 0. res = newton(Myf,x0) 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 # the returned tuple is printed into the console window print "\n> next run" print "x0 = %f" % x0 # starting value print "x = %f" % res[0] # last x value print "f(x) = %f" % res[1] # last f(x) value if res[2] is not None: print "f’(x) = %f" % res[2] # last f’(x) value print "i = %d" % res[3] # number of iterations if res[4] is not None: print "%s" % res[4] # error message if len(res[5]) > 0: print "--------------x -----------f(x) ----------f’(x)" for i in range(len(res[5])): print "%15.6e %15.6e %15.6e" % (res[5][i],res[6][i],res[7][i]) print "--- end of code ---\n" 124 E. Baeck 3.1. NEWTON, STEP2 125 126 Page 63 # plot the iteration path pylab.plot(res[5],res[6],’rp’) 127 128 129 130 # output of plot data pylab.grid(True) # show grid lines pylab.savefig("NewtonStep2.png") 131 132 133 # - show plot data pylab.show() After the output lists are created the lists are passed to the plot post-processor by the plot method of PyLab. After enabling the grid the plot data are written in a graphic file and are visualized in the PyLab viewer application. Figure 3.2 shows the iteration path for the equation f (x) = x2 − 1 with an starting value of 4 with red dots. Figure 3.2: Newton’s Iteration Path starting with 4 If we would start our calculation with a starting value of 0, we know that the slope will vanish. If we now step aside with the precision of our numerical derivative, we will get the following iteration path. 1 2 3 4 5 6 7 8 9 10 11 12 x0 = 0.000000 x = 1.000000 f(x) = 0.000000 f’(x) = 2.000000 i = 18 --------------x -----------f(x) ----------f’(x) 0.000000e+00 -1.000000e+00 0.000000e+00 1.000000e-04 -1.000000e+00 2.000000e-04 5.000000e+03 2.500000e+07 1.000000e+04 2.500001e+03 6.250002e+06 5.000003e+03 1.250001e+03 1.562501e+06 2.500002e+03 6.250008e+02 3.906250e+05 1.250002e+03 23.4.2015 Analysis of Structures - SS 15 Page 64 13 14 15 16 17 18 19 20 21 22 23 24 25 26 3.125012e+02 9.765602e+04 1.562522e+02 2.441375e+04 7.812931e+01 6.103189e+03 3.907105e+01 1.525547e+03 1.954832e+01 3.811370e+02 9.799739e+00 9.503489e+01 4.950891e+00 2.351133e+01 2.576438e+00 5.638031e+00 1.482285e+00 1.197170e+00 1.078460e+00 1.630751e-01 1.002854e+00 5.716204e-03 1.000004e+00 8.122320e-06 1.000000e+00 1.649347e-11 --- end of code --- 6.250025e+02 3.125044e+02 1.562586e+02 7.814211e+01 3.909665e+01 1.959948e+01 9.901783e+00 5.152875e+00 2.964570e+00 2.156919e+00 2.005708e+00 2.000008e+00 2.000000e+00 We see that due to the very small slope, the resulting jump in the iteration is very large, so that we need some iterations more to get the root. If we would do this on a very flat function like f (x) = x1 0 − 1, we have to count the steps, to avoid the programs hanging. The function is as flat that the number of small steps to get out of the trap will increase to a nearly infinite number. Figure 3.2 shows the iteration path for the equation f (x) = x2 − 1 with an starting value of 0 with red dots. Figure 3.3: Newton’s Iteration Path starting vom 0 E. Baeck 3.2. PROFILES, THIN WALLED APPROACH 3.2 Page 65 Profiles, Thin Walled Approach In this section we will implement a class hierarchy to model a profile’s data. We will implement logging into file and screen in a common base class and we will implement the thin walled approach to analyze and calculate the profile’s section properties. Applying the Thin Walled Approximation every part of a section will be approximated by a line with a given thickness. This approach is similar to the approach modeling a three dimensional shell structure. Hereby we use shell elements, i.e. faces, with a given thickness. The lines like the shell elements in FE will connect nodes or points. So if we want to build up a calculation model for our thin-walled section, we need something like lines and points. Lines are called Elments and Points are called Nodes like in the case of programs. In our case we introduce two classes. • The class Node describes the contour point of elements. In our case we have the end points of lines in two dimensions. • The class Element describes the connection between nodes. In our case we connect points with lines so that we have to specify the thickness of the line, ie. the connection. Further we introduce a class Base, which should content all common features. A general profile then will be described by the class Profile. This class can therefore be considered as a container for our points and lines as well as the class which should perform all calculations. To show the power of inheritance we introduce a derived profile class, which is called UProfile. This class will describe all U-profiles with their geometric parameters. Based on our class Profile this class will be able to calculate all section values based on the given parameter set. All details, i.e. points and lines are encapsulated in it’s base class. 3.2.1 A General Base Class If we want to use the code of an existing class within a new class, we can inherit this old class and use it as a base class. That means, that the new class contents also the attributes and the methods of the old one. A base class is used in the class statement as an argument. A class can inherit more than one class. In our case everything is derived from our general base class called Base. The class Base should content logging for all classes. This class should also be able to count all created instances of the developed application. The output method for all classes should be the method AppendLog, i.e. this method will be inherited by all classes of our project. The classes UML diagram is given in figure 3.4. The class Base counts the instances using the global class attribute __count. A common logging method is implemented. So the name of the log file is implemented as a class method (not an instance method) __log. The method appendLog is implemented as a general logging function. The actual time is called an copied as heading for our logging comment. Then the completed logging text is written into the log file and into the screen. 23.4.2015 Analysis of Structures - SS 15 Page 66 Base − log: global log file name − count: global instance counter The class Base contents common features which are used in every class. − self. init (..): constructor + self.appendLog(..): write to log Figure 3.4: The Base Class of all TWD Classes The implementation of the class Base is given in listing 3.2. Listing 3.2: Implementation of a Base-Class 1 2 3 4 5 6 7 8 ’’’ the Base class of all TWA-classes ’’’ # import some packages # |package # | | select the item to import # | | | alias name from datetime import datetime as time 9 10 11 12 class Base: __log = "profiles.log" __count = 0 # class attribut, type private # class attribut, instance counter 13 14 15 16 17 18 19 # construtor # |instance pointer # | |optional parameter def __init__(self,log = ""): Base.__count += 1 if len(log) > 0: Base.__log = log # increment the counter # set a new log-file name 20 21 22 # print the instance counter value self.appendLog(">> instance %d created!" % Base.__count) 23 24 25 26 27 28 29 30 # print a message into the log file and to the console # | instance pointer # | | text to print def appendLog(self,text): t = time.now() # get the time tstamp = "%2.2d.%2.2d.%2.2d |" % (t.hour,t.minute,t.second) textout = tstamp + text # text to print 31 32 33 34 # use the file object to print with the append mode "a" f = file(Base.__log,"a") f.write(textout+"\n") # write text 35 36 print textout 37 38 39 40 41 E. Baeck # destructor def __del__(self): self.appendLog("instance %d deleted" % Base.__count) Base.__count -= 1 # decrement the counter 3.2. PROFILES, THIN WALLED APPROACH 3.2.2 Page 67 A Node Class The class Node only has the attributes of the points number and it’s coordinates. The coordinates are stored within a list. If we do this, we simply can extend this object to a three dimensional application. Besides the constructor we will implement a method list, which should be able to print the instances data to a given output stream. This method will use our method appendLog of the base class. The classes UML diagram is given in figure 3.5. Base Will provide appendLog Node + no: node number + x[]: List for point coordinates The class Node contents the coordinates and the number of a profile’s node. − self. init (..): constructor + self.list(..): print node data Figure 3.5: UML-Diagram for a Node Class The implementation of the class Node is given in listing 3.3. Listing 3.3: Implementation of a Node-Class 1 __author__ = ’baeck’ 2 3 4 5 # forced reload import Base reload(Base) 6 7 8 # module item from Base import Base 9 10 11 # class to describe a profile node class Node(Base): 12 13 14 15 16 # constructor # self pointer # number and the coordinates def __init__(self,no,x,y): 17 18 19 # run the constructor of the Base class Base.__init__(self) 20 21 try: 22 self._no = int(no) self._x = [float(x),float(y)] except: raise "*** error: invalid input!" 23 24 25 26 27 # destructor 23.4.2015 Analysis of Structures - SS 15 Page 68 def __del__(self): pass 28 29 30 # list the node content def list(self): self.appendLog("node %d, x = %10.4f, y = %10.4f" % \ (self._no,self._x[0],self._x[1])) 31 32 33 34 3.2.3 Testing the Node Class In our testing environment we simple import the Node module. Than we create a Node instance and list it’s data. After that we delete the instance and we try to print it again. The implementation of the testing environment is given in listing 3.4. Listing 3.4: Implementation of a Node-Testing Environment 1 2 3 4 5 ’’’ checking the Node class ’’’ import Node # we have to be sure reload(Node) # that we check the current file 6 7 from Node import Node # import the Node class n = Node(3,1.,2.) n.listData() # we create a point # and list it’s value # delete the instance print n del n print n # let’s print the Nodes address # and than we delete the instance # try to print it again 8 9 10 11 12 13 14 15 The output of our test application is given in the following code. We see, that the Base class constructor shows, that a new instance is created. The next line shows the output of Node’s list method followed by the simple instance print, which shows the address of the instance. Than the instance is explicitly deleted, so that the destructor of the Base class shows that one instance is deleted. Because the address of the instance after deleting is invalid, the program will crash with a trace back message. The line (line 15) is given and the message shows us, that at this moment there is no valid variable called p. 1 2 3 4 5 6 7 8 9 10 11 12 15.49.12 |>> instance 1 created! 15.49.12 | point 3, y = 1.000, z = 2.000 <Node.Node instance at 0x031AE300> 15.49.12 |instance 1 deleted Traceback (most recent call last): File "C:\Python27\Lib\site-packages\Pythonwin\pywin\framework\scriptutils.py", line 326, in RunScript exec codeObject in __main__.__dict__ File "C:\UniDue\CM\Cm-AoS\AOS-BookOfExamples\Py\Code\Profiles\NodeApp.py", line 15, in <module> print p # try to print it again NameError: name ’p’ is not defined E. Baeck 3.2. PROFILES, THIN WALLED APPROACH 3.2.4 Page 69 An Element Class The class Element will describe the connection between points and will describe further the type of connection. In the case of a line we will have the line’s thickness. Because in our case the properties of the profile, i.e. it’s area and moments, are given by the sum over the element’s contribution (see section B), we put the code to calculate this properties into the element’s class. If we do so, the element must know everything about it’s points, i.e. the numbers of the points are not sufficient to have success. So we store the point’s instance pointers and not there number in our element. The classes UML diagram is given in figure 3.6. The element can be considered as a container for Node instance pointers too. This is shown within the diagram using the composition link to the class Node. The cardinality in our case is exactly two. If we would introduce faces too for the thick-walled case, the cardinality can also be greater then two. Base Element + no: element number + n[]: list of point instance pointers The class Element contents the element’s point data. + t: line’s thickness − self. init (..): constructor + self.getL(): get line’s length + self.getA(): get area + self.getS(): get static moments Node + no: node number + x[]: List for point coordinates + self.getI(): get moments of inertia + self.list(..): print element data − self. init (..): constructor + self.list(..): print node data 2..* Figure 3.6: UML-Diagram for a Element Class 23.4.2015 Analysis of Structures - SS 15 Page 70 The implementation of the class Element is given in listing 3.5. Listing 3.5: Implementation of an Element-Class 1 2 3 4 5 6 __author__ = ’baeck’ ’’’ implementation of an element class ’’’ import Base reload(Base) 7 8 9 # module item from Base import Base 10 11 12 # | inherit the Base class class Element(Base): 13 14 15 # constructor def __init__(self,no,node1,node2,t): 16 17 Base.__init__(self) 18 19 20 21 self._no = no self._n = [node1,node2] self._t = t 22 23 24 25 26 # calculate projected length x,y # i: dimension def delX(self,i): return self._n[1]._x[i] - self._n[0]._x[i] 27 28 29 30 # calculate the length of the element def getL(self): return (self.delX(0)**2 + self.delX(1)**2)**0.5 31 32 33 34 # calculate the element’s area def getA(self): return (self.getL()*self._t) 35 36 37 38 39 # calculate the center coordinates of an element # i: dimension (y,z) def getC(self,i): return ( (self._n[1]._x[i] + self._n[0]._x[i])/2. ) 40 41 42 43 44 # calculate the static moment of the element # i: dimension (y,z) def getS(self,i): return ( self.getC((i+1)%2)*self.getA()) 45 46 47 48 49 50 51 52 E. Baeck # calculate the moment of inertia # i: dimension (yy,zz,yz) def getI(self,i): if i < 2: j = (i+1)%2 return ( (self.delX(j)**2/12. + self.getC(j)**2)*self.getA() ) else: 3.2. PROFILES, THIN WALLED APPROACH Page 71 return ( (self.delX(0)*self.delX(1)/12. + self.getC(0)*self.getC(1)) *self.getA() ) 53 54 55 56 57 58 59 60 61 62 63 64 65 3.2.5 # list the elements data def list(self): self.appendLog("element %d: nodes: %d %d, t = %.2f, A = %.1f" % (self._no, self._n[0]._no, self._n[1]._no, self._t,self.getA() )) self.appendLog(" center..............: %10.3e %10.3e" % (self.getC(0),self.getC(1))) self.appendLog(" static moments......: %10.3e %10.3e" % (self.getS(0),self.getS(1))) self.appendLog(" moments.of inertia..: %10.3e %10.3e %10.3e" % (self.getI(0),self.getI(1),self.getI(2))) Testing the Element Class In our testing environment we simple import the Element as well as the Point module. Than we create two Node instances, which will describe the end points of our line. Then the Element instance is created, passing the two Node instance pointer and the lines thickness. After that we list the element’s data and delete it. The implementation of the testing environment is given in listing 3.6. Listing 3.6: Implementation of a Element-Testing Environment 1 2 3 4 5 ’’’ check the Element class ’’’ import Element reload(Element) # do this if you want to sure # that the module file is the currend one from Element import Element from Node import Node # of course we need the Element class # and the Node class too 6 7 8 9 10 11 h = 200. t = 5. # thickness 12 13 14 15 # create the points n1 = Node(3,0.,-h/2.) n2 = Node(2,0., h/2.) 16 17 18 # create the element e1 = Element(2,n1,n2,t) 19 20 21 # print the element’s data e1.list() 22 23 24 25 26 # delete instances del n1 del n2 del e1 The following output shows, that 3 instances (2 nodes and 1 element) are created. We see the element’s data, i.e. the node numbers (3,2) and the thickness (5.0). The length and die area are calculated by the 23.4.2015 Page 72 Analysis of Structures - SS 15 call of the according methods. 1 2 3 4 5 6 7 8 9 10 11 12 >>> 15.57.08 |>> instance 1 created! 15.57.08 |>> instance 2 created! 15.57.08 |>> instance 3 created! 15.57.08 | element 2, A= 3, B= 2, t= 5.000 L= 200.000 A= 15.57.08 | center............: 0.000 0.000 15.57.08 | static moments....: 0.000e+00 0.000e+00 15.57.08 | moments of inertia: 3.333e+06 0.000e+00 0.000e+00 15.57.08 | node 3, y = 0.000, z = -100.000 15.57.08 | node 2, y = 0.000, z = 100.000 15.57.08 |instance 3 deleted 15.57.08 |instance 2 deleted 15.57.08 |instance 1 deleted 1000.000 The element’s length obviously is 200 and the area 1000. If we apply the well known formula for the 3 3 moment of inertia of an rectangle I = h12·w = 20012 ·5 = 3.33 · 106 we get the value in the listing. So it seems to be ok. E. Baeck 3.2. PROFILES, THIN WALLED APPROACH 3.2.6 Page 73 A General Profile Class So if we want to develop a software, which is able to calculate the profile’s section properties like it’s area, moment of inertia and so on, we can consequently split up the problem in general and specific parts. So we start with the data and the functions of a general object. This data is collected in a class which we call Base. So if we want to describe a wide range of profile types, then we should think about the profile’s common features. Every profile has a name and therefore this name is a common feature. This common features are collected in a general profile class which is called Profile. If we want to handle a specific profile type given by a set of parameters like an U-profile ore a tube profile, we collect the specific type dependent features in the classes UProfile and TubeProfile, which are obviously the parameters describing the profile’s geometry. The class Profile now contents common profile features. One of them is the profile’s name. Note, if we inherit a class from a base class, we have to call the base class’s constructor method. Every attribute and every method of the base class is now available. A second feature of the Profile class is the implementation of the thin walled model. Therefore we introduce an Element container using the AList class. With a specific method we insert an Element instance into the container. To calculate the section values of the created thin walled model we introduce some methods, which calculate the global section values summing up all element values and transforming them into the desired coordinate system. At least we introduce a little viewing method, which should create a png picture file of the profile and should optionally start the pylab viewer tool. To be sure, that the environment supports the Tkinter package1 , which is used by the viewer, we import the package using a try: ... except: handler. At the end of the view method we should close the plot, to avoid a memory effect. Profile + self. name: The profile’s name + self. elem: Element container − self. init (..): constructor + self.list(..): List profiles values + self.addElement(..): insert a new Element The class Profile contents all common features of a profile especially the containers for the Points and Lines of shape modell + self.getResults(): calculate section values − self.getPValues(): transform the moment of inertia − self.pTrans(): principal axis transformation + self.view(..): views a profile plot Figure 3.7: The general Profile Class A U-profile is described within the frame of the TWA by it’s height and width as well as by the thicknesses of it’s web and flange. So we need 4 floats to do this. It’s UML diagram is given in figure 3.8. 1 We will see later trying to apply our package Profiles in the frame of the Abaqus scripting, that we will get problems, because the Abaqus environment is not developed with Tkinter. 23.4.2015 Analysis of Structures - SS 15 Page 74 UProfile + self. h: height + self. w: with + self. s: web thickness + self. t: flange thickness − self. init (..): constructor + self.list(..): list profiles values The class UProfile contents the special features of an U-Profile, i.e. the geometric parameters. The class will be inherit the Profile class and will have the feature to create this profile with an U geometry. + self.check(): check input values + self.create(..): create thin walled model Figure 3.8: The U-Profile Class A tube-profile is described within the frame of the TWA by it’s diameter and it’s thickness. So we need only 2 floats to do this. It’s UML diagram is given in figure 3.9. TubeProfile + self. d: diameter + self. t: thickness − self. init (..): constructor + self.list(..): list profiles values The class TubeProfile contents the special features of a TubeProfile, especially the geometric parameters + self.check(): check input values + self.create(..): create thin walled model Figure 3.9: The Tube-Profile Class To hold the desired Element instances we implement a slightly modified list class, which should work like an array with a well defined index access (see section 3.2.7). If we would use a standard list class, we would get problems if we want to insert an object at a position outside the list range. Then a standard list object simply appends this object as a new item at the tail of the list and we would loose the correct index position. So we implement a new index save Add method, which checks the list length and if necessary enlarges the list up to the desired index position. The implementation of the class Profile is given in listing 3.7. Listing 3.7: Implementation of an Profile-Class 1 # -*- coding: utf-8 -*- 2 3 4 5 6 7 __author__ = ’baeck’ ’’’ Profile class to calculate section values with the thinwalled approximation ’’’ 8 9 10 from Base import Base from AList import AList 11 E. Baeck 3.2. PROFILES, THIN WALLED APPROACH 12 Page 75 class Profile(Base): 13 14 15 # constructor def __init__(self,name): 16 17 Base.__init__(self) 18 19 20 self._name = name self._elem = AList() # profile name # element container # result data self._a = 0. self._s = [0.,0.] self._e = [0.,0.] self._iu = [0.,0.,0.] self._ic = [0.,0.,0.] self._ip = [0.,0.] self._alp = 0. # # # # # # 21 22 23 24 25 26 27 28 29 static moments center of mass coordinates moment of intertia in user coordinates moment of intertia in center of mass coordinates moment of intertia in principal coordinates rotation angle 30 31 32 33 # add an element into the profile container def addElement(self,e): self._elem.add(e._no,e) 34 35 36 # caculate the sections values of the profile def getResults(self): 37 38 elements = 0 # element counter 39 40 41 # sum up all element contributions for e in self._elem: 42 43 44 # only for available elements! if e is not None: 45 46 47 # area self._a += e.getA() 48 49 50 51 # static moment for i in range(2): # [0],[1] self._s[i] += e.getS(i) 52 53 54 55 # moment of inertia for i in range(3): self._iu[i] += e.getI(i) 56 57 elements += 1 # count the elements 58 59 if elements < 1: raise "error: no elements found!" 60 61 62 63 # calculate the center of mass coordinates self._e[0] = self._s[1]/self._a self._e[1] = self._s[0]/self._a 64 65 66 # calculate the princial values self.getPValues() 67 23.4.2015 Analysis of Structures - SS 15 Page 76 68 return (self._a,self._s,self._ip,self._alp) 69 70 71 72 73 # calculate the principal values # shift and rotate! def getPValues(self): import math 74 75 76 77 78 # shift the self._ic[0] self._ic[1] self._ic[2] moment of inertia into the center of mass cs = self._iu[0] - self._e[1]*self._e[1]*self._a = self._iu[1] - self._e[0]*self._e[0]*self._a = self._iu[2] - self._e[0]*self._e[1]*self._a 79 80 81 82 83 # introduce some helpers idel = self._ic[0] - self._ic[1] isum = self._ic[0] + self._ic[1] isqr = math.sqrt(idel*idel + 4.*self._ic[2]) 84 85 86 87 # calculate the principal values self._ip[0] = 0.5*(isum + isqr) self._ip[1] = 0.5*(isum - isqr) 88 89 90 # calculate the rotation angle self._alp = 0.5*math.atan2(-2.*self._ic[2],idel) 91 92 93 # get angle value in degrees self._alp *= 45./math.atan(1.) 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 # list the profile’s data def listData(self): self.appendLog("name..................: %s" % self._name) self.appendLog("area..................: %10.2f cmˆ2" % (self._a/1.e2,)) self.appendLog("center of mass .......: %10.2f %10.2f cm" % \ (self._e[0]/1.e1,self._e[1]/1.e1)) self.appendLog("static moments .......: %10.2f %10.2f cmˆ3" % \ (self._s[0]/1.e3,self._s[1]/1.e3)) self.appendLog("moment of inertia uc: %10.2f %10.2f %10.2f cmˆ4" % \ (self._iu[0]/1.e4,self._iu[1]/1.e4,self._iu[2]/1.e4)) self.appendLog("moment of inertia cc: %10.2f %10.2f %10.2f cmˆ4" % \ (self._ic[0]/1.e4,self._ic[1]/1.e4,self._ic[2]/1.e4)) self.appendLog("moment of inertia pc: %10.2f %10.2f cmˆ4" % \ (self._ip[0]/1.e4,self._ip[1]/1.e4)) self.appendLog("rotation angle : %10.2f " % self._alp) 110 111 112 # print a picture of the profile geometry def view(self,viewer = True): 113 114 try: 115 import pylab except: self.appendLog("error: pylab not intalled!") return 116 117 118 119 120 121 122 123 E. Baeck # list of lines. every element -> a line lines = [] 3.2. PROFILES, THIN WALLED APPROACH 124 125 126 127 128 129 130 Page 77 # over all elements for e in self._elem: if e is not None: x1 = e._n[0]._x[0] y1 = e._n[0]._x[1] x2 = e._n[1]._x[0] y2 = e._n[1]._x[1] 131 132 133 134 # add the line data into the list lines += [ [ [x1,x2], [y1,y2] ], ] # --- a line --------- 135 136 137 138 # plot the lines for line in lines: pylab.plot(line[0],line[1],’b’) # b: blue # plot the lines nodes for line in lines: pylab.plot(line[0],line[1],’rp’) # r: red p: point 139 140 141 142 143 144 145 # plot the title pylab.title(self._name) 146 147 148 # plot it into a png-file pylab.savefig(self._name + ".png") 149 150 151 # show viewer if viewer: pylab.show() 152 153 154 3.2.7 # close the pylab pylab.close() The AList Class In our implementation of the Profile class we want to work with lists giving an array like feeling, i.e. in every case an insertion with a given index has to be like an assignment to an array slot. If we now use Pythons buildin list class, we will see, that if the index is greater than the upper-bound, the item to be inserted will be appended. If so, we will loose the strict array behavior of our container. The solution is very simple. We take the build-in list class and derive our own list object called AList. The only thing we have to add to the build-in list will be an index save add method. On the other hand it would be very comfortable, if we implement a list method, which is able to print every thing inside the container in it’s desired format. The implementation of the last is very simple due to the polymorphism strategy of OOP, i.e. an objects list method can be called without implementing a case like filtering. This assignment is done automatically, because the general list method of an object is projected onto the specific of the object. So every object will have it’s own list method. In our case we have a Node and an Element object. Both of them, we have seen, have their own list method, which then automatically is called. The UML diagram of the AList class is given in figure 3.10. The implementation of the class AList is given in listing 3.8. 23.4.2015 Analysis of Structures - SS 15 Page 78 Base list Python’s build-in list container AList − self. init (..): constructor + self.add(..): Add by index The class AList is based on the python build-in list class. The Add method adds an object with a given index value. + self.list(..): print instance’s data Figure 3.10: UML-Diagram for a Array-List Class Listing 3.8: Implementation of an AList-Class 1 __author__ = ’baeck’ 2 3 4 5 6 ’’’ extends the python’s buildin class List to get a perfect array feeling ’’’ 7 8 from Base import Base 9 10 class AList(Base,list): 11 12 13 14 # constructor def __init__(self): Base.__init__(self) 15 16 17 18 19 20 21 # add an instance like an array would do # no : index # ojb: object def add(self,no,obj): # check the length ubound = len(self) -1 22 23 24 25 if no > ubound: for i in range(ubound+1,no+1): self += [None,] 26 27 28 # store it! self[no] = obj 29 30 31 32 # print the list’s content def list(self): lobj = 0 # counter 33 34 35 36 E. Baeck # iterate the container for obj in self: 3.2. PROFILES, THIN WALLED APPROACH Page 79 37 try: 38 obj.list() except: self.appendLog("object %d is unprintable" % lobj) # pass 39 40 41 42 43 lobj += 1 In line 6 we can see, that the AList has two base classes Base and list. The features of both classes are inherited by our new one. Base is used to print and list is used to store the instance pointers. In line 15 we see the new method add. If the index is less than the list’s length a general list add is done. If not, we first enlarge the list up to the needed index value and then call the standard function. If we do so, the list also can have wholes inside, i.e. None pointers. So we have to be careful with the array access. In line 27 we call the list method of every list index. To avoid crashing we simple make the list access a save access by putting it into a try-except environment. The Profile class can be considered as a container class for the Element instances. The cardinality is one and greater. One we would have for a flat steel, two for an L profile and three for a U profile. The UML diagram is given in figure 3.11. 1 Base Profile 1..* Element Figure 3.11: Profile’s Element Container 3.2.8 Testing the Profile Class To test the Profile class we implement a simple main program which creates the points of a TWA based approximation of the unsymmetric L-profile L 100x50x6. The plates of the L have the length 100 and 50 mm. The thickness of the L is 5 mm. The implementation of the testing environment is given in listing 3.9. Listing 3.9: Implementation of a Profile-Testing Environment 1 2 3 4 5 6 7 8 9 ’’’ check the Profile class 3 | :2 :1 | 1---2 ’’’ import Profile reload(Profile) 10 11 12 from Profile import Profile from Element import Element 23.4.2015 Analysis of Structures - SS 15 Page 80 13 from Node import Node 14 15 16 print ">> ProfileApp: check the Profile class" p = Profile("L 100x50x6") 17 18 19 20 h w t = 100. = 50. = 6. 21 22 23 24 25 # create Noints n1 = Node(1, -w,t/2.) n2 = Node(2,-t/2.,t/2.) n3 = Node(3,-t/2.,h) 26 27 28 29 # create elements e1 = Element(1,n1,n2,t) e2 = Element(2,n2,n3,t) 30 31 32 33 # add the elements p.addElement(e1) p.addElement(e2) 34 35 36 # calculate the section values p.getResults() 37 38 39 # print profile’s data p.list() 40 41 42 43 # view the profile and delete it p.view() del p The application will give us the following output. We see, that the Profile’s list will print the name and the section values of the total profile. Then we see that the list method is calling the list method of the points and elements too. If an index instance is not available, the list method of the AList will give a hint. In this case there is no element with number 0, therefore we get the message, that in this slot there is no object found. The profile’s area is given in the standard profile table with 8.73 cm2 . The deviation comes from the neglected filets. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >> ProfileApp: check the profile class 19.45.04 |Name.................: L 100x50x6 19.45.04 | area...............: 8.6400 cmˆ2 19.45.04 | center of mass.....: -10.67 35.67 mm 19.45.04 | 1st moments.....: 30.82 -9.22 cmˆ3 19.45.04 | 2nd moments ucs.: 200.25 25.52 -11.23 cmˆ4 19.45.04 | 2nd moments scs.: 90.32 15.68 21.65 cmˆ4 19.45.04 | 2nd moments mcs.: 96.14 9.86 cmˆ4 19.45.04 | rotation angle..: 15.06 deg, tan(a) = 0.269 19.45.04 | element 1, A= 1, B= 2, t= 6.000 L= 47.000 A= 282.000 19.45.04 | center............: -26.500 3.000 19.45.04 | static moments....: 8.460e+02 -7.473e+03 19.45.04 | moments of inertia: 2.538e+03 2.499e+05 -2.242e+04 19.45.04 | node 1, y = -50.000, z = 3.000 19.45.04 | node 2, y = -3.000, z = 3.000 19.45.04 | element 2, A= 2, B= 3, t= 6.000 L= 97.000 A= 582.000 E. Baeck 3.2. PROFILES, THIN WALLED APPROACH 17 18 19 20 21 19.45.04 19.45.04 19.45.04 19.45.04 19.45.04 | | | | | Page 81 center............: -3.000 51.500 static moments....: 2.997e+04 -1.746e+03 moments of inertia: 2.000e+06 5.238e+03 -8.992e+04 node 2, y = -3.000, z = 3.000 node 3, y = -3.000, z = 100.000 Figure 3.12 shows the output of the profile’s view method. First the picture is created, then a png file is written from the picture and at the end the viewer is started.2 Figure 3.12: pylab - View of the Profile 2 Note, that the viewer of the used version is very sensible for multiple calls. So if you want to restart the program, you should first close an open viewer instance. If not, the program will hang. 23.4.2015 Analysis of Structures - SS 15 Page 82 3.2.9 The U-Profile Class Figure 3.13 shows the UML diagram of the UProfile class. The parameters we pass to the classes constructor are it’s name, the height, the width and the thicknesses of web and flange. UProfile + self. h: height The class UProfile contents the special features of an U-Profile, i.e. the geometric parameters. The class will be inherit the Profile class and will have the feature to create this profile with an U geometry. + self. w: with + self. s: web thickness + self. t: flange thickness − self. init (..): constructor + self.list(..): list profiles values + self.check(): check input values + self.create(..): create thin walled model Figure 3.13: The U-Profile Class A class hierarchy UML diagram is given in figure 3.14. Base Profile UProfile Figure 3.14: The U-Profile Class Hierarchy The implementation of the class UProfile is given in listing 3.10. The method create creates the profile’s Node and Element instances. Before the geometry data is created the input parameters of the U geometry should be checked. To cancel further activities, the simplest solution is to raise an exception to break the execution. Listing 3.10: Implementation of an UProfile-Class 1 2 3 4 5 6 ’’’ Implementaion of a UProfile class ’’’ from Profile import Profile from Node import Node from Element import Element 7 8 class UProfile(Profile): 9 10 def __init__(self,name,h,w,s,t): 11 12 Profile.__init__(self,name) 13 14 15 16 17 18 E. Baeck self._h self._w self._s self._t = = = = h w s t # # # # height width web thickness flansch thickness 3.2. PROFILES, THIN WALLED APPROACH 19 20 Page 83 # check input parameters self.check() 21 22 23 # create the geomtry data self.create() 24 25 26 27 # check the input parameters def check(self): dMin = 1. 28 29 30 31 32 33 34 35 36 37 # checking the parameters if self._h < dMin: raise Exception("invalid if self._w < dMin: raise Exception("invalid if self._s < dMin: raise Exception("invalid if self._t < dMin: raise Exception("invalid height h: %e" % self._h) width w: %e" % self._w) thickness s: %e" % self._s) thickness t: %e" % self._t) 38 39 40 # create the datastructure of an u-profile def create(self): 41 42 43 hs = (self._h - self._t)/2. ws = self._w - self._s/2. 44 45 46 47 48 49 # create the nodes n1 = Node(1,0., hs) n2 = Node(2,0.,-hs) n3 = Node(3,ws, hs) n4 = Node(4,ws,-hs) 50 51 52 53 54 # create the elements e1 = Element(1,n1,n2,self._s) e2 = Element(2,n1,n3,self._t) e3 = Element(3,n2,n4,self._t) 55 56 57 58 59 3.2.10 # assign the elements self.addElement(e1) self.addElement(e2) self.addElement(e3) Testing the UProfile Class To test the UProfile class we implement a simple main program, which passes the name and the parameters of a U-geometry to the UProfile constructor. Then the data of the instance is printed. In our example we calculate the values of an U80. 23.4.2015 Analysis of Structures - SS 15 Page 84 The implementation of the testing environment is given in listing 3.11. Listing 3.11: Implementation of a UProfile-Testing Environment 1 2 3 4 5 ’’’ check the UProfile class ’’’ import UProfile reload(UProfile) 6 7 from UProfile import UProfile 8 9 10 11 12 print ">> UProfileApp: check the profile class" try: # name h w s t p = UProfile("U80",80,45, 6, 8) 13 # calculate the section values p.getResults() 14 15 16 # print profile’s data p.list() 17 18 19 # view the profile’s system p.view() 20 21 22 23 24 25 26 # delete the instance del p except Exception,e: print "*** error:",e 27 28 print ">> UProfileApp: stop" The following lines show the output of the test application. The exact value of the U profile’s area is 11 cm2 . Note that we have to put the constructor call into a try-except frame to catch the exception. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >> UProfileApp: check the profile class 19.57.13 |Name.................: U80 19.57.13 | area...............: 11.0400 cmˆ2 19.57.13 | center of mass.....: 12.78 0.00 mm 19.57.13 | 1st moments.....: 0.00 14.11 cmˆ3 19.57.13 | 2nd moments ucs.: 105.75 39.51 0.00 cmˆ4 19.57.13 | 2nd moments scs.: 105.75 21.47 0.00 cmˆ4 19.57.13 | 2nd moments mcs.: 105.75 21.47 cmˆ4 19.57.13 | rotation angle..: 0.00 deg, tan(a) = 0.000 19.57.13 | element 1, A= 1, B= 2, t= 6.000 L= 72.000 A= 432.000 19.57.13 | center............: 0.000 0.000 19.57.13 | static moments....: 0.000e+00 0.000e+00 19.57.13 | moments of inertia: 1.866e+05 0.000e+00 0.000e+00 19.57.13 | node 1, y = 0.000, z = 36.000 19.57.13 | node 2, y = 0.000, z = -36.000 19.57.13 | element 2, A= 1, B= 3, t= 8.000 L= 42.000 A= 336.000 19.57.13 | center............: 21.000 36.000 19.57.13 | static moments....: 1.210e+04 7.056e+03 19.57.13 | moments of inertia: 4.355e+05 1.976e+05 2.540e+05 19.57.13 | node 1, y = 0.000, z = 36.000 19.57.13 | node 3, y = 42.000, z = 36.000 E. Baeck 3.2. PROFILES, THIN WALLED APPROACH 22 23 24 25 26 27 28 Page 85 19.57.13 | element 3, A= 2, B= 4, t= 8.000 L= 42.000 A= 19.57.13 | center............: 21.000 -36.000 19.57.13 | static moments....: -1.210e+04 7.056e+03 19.57.13 | moments of inertia: 4.355e+05 1.976e+05 -2.540e+05 19.57.13 | node 2, y = 0.000, z = -36.000 19.57.13 | node 4, y = 42.000, z = -36.000 >> UProfileApp: stop 336.000 Figure 5.2 shows the output of the profile’s view method. First the picture is created, then a png file is written from the picture and at the end the viewer is started. Figure 3.15: pylab - View of the Profile 3.2.11 The Profile Package If we want to develop reusable code, it’s recommended to create a separate file for every class and put them into a package. A package is a folder with the package’s name. So we introduce a folder called Profile. To initialize a package we have to create a file named __init__.py in this folder. If the package is loaded, the __init__.py is executed. All our class files we put into this package folder. Listing 3.12: Initializing the Package 1 2 3 # This script is executed only once, if the package is loaded. # So we print a little to show what’s going on print ">> Package ’Profiles’ is loaded" If we implement a new class within the Profile package, we only have to import the module base, i.e. the file Base.py. From this module we import the class Base. So we can write the import statement as follows.3 1 from Base import Base # inherit from the Base If we want to use a class from a package, we have to import it with the following statement. 1 from <package>.<module> import <class> as <locale name / alias name> As an example we use the application for testing a U-profile (see listing 3.11). We move the file on folder up, so that all our used class files are living in the subfolder Profile.4 3 4 Note, that the file names are case sensitive although you may work on Windows plattform. Note, if we do so, we also can move the total folder Profile into Pythons folder Lib/site-packages. 23.4.2015 Analysis of Structures - SS 15 Page 86 The implementation of the testing environment using the package Profile is given in listing 3.13. Listing 3.13: Implementation of a UProfile-Testing Environment using Package Profile 1 2 3 4 5 6 7 8 9 10 11 12 ’’’ check the UProfile class ’’’ import Profile.UProfile reload(Profile.UProfile) # import total module # reload total module # make sure that’s the current from Profile.UProfile import UProfile # import from a package # from here on every thing is the same... print ">> UProfileApp: check the profile class" try: # name h w s t p = UProfile("U80",80,45, 6, 8) 13 # print profile’s data p.listData() 14 15 16 17 18 19 20 # delete the instance del p except Exception,e: print "*** error:",e 21 22 print ">> UProfileApp: stop" 3.2.12 A Little Profile Database To implement a simple profile data base for arbitrary profile instances, we can simply use the Python buildin object dict 5 and extend it by inheritance with a specific list method (see figure 3.16). The list iterates the dictionary getting the keys of the stored instances. With the given key we get the instance pointer from the dictionary. Then we can use the mechanism of the polymorphism and call the instances specific list method. If there is an error occurring, the try/except error handler will do his job and executes the except branch code. So we can avoid program crashing due to invalid ore missing pointers. dict Python’s build-in dictionary class ProfileDB − self. init (..): constructor The class ProfileDB is based on a a dictionary. Profile instances are are stored like in a database. + self.list(..): print instance’s data Figure 3.16: UML-Diagram of the Profile Database The code of the ProfileDB class is given below. 5 A dictionary is used to store instance pointers with an arbitrary access key. E. Baeck 3.2. PROFILES, THIN WALLED APPROACH Page 87 Listing 3.14: Implementation of a Profile-Database, the ProfDB-Class 1 from Base import Base # the ProfDB class should inherit the Base class 2 3 4 5 # The ProfileDB class should store arbitray profile objects in # a dictionary. Therefore we inherit from the buildin dict object class ProfileDB(Base,dict): 6 7 8 def __init__(self): Base.__init__(self) # call the Base constructor 9 10 11 12 13 14 15 16 17 18 19 20 # the List method calls the List method of it’s items # if there is no item, the case is handled with an exception block def list(self): iobj = 0 # initialize the object counter for name in self: # iterate the keys of the dictionary try: # open the try block self[name].list() # try to list an object except: # if not possible log it to the log file self.appendLog(" Error: No profile key %s found, slot %d" % \ (name,iobj)) iobj += 1 # increment the counter 21 22 23 24 25 26 27 28 29 30 31 32 # the View method calls the View method of it’s items with a False Flag. # if there is no item, the case is handled with an exception block def view(self): iobj = 0 # initialize the object counter for name in self: # iterate the keys of the dictionary try: # open the try block self[name].View(False) # try to write the png file except: # if not possible log it to the log file self.appendLog(" Error: No profile key %s found, slot %d" % \ (name,iobj)) iobj += 1 # increment the counter To check the database class ProfileDB we write a little script, which first creates a ProfileDB instance. Then we create a UProfile instance with the values of the U100. This instance is stored in the dictionary using the key of it’s name U100. Then we create a second UProfile instance with the values of the U140 and store it as well in the dictionary using it’s name U140 as it’s key. After that we simple call the ProfileDB instance method list. The data of the two profiles are listed on the screen and into the log file. After the job is done, we print the png files of our profiles. The code of the testing environment is given below, the resulting png files are shown in figure . 23.4.2015 Analysis of Structures - SS 15 Page 88 Listing 3.15: Implementation of a Testing-Environment for the ProfileDB-Class 1 2 3 4 5 6 7 8 9 ’’’ This testing example implements a little profile database. Two U-profiles are created and stored in the database. Then the whole content of the database is printed using the AppendLog method of the base class ’’’ # package module class from Profile.UProfile import UProfile from Profile.ProfileDB import ProfileDB 10 11 12 13 # create Profile database db = ProfileDB() 14 15 16 # create the instance of an U-Profile prof = UProfile("U100",100.,50.,6.0,8.5) 17 18 19 # and save it into the db db.setDefault("U100",prof) 20 21 22 # create the instance of an U-Profile prof = UProfile("U140",140.,60.,7.0,10.) 23 24 25 # and save it into the db db.setDefault("U140",prof) 26 27 28 # print the content of the db db.list() 29 30 31 # print png files of the content of the db db.view() Figure 3.17: Pictures of the Profiles stored in the Database E. Baeck Part II Scripting with Abaqus 89 4 Some Aspects and Introduction In this chapter we talk about the Abaqus Student Edition Licence 6.10.. A main aspect will be the development of Python programs, which should automate the creation of FE models and the subsequent calculation and preprocessing. 4.1 Aspects of the Abaqus GUI In this chapter we talk about the Abaqus Student Edition Licence 6.10. GUI1 . Figure 4.1 shows the Abaqus GUI. A very important item is the combo box to select the module. The selected module loads the specific menu items. The left window shows the Abaqus object tree. The bottom window contents an output window for the output messages and the command window for the Python commands. Figure 4.1: Abaqus-GUI 1 Graphical User Interface is a window driven program which contents all commands in terms of menu items, buttons, boxes and so on which are commonly called widgets. 91 Page 92 4.2 Analysis of Structures - SS 15 The Abaqus CAE Module The Abaqus/CAE kernel offers several possibilities to build up an FE model. • The GUI offers interactive functions to create the FE model. • The Python command window offers the possibility to execute single Python commands. • The Scripting interface offers the possibility to run Python scripts. • The GUI offers the possibility to record interactive actions in a Python script format. So we can record and replay everything we do within the GUI. The Python interpreter creates the input for the CAE kernel. The CAE kernel creates the input stream for the Abaqus FE-Module.2 2 The input file for the Abaqus FE-Module is a simple Text file, the classical input file, which can also be written ’by hand’. E. Baeck 4.3. A MODELING CHAIN 4.3 Page 93 A Modeling Chain Figure 4.2 shows how to create a Finite Element model in Abaqus/CAE. It is not possible to create elements and nodes directly. Element and nodes are only created by the mesh generator, which works an the geometric objects, which are created drawing a sketch. Create Database The only FE model parameter, which are created directly, are the material properties and the section values. This properties are created within the module Property. The properties are then assigned to the geometric objects (lines, areas and volumes). Module Part Create a part and assign the sketch After having created a sketch the sketch has to be assigned to a part. If no part exists, a part hast to be created. The properties (materials and section data) are assigned to the sketches’ geometric objects. Module Property Create a properties, materials and sections, assign them to sketch lines To create a mesh, an instance is needed, so an instance has to be created. The part with the sketch are assigned to the instance for later meshing. Loadcases are modeled in terms of load steps. So a new step has to be created as a container for loads and boundary conditions. Loads and boundaries are assigned to the geometric objects of the sketch which were assigned to a part before. To create the mesh, the mesh’s control parameters should be configured and the element types are to be assigned. Then the instance can be meshed. After the mesh is created, the complete model can be assigned to a job. To calculate the results the job has to be submitted. Module Sketch Draw a sketch Module Assembly Create an instance and assign the part to it Module Step Create a step as container for a load case Module Load Create loads and boundary condition within the step Module Mesh Set Seeds, elements per length select and assign element type mesh the instance Module Job Create a Job and submit Module Postprocessing Visualization and Evaluation Figure 4.2: Modeling Chain Diagram 23.4.2015 Analysis of Structures - SS 15 Page 94 4.4 A little interactive Warm Up Example In section 4.3 we have discussed the Abaqus modeling chain. Following this outlines the subsequent example based on [5] shows how to create and calculate a simple 3 truss system with a concentrated force. We use the following parameters. • Lx horizontal length 1000 mm Ly • Ly vertical length 1500 mm • Fx horizontal force -100 kN • Fy vertical force -100 kN Fx y x Fy Lx 4.4.1 Create a Database Figure 4.3: 3 Trusses Create Model Database File/Set Working Directory (setup the desired work folder if necessary) 4.4.2 Create a Sketch Module: Sketch Sketch => Create => Name: ’Truss-Example’ => Continue Add=> Point => enter coordinates (0,0), (1000,0), (1000,1500), (0,1500) Add => Line => Connected Line => select (0,0) node with mouse, then (1000,0) node, right click => Cancel Add => Line => Connected Line => select (0,0) node with mouse, then (1000,1500) node, right click => Cancel Add => Line => Connected Line => select (0,0) node with mouse, then (0,1500) node, right click => Cancel => Done 4.4.3 Create a Part Module: Part Part => Create => Name: ’Part-1’,=> select 2D Planar, Deformable, Wire => Continue Add => Sketch => select ’Truss-Example’ => Done => Done E. Baeck 4.4. A LITTLE INTERACTIVE WARM UP EXAMPLE 4.4.4 Page 95 Create and Assign Properties Module: Property Material => Create => Name: ’Steel’, Mechanical, Elasticity, Elastic => set Young’s modulus = 210000, Poisson’s ratio = 0.3 => OK Section => Create => Name: ’Truss-Section’, Beam, Truss => Continue => set Material: ’Material-1’, Cross-sectional area: 2 Assign Section => select all elements by dragging mouse => Done => ’Truss-Section’ => OK => Done 4.4.5 Create the Instance, Assign the Part Module: Assembly Instance => Create => ’Part-1’ => Independent (mesh on instance) => OK 4.4.6 Create a Load Step Module: Step Step => Create => Name: ’Step-1’, Initial, Static, General => Continue => accept default settings => OK 4.4.7 Create Loads and Boundary Conditions Module: Load Load => Create => Name: ’Step-1’, Step: ’Step 1’, Mechanical, Concentrated Force => Continue => select node at (0,0) => Done => set CF1: -10000 set CF2: -10000 => OK BC => Create => Name: ’BC-1’, Step: ’Step-1’, Mechanical, Displacement/Rotation => Continue => select nodes at (1000,0), (1000,1500) and (0,1500) using SHIFT key to select multiple nodes => Done => set U1: 0 and U2: 0 4.4.8 Create the Mesh Module: Mesh Seed => Edge by Number => select entire truss by dragging mouse => Done => Number of elements along edges: 1 => press Enter => Done Mesh => Element Type => select entire truss by dragging mouse => Done => Element Library: Standard, Geometric Order: Linear: Family: Truss => OK => Done Mesh => Instance => OK to mesh the part Instance: Yes 23.4.2015 Analysis of Structures - SS 15 Page 96 4.4.9 Create a Job and Submit Module: Job Job => Create => Name: Job-1, Model: Model-1 => Continue => Job Type: Full analysis, Run Mode: Background, Submit Time: Immediately => OK Job => Submit => ’Job-1’ Job => Manager => Results (enters Module: Visualization) E. Baeck 5 Scripts and Examples In this chapter we implement to example scripts, to discuss the automation in Abaqus using the Python script language. 1. 3 Trusses The first script gives the implementation of the interactive example, a little 3 trusses system with one concentrated load. The calculation is performed automatically for a linear analysis. 2. U Profile The second script gives the implementation of the automated model creation of U Profile using the thin-walled approach (see also section 3.2 and B.1). The calculation is performed automatically for a linear analysis and a buckling analysis. 5.1 3 Trusses Script Within this section the implementation of a script is shown, which automates the example of section 4.4. With the change of the parameter’s values, every system can be calculated with the execution of the script. Creating the calculation model we follow our outlines discussed in section 4.3. To avoid problems with existing project files, we first delete an old database, if exist. Then we set the work directory by the use of Python’s standard chdir function.1 To get the Python code for the necessary steps, we can simple run the macro recorder and record the interactive actions. If you do this, then it’s recommended to save the macro file into the work directory and not into the home directory. The recorded macros are saved into the file abaqusMacros.py. Note, that the work directory will be set, if the script is executed. Within the script code the following steps are numbered in the same way. (1) Create a model The model is member of the global mdb class. We have to specify the name and the type of the model. The reference to the created model is saved into the variable myModel for further usage. 1 Note, that on Windows-Systems a path contents backslashes, which in Python are used as escape characters. Therefore if a backslash is used, we have to duplicate it. 97 Analysis of Structures - SS 15 Page 98 (2) Create a sketch The ConstrainedSketch is member of the model class, so we use the before saved model reference myModel to create the sketch. Initializing a sketch we have to specify the sketch’s name and the sketch page’s size. The return reference is saved into the variable mySketch. Within our sketch we draw the lines with the method Line. This is done within a for loop iterating the tuple containing the endpoint coordinates of the lines. (3) Create a part The part class is a member of the model class. We create the part instance specifying it’s name and it’s type. In this we select the TWO_D_PLANAR type. The return of the constructor is saved into the variable myPart. The method BaseWire of myPart is used to assign the sketch. Now we have the geometry to start with the creation of nodes and elements using the Abaqus mesh generator.2 (4) Create the elements properties The material class is a member of the model class. A material instance is created specifying the materials name. Within a second step we assign material properties using the Elastic method of the material class. A tuple containing the Young’s module and the Poisson ratio is assigned to the parameter table. In a second step we create the truss section instance. The TrussSection class is a member of the model class. The return is saved into the variable mySection. Within the third step the section, which is also linked to the material, will be assigned to the line objects of the part. So we have to create a region containing the line objects - in this case called edges - to perform the assignment. By default the selection is recorded with the parts member edges method getSequenceFromMask(mask=(’[#7]’, ), ). This selection method is fastest version of selection, but it is also the most cryptic one. If you use this mask, you have to be sure, that the index of the selected elements does not change and you have to know the selected objects’ index value. The mask value is created assigning the index values as binary digits. So in general the mask’s value can be calculated with the following equation. mask = X 2i , with i = Objectlabel − 1 for all selected objects (5.1) i∈Selection In our case the lines 1, 2 and 3 are selected. So we get #7 = 716 = 7 = 21−1 + 22−1 + 23−1 = 1 + 2 + 4 (5.2) After having selected the lines the truss section will be assigned by the part’s method SectionAssignment. We have to specify the region and the name of the section. (5) Create an instance To create the instance, which is needed for meshing, we have to select the rootAssembly object, which is member of the model class. Because the rootAssembly also is needed later, we assign it’s reference to the variable root. Within the rootAssembly we create the instance object, specifying it’s name and the part to assign. 2 Note, that within the GUI nodes and elements can only be created by the mesh generator, i.e. not directly. If the mesh is created, the node’s and element’s properties can be changed by special system edit methods. E. Baeck 5.1. 3 TRUSSES SCRIPT Page 99 (6) Create loads and boundary conditions First we should create a StaticStep class, which is a member of the modul class. The StaticStep class is a container for loads and boundary conditions. To create a concentrated load, which is a points load, we have to select first some points, which are called vertices in Abaqus. The vertices container is a member of the instance class. Vertices also can be selected with the getSequenceFromMask method, if the label number of the points are known. The class methode regionToolset.region converts a set of vertices into a region. The class ConcentratedForce is a member of the model class. We have to specify the loads name. We have to assign the load to a step and a region and have to set up the load values. To create boundary conditions we select vertices as well and convert them into a region. Then the DisplacementBC object, a member of the instance class has to be created like the concentrated forces specifying it’s name and assigning it to a load step. (7) Create the mesh The mesh will be created on the geometric data, in this case the lines of the sketch. So we have to set up the seeds of the lines, the number of elements, which should be created by the mesh generator. Like in the case of the section assignment we select all lines using the edges container of the part object. With #7 we set the mask for our 3 lines. To set the numbers of elements, we apply the method seedEdgeByNumber of the part class passing the selected edges and the number of elements to create, in this case one. Within a second step we select the element type to use in our model with the method elemType of the class mesh. We select from the standard element library the truss element with the code T2D2. Then the selected element type is assigned to all elements using the method setElementType of the part class. As parameters we pass a region, which is created with the mask #7 for all edges and with an tuple of selected element types. Within an ultimate step we simple call the method generateMesh of the part class. Now the mesh is created with all it’s properties and with the method setValues of the standard viewport instance, which is a member of the session class, we can view the mesh passing the part instance as displayedObject parameter. If no viewport is explicitly created, the standard viewport is called Viewport: 1. (8) Create the job and submit We set up the jobname and create the job using the standard parameter values. In this case the job is executed immediately after submission. The return value of the creation step is saved into a variable for later usage. After the creation of the job, the job is submitted with the job classes’ the method submit. To automate the analysis of the results two features are available. – synchronous analysis In this case the script halts for completion of the calculation. This can be obtained by the usage of the job classes’ method waitForCompletion. The next commands of the script are executed after the calculation is completed. – asynchronous analysis In this case the script does not halt for completion and the next instructions are executed immediately. To automate the analysis of the results we have to set up a call back function. This function is executed automatically after the calculation is done. 23.4.2015 Analysis of Structures - SS 15 Page 100 In our script we implement a synchronous analysis. (9) Analysis of the result values To start with the analysis we have to open the result database first. This can be done by the openOdb method of the class visualization passing the name of the result database. The return value is a reference to the data base instance. To access the result values first we have to select the desired step. The step instance contains a frame container. The result values are to obtain from the member class fieldOutputs. In our case we select the displacements with the output variable name U. The stresses can be selected with the output variable S. The values container of the fieldOutput instance contains a member elementLabel, which is set, if an element result data set is available. If a node result data set is available the nodeLabel member is set. The result values are items of the data array. After the analysis is done, you should not forget to close the database with the close member function. Listing 5.1: Implementation of a the 3-Trusses Example 1 2 # copy the next line into the abaqus command window and execute # execfile("c:\\CM\\Cm-AoS\\WS1011\Abaqus\\3Trusses.py") 3 4 5 6 from abaqus import * from abaqusConstants import * from caeModules import * # this we need for regionToolset 7 8 modelname = ’3Trusses’ 9 10 11 12 13 14 15 # delete old database if exists try: del mdb.Models[modelname] print "model ’%s’ deleted." % modelname except: print "No model ’%s’ found!" % modelname 16 17 18 19 import os # os tools os.chdir(r’c:\\cm\\Cm-AoS\\WS1011\\Abaqus’) 20 21 22 23 24 25 26 27 28 29 # set up parameters dx = 1000. dy = 1500. A = 13.5*100. Fx = -100000. Fy = -100000. EMod = 210000. nue = 0.3 # # # # # # # length in x length in z U100 area horzontal load vertikal load Young’s module Poisson’s ratio 30 31 32 # (1) create a model myModel = mdb.Model(name=modelname, modelType=STANDARD_EXPLICIT) 33 34 35 36 # (2) create a sketch mySketch = myModel.ConstrainedSketch(name=’3Truss-Sketch’, sheetSize=1.1*2*dy) E. Baeck 5.1. 3 TRUSSES SCRIPT Page 101 37 38 39 40 41 42 # - create tuple with coordinates xyPoint = ( (0,0), (dx,0), (dx,dy), (0,dy) ) # - iterate the tuple and draw lines for i in range(1,len(xyPoint)): mySketch.Line(point1=xyPoint[0], point2=xyPoint[i]) 43 44 45 46 # (3) create part myPart = myModel.Part(name=’Trusse-Part-1’, dimensionality=TWO_D_PLANAR, type=DEFORMABLE_BODY) 47 48 49 # - assign the sketch myPart.BaseWire(sketch=mySketch) 50 51 52 53 54 # (4) set up properties # - create material for steel mySteel = myModel.Material(name = ’Steel’) mySteel.Elastic(table=( (EMod,nue), )) 55 56 57 58 # - create truss section mySection = myModel.TrussSection(name=’Truss-Section’, area=A, material=’Steel’) 59 60 61 62 63 64 65 66 # - assign the section to all lines alledges = myPart.edges edges = alledges.getSequenceFromMask(mask=(’[#7 ]’, ), ) region = regionToolset.Region(edges=edges) myPart.SectionAssignment(region=region, sectionName=’Truss-Section’, offset=0.0, offsetType=MIDDLE_SURFACE, offsetField=’’, thicknessAssignment=FROM_SECTION) 67 68 69 70 # (5) create an instance root = myModel.rootAssembly myInst = root.Instance(name=’Trusses-Instance’, part=myPart, dependent=ON) 71 72 73 74 # (6) create a load step myModel.StaticStep(name=’First Step’, previous=’Initial’, description=’Static Load Step’) 75 76 77 78 79 80 81 82 # - create loads for the loadstep v1 = myInst.vertices verts1 = v1.getSequenceFromMask(mask=(’[#2 ]’, ), ) region = regionToolset.Region(vertices=verts1) myModel.ConcentratedForce(name=’Load-1’, createStepName=’First Step’, region=region, cf1=Fx, cf2=Fy, distributionType=UNIFORM, field=’’, localCsys=None) 83 84 85 86 87 88 89 90 # - create boundary conditions for the loadstep verts1 = v1.getSequenceFromMask(mask=(’[#d ]’, ), ) region = regionToolset.Region(vertices=verts1) myModel.DisplacementBC(name=’BC-1’, createStepName=’First Step’, region=region, u1=0.0, u2=0.0, ur3=UNSET, amplitude=UNSET, fixed=OFF, distributionType=UNIFORM, fieldName=’’, localCsys=None) 91 92 # (7) create mesh 23.4.2015 Page 102 93 94 95 96 Analysis of Structures - SS 15 # - number of elments / line e = myPart.edges pickedEdges = e.getSequenceFromMask(mask=(’[#7 ]’, ), ) myPart.seedEdgeByNumber(edges=pickedEdges, number=1, constraint=FINER) 97 98 99 100 101 102 103 # - set up the element type elemType1 = mesh.ElemType(elemCode=T2D2, elemLibrary=STANDARD) e = myPart.edges edges = e.getSequenceFromMask(mask=(’[#7 ]’, ), ) Region=(edges, ) myPart.setElementType(regions=Region, elemTypes=(elemType1, )) 104 105 106 # - create the mesh myPart.generateMesh() 107 108 109 # - show the mesh session.viewports[’Viewport: 1’].setValues(displayedObject=myPart) 110 111 112 113 114 115 116 117 118 119 120 # (8) create a job and submit it jobname = modelname+’-Job-1’ myJob = mdb.Job(name=jobname, model=modelname, description=’Calculation of the 3 Trusses System’, type=ANALYSIS, atTime=None, waitMinutes=0, waitHours=0, queue=None, memory=50, memoryUnits=PERCENTAGE, getMemoryFromAnalysis=True, explicitPrecision=SINGLE, nodalOutputPrecision=SINGLE, echoPrint=OFF, modelPrint=OFF, contactPrint=OFF, historyPrint=OFF, userSubroutine=’’, scratch=’’) myJob.submit(consistencyChecking=OFF) 121 122 123 # - note: wait for completion, if the result values should be read form DB myJob.waitForCompletion() 124 125 126 127 128 # (9) analysis of results # - open the database: odb file myOdb = visualization.openOdb(jobname+’.odb’) 129 130 131 # - read data from the last frame frame = myOdb.steps[’First Step’].frames[-1] 132 133 134 135 136 137 138 139 # - printing the displacements data = frame.fieldOutputs[’U’] n = len(data.values) print "%d Result records" % n print ’--no ---ux----- ---uy----’ for value in data.values: print "%4d %10.5f %10.5f" % (value.nodeLabel,value.data[0],value.data[1]) 140 141 142 143 144 145 146 147 # - printing the stresses data = frame.fieldOutputs[’S’] n = len(data.values) print "%d Result records" % n print ’--no --S11-----’ for value in data.values: print "%4d %10.5f" % (value.elementLabel,value.data[0]) 148 E. Baeck 5.1. 3 TRUSSES SCRIPT 149 150 Page 103 # close the database myOdb.close() Figure 5.2 shows the system with it’s loads and boundary condition (see figure 4.3 too). All points are fixed but not lower left. Here we see the applied point loads in down and left direction. Figure 5.1: Loads and Boundary Conditions Figure 5.2 shows the magnitude of displacement on the deformed truss structure. Figure 5.2: Magnitude of Displacement 23.4.2015 Page 104 5.2 Analysis of Structures - SS 15 U-Girder Script The goal of our next example is, to create a mesh from a 2 dimensional sketch of an U geometry by extrusion. Figure 5.2 shows an U profile with it’s parameters [6]. The U profile geometry is used to create a FE model of a single span girder, shown in figure 5.4. The girder has a length of L and will be loaded with a line load q. The boundary conditions should be set according to the simple single girder system. The model of the U profile is created step by step according to the modeling chain diagram (figure 4.2). The created mesh is shown in figure 5.9. Figure ?? shows the load onto the upper flange of the girder. The girder is supported on both ends at the lower flange’s edges. Additional point support is needed to avoid longitudinal and transversal move- Figure 5.3: U Profile ments as well as rigid body rotations. If we would not do this, it would be like walking on perfect ice. We may glide in both horizontal directions and may rotate around the vertical axis without any resistivity. So the stiffness matrix of the girder would be singular without the suppression of this degrees of freedom (dof ). So we have to fix three dof s which will suppress this rigid body moves on an arbitrary point in the system. After the calculation is done we will see, whether the choice was ok, if the reaction forces on this fixed dof s are numerically vanishing. 5.2.1 System and Automated Analysis Figure 5.4 shows the single span girder in terms of a simple beam. The load q is distributed constantly on the girder length L. One end of the girder is supported hinged and the other side is supported with a roller support. The following steps of analysis should be done automated by the script. q L Figure 5.4: Girder Single Span • According to the calculation switches the script automatically should be able to do a linear static calculation, a buckling analysis or a frequency analysis; • for a calculation with loads, the script should check automatically the load balancing, i.e. it should check whether the applied load will be totally seen as reaction forces; • the script should find all nodes along a given longitudinal system fiber; • along this fiber the maximal displacement should be evaluated; • pictures of the relevant results should be created in terms of png-files. E. Baeck 5.2. U-GIRDER SCRIPT 5.2.2 Page 105 Scripting and OOP As opposite to our first example, the three trusses (section 5.1), in this case we want to apply the strategy of OOP to our script and want to implement it in terms of classes. So we introduce the following classes. • UGirder, the class of our complete model. • UGirder should have printing features, deriving them from our class Base like in the Python part (see section 3.2.9). • All input data should be handled by a class InputData. • All result data should be handled by a class ResultData. Thus we get the following class structure (see 5.5). Following the concept, we have discussed in part 1 section 3.2, all classes are derived from the superclass Base, which should provide all common features. InputData Base UGirder ResultData Figure 5.5: The U-Girder Class Hierarchy The class UGirder should get methods for every build step of the system. After the calculation is done we need some additional methods, which are discussed later. • createSketch, start with a sketch. • createPart, creates the part we use. • createMaterials, creates the materials of the system. • createProperties, creates the properties of the system. • assignProperties, assign the properties to the girder’s faces. • createMesh, creates the mesh on the girder’s faces. • createInstance, creates the instance, which should be analyzed by Abaqus. • createStep, creates the load step according to the desired analysis mode. • createBCs, creates the boundary conditions inside the last created load step. • createLoads, creates the loads inside the last created load step. 23.4.2015 Analysis of Structures - SS 15 Page 106 5.2.3 Class InputData Figure 5.6 shows the UML class diagram of the InputData class. This class is used to assemble, check and work up the girder’s parameters. InputData + self.h: profile’s height + self.w: profile’s width + self.t: profile’s flange thickness + self.s: profile’s web thickness + self.len: girder’s length + self.load: load to apply on girder’s top face + ... some attributes more Only the most important attributes are shown in the diagram to get a compact picture. All attributes are given in the following table. + self. init (..): constructor + self.setHelpers(..): create helper data + self.check(..): check the input parameter + self.read(..): read parameters from an input file Figure 5.6: UML-Diagram of the InputData Class Attribute Dimension Comment prjName − project’s name h mm profile’s height w mm profile’s width t mm profile’s flange thickness s mm profile’s web thickness len m load kN ymod N/mm nue 1 girder’s length applied load 2 Young’s module Poisson ratio 3 nue kg/mm maxElement 1 maximal allowed element and node number webSeed 1 element number along the the web edge flangeSeed 1 element number along the the flange edge stepType − specifies the calculation type (static, stability, dynamic) mass density (used for frequency analysis) Besides the parameters discussed in the table above, there are some other attributes in the class, which are used to prepare or initialize the parameter set for the calculation. This attributes are explained inside the code. E. Baeck 5.2. U-GIRDER SCRIPT Page 107 The following listing 5.2 shows the implementation of the class InputData. Listing 5.2: Implementation of U-Girders’ InputData Class 1 2 # -*- coding: utf-8 -*__author__ = ’baeck’ 3 4 5 # module content/class from Base import Base 6 7 8 # input dataset class InputData(Base): 9 10 11 # contructor method def __init__(self): 12 13 14 # initialize Base-Data Base.__init__(self) 15 16 17 18 19 # some constants like macros in C self.LINEAR = 0 # linear static calculation self.BUCKLING = 1 # stability analysis self.FREQUENCY = 2 # frequency analysis 20 21 ## dimensions: N, mm, s 22 23 24 25 26 27 28 ## >> start # parameter self.h self.w self.t self.s user input data -of U100 = 100. # data according to profile tables [mm] = 50. = 8.5 = 6.0 29 30 31 # system parameter self.len = 4.0 # length [m] # material parameters self.ymod = 210000. self.nue = 0.3 self.rho = 7.8e-6 # N/ m m Young’s module # Poisson ratio # mass density kg/ m m # loads self.load # load in kN 32 33 34 35 36 37 38 39 = 10. 40 41 42 43 44 45 # mesh parameter (seeds) self.maxElement = int(1000) # element and node number of the self.webSeed = int(4) # student version are restricted to 1000 self.flangeSeed = int(2) # so we have to be carefull self.lengthSeed = self.maxElement/(self.webSeed + self.flangeSeed*2 +1) -1 46 47 48 49 # step or calculation control self.stepType = self.LINEAR self.stepNames = ("Linear","Buckling","Frequency") 50 51 52 # buckling input data self.numBEigen = 6 23.4.2015 Analysis of Structures - SS 15 Page 108 53 54 self.numBIterX = 100 self.numBVectors = self.numBEigen + 10 55 56 57 58 59 60 # frequency input data self.numFEigen = 6 self.numFIterX = 100 self.numFVectors = self.numFEigen + 10 ## >> close user input data -- 61 62 63 64 ## project parameters self.prjName = "U-Girder" self.jobName = self.prjName; 65 66 67 # output control self.bPngFiles = True # flag to control the file export 68 69 70 # read the inputdata (till now not implemented) self.read() 71 72 73 # calculate parameter values for the calculation self.setHelpers() 74 75 76 # check the input data (till now not implemented) self.check() 77 78 79 # calculate parameter values for the calculation def setHelpers(self): 80 81 82 83 84 self.hs = self.ws = self.len *= self.p = (self.h - self.t)/2. self.w - self.s/2. 1000. # length in [mm] self.load/(self.ws*self.len)*1.e3 85 86 87 88 89 # check the calculations parameter def check(self): #> it’s your job! pass 90 91 92 93 94 E. Baeck # read the calculations parameter from a file def read(self): #> we’ll see, whether we will do it! pass 5.2. U-GIRDER SCRIPT 5.2.4 Page 109 Class ResultData Figure 5.7 shows the UML class diagram of the ResultData class. This class is used to hold the calculated result data. ResultData + self.nodePos: node data of stiff fiber + self.nodeRFo: reaction forces + self.nodeDis: node displacements + self.sumRFo: sum of reaction forces to check load balancing The data containers we use here are dictionaries, which are using the node’s key as key value. + self. init (..): contructor + self.getMaxDisp(..): calculate the maximal fiber displacement Figure 5.7: UML-Diagram of the ResultData Class To get the maximal vertical displacement of a very stiff girder fiber to fade out the cross displacement, we have to select the fiber’s nodes. This node data is stored in the dictionarry nodePos. After the calculation is done, we can search for the maximal vertical displacement in the subspace of the fiber nodes. The following listing 5.3 shows the implementation of the class ResultData. Listing 5.3: Implementation of U-Girders’ ResultData Class 1 2 # -*- coding: utf-8 -*__author__ = ’baeck’ 3 4 5 6 7 8 ’’’ container for result data ’’’ from math import fabs from Base import Base 9 10 class ResultData(Base): 11 12 13 14 15 16 17 # constructor def __init__(self): self.nodePos = {} self.nodeRFo = {} self.nodeDis = {} self.sumRFo = [0.,0.,0.] # # # # to store the node coordinates and it’s label to store the reaction forces to store the node displacements sum vector of the reaction forces 18 19 20 21 22 23 24 25 # calculate the maximum displacement along the center line def getMaxDisp(self): max = 0. for label in self.nodePos: disp = self.nodeDis[label] if fabs(disp[1]) > max: max = fabs(disp[1]) return max 23.4.2015 Analysis of Structures - SS 15 Page 110 5.2.5 Class Base From figure 5.5 we can see, that like in the case of the TWA (see section 3.2 too) all classes in our script are derived from a superclass, which is called Base. The only change we make is, to replace the log files name with UGirder.log. The classes’ implementation is given in listing 5.4. Listing 5.4: Implementation of U-Girders’ Base Class 1 2 # -*- coding: utf-8 -*__author__ = ’baeck’ 3 4 5 6 7 8 9 ’’’ all common functions and variables ’’’ # module item our name of this item from datetime import datetime as time import os 10 11 class Base(): 12 13 14 __count = 0 # count the instances __log = "UGirder.log" 15 16 17 18 19 20 # constructor def __init__(self,log = ""): if log != "": Base.__log = log Base.__count += 1 self.appendLog("instance %d created" % Base.__count) 21 22 23 24 # destructor def __del__(self): Base.__count -= 1 25 26 27 28 29 30 # reset all members, here a static method to work on class attributes @staticmethod def resetAll(): Base.__count = 0 os.remove(Base.__log) 31 32 33 34 35 # print into the log file def appendLog(self,text): t = time.now() timestamp = "%2.2d.%2.2d.%2.2d |" % (t.hour,t.minute,t.second) 36 37 38 39 40 textout = timestamp + text # print into the logfile f = file(Base.__log,"a") f.write(textout + "\n") 41 42 E. Baeck print textout 5.2. U-GIRDER SCRIPT 5.2.6 Page 111 Class UGirder The UGirder class will be implemented to organize, calculate and analyze the data. The class’ UML diagram is shown in figure 5.8. UGirder + self.input: InputData object + self.result: ResultData object + self.model: Abaqus model + self.part: Abaqus part + self.material: Abaqus material + self.property: Abaqus property + self.instance: Abaqus instance + self.odb: Abaqus object database Only the most important attributes are shown in the diagram to get a compact picture. + self.viewport: Abaqus viewport + ... some attributes more + self. init (..): constructor + self.createSystem(..): create the entire calculation model + self.doCalculation(..): starts the solver Figure 5.8: UML-Diagram of the UGirder Class The class’ constructor creates an InputData and a ResultData object. To be sure, that all modules are compiled before execution, i.e. a kind of rebuild, we have to reload this modules. This is only working, if we load them as entire module (see section A.1 too). Further methods are implemented to add feature by feature to our U-Girder system. All this steps are done within the method createSystem. The fist part of the class’ listing 5.5 shows all methods, to create the geometric system and its mesh. The mesh we can see in figure 5.9. Listing 5.5: Implementation of U-Girders’ Main Class UGirder Class, Part System 1 2 # -*- coding: utf-8 -*__author__ = ’baeck’ 3 4 5 6 7 8 9 10 11 12 13 ’’’ the girder’s main class ’’’ # for testing only, to be sure, that every modul will be compiled import Base reload(Base) import InputData reload(InputData) import ResultData reload(ResultData) 14 15 16 # UGirder imports from Base import Base 23.4.2015 Analysis of Structures - SS 15 Page 112 17 18 19 from InputData import InputData from ResultData import ResultData from math import sqrt 20 21 22 23 24 # all abaqus imports from abaqus import * from caeModules import * from abaqusConstants import * 25 26 27 # the UGirder class class UGirder(Base): 28 29 30 31 32 # constructor def __init__(self): Base.__init__(self) Base.resetAll() 33 34 35 36 37 38 39 40 41 42 43 44 45 46 # references to Abaqus objects self.input = InputData() self.result = ResultData() self.model = None self.sketch = None self.part = None self.materials = [] self.properties = [] self.instance = None self.odb = None self.viewport = None self.selectFlange = None self.selectWeb = None 47 48 49 50 51 52 53 54 55 56 57 # creates all used objects to create the FEM model def createSystem(self): self.createModel() self.createPart() self.createMaterials() self.createProperties() self.assignProperties() self.createMesh() self.getFiberNodes() self.createInstance() 58 59 60 61 62 63 64 65 66 # creates the model instance def createModel(self): self.appendLog("create model...") try: del mdb.models[self.input.prjName] except: pass self.model = mdb.Model(name=self.input.prjName) 67 68 69 70 # creates a part from a sketch def createPart(self): self.appendLog("create a part from a sketch...") 71 72 E. Baeck # do a little abbreviation 5.2. U-GIRDER SCRIPT 73 Page 113 data = self.input 74 75 76 77 # create the sketch self.appendLog("create sketch...") sketch = self.model.ConstrainedSketch(name=data.prjName,sheetSize=data.h*2.) 78 79 80 81 # specify the polygon points xyPoints = ( (data.ws , data.hs), ( 0., data.hs) , ( 0.,-data.hs), (data.ws,-data.hs) ) 82 83 84 85 86 87 88 89 # create the lines for i in range(1,len(xyPoints)): msg = "Line %d: x1 = %10.3f y1 = %10.3f x2 = %10.3f y2 = %10.3f" % \ (i,xyPoints[i-1][0],xyPoints[i-1][1], xyPoints[ i][0],xyPoints[ i][1]) self.appendLog(msg) sketch.Line(point1=xyPoints[i-1],point2=xyPoints[i]) 90 91 92 93 94 95 # create the part self.part = self.model.Part(name=data.prjName, dimensionality=THREE_D, type=DEFORMABLE_BODY) self.part.BaseShellExtrude(sketch=sketch,depth=data.len) 96 97 98 99 100 101 102 # creates the materials def createMaterials(self): self.appendLog("create materials...") self.materials.append(self.model.Material(name="steel")) self.materials[0].Elastic(table = ( (self.input.ymod, self.input.nue) ,) ) self.materials[0].Density(table = ( (self.input.rho, ) ,) ) 103 104 105 106 107 108 109 110 111 112 113 # creates the properties def createProperties(self): self.appendLog("create properties...") self.properties = [] self.model.HomogeneousShellSection(name="Flange", material="Steel", thickness=self.input.t) self.model.HomogeneousShellSection(name="Web", material="Steel", thickness=self.input.s) 114 115 116 117 118 119 120 121 122 123 124 125 # assign the properties def assignProperties(self): self.appendLog("assign properties...") data = self.input self.selectFlange = self.part.faces.findAt( ( (data.ws/2., -data.hs,data.len/2.) ,), ( (data.ws/2., data.hs,data.len/2.) ,)) #usage of regionToolset: #region = regionToolset.Region(faces = selectFlanges) #myPart.SectionAssignment(region=region, ...) self.part.SectionAssignment(region=(self.selectFlange,),sectionName="Flange") 126 127 128 # - Web self.selectWeb = self.part.faces.findAt( ( ( 0., 0., data.len/2.) ,),) 23.4.2015 Analysis of Structures - SS 15 Page 114 129 self.part.SectionAssignment(region=(self.selectWeb,),sectionName="Web") 130 131 132 133 134 135 136 137 138 # creates the mesh def createMesh(self): self.appendLog("generate mesh...") data = self.input # - assign element type: S4R elemType = mesh.ElemType(elemCode = S4R) self.part.setElementType(regions=(self.selectFlange,self.selectWeb), elemTypes = (elemType,)) 139 140 141 142 143 144 145 146 # - assign seeds # o flange select = self.part.edges.findAt( ( (data.ws/2., data.hs, 0.), ( (data.ws/2.,-data.hs, 0.), ( (data.ws/2., data.hs, data.len), ( (data.ws/2.,-data.hs, data.len), self.part.seedEdgeByNumber(edges=select,number=data.flangeSeed) ), ), ), ),) 147 148 149 150 151 # o web select = self.part.edges.findAt( ( (0.,0., 0.), ), ( (0.,0., data.len), ),) self.part.seedEdgeByNumber(edges=select,number=data.webSeed) 152 153 154 155 156 157 158 # o length direction select = self.part.edges.findAt( ( (data.ws, data.hs, data.len/2.), ( ( 0., data.hs, data.len/2.), ( ( 0.,-data.hs, data.len/2.), ( (data.ws,-data.hs, data.len/2.), self.part.seedEdgeByNumber(edges=select,number=data.lengthSeed) ), ), ), ),) 159 160 161 # generate mesh self.part.generateMesh() 162 163 164 165 # searching for fiber nodes, i.e nodes on the center line def getFiberNodes(self): self.appendLog("find nodes on center fiber...") 166 167 168 # iterate the container (list: the data is given) for node in self.part.nodes: 169 170 171 172 173 # calculate the distance of the node from the centerline # precision = 1 mm if (sqrt(node.coordinates[0]**2 + node.coordinates[1]**2) < 1.): self.result.nodePos[node.label] = node.coordinates 174 175 176 177 178 179 180 181 182 183 184 E. Baeck self.appendLog("%d elements created." % len(self.part.elements)) self.appendLog("%d nodes created." % len(self.part.nodes)) self.appendLog("%d nodes on the center line." % len(self.result.nodePos)) # print node coordinates on center line # iterate the container (dictionary: -> the key is given) self.appendLog("--no ---------x ---------y ---------z") for label in self.result.nodePos: node = self.result.nodePos[label] self.appendLog("%4d %10.2f %10.2f %10.2f" % \ (label,node[0],node[1],node[2])) 5.2. U-GIRDER SCRIPT Page 115 185 186 187 188 189 190 191 # create instance def createInstance(self): self.appendLog("create instance...") self.instance = self.model.rootAssembly.Instance(name=self.input.prjName, part=self.part, dependent=ON) Figure 5.9: U Girder’s Mesh To be able to do an independent calculation for the linear static, the stability and the frequency analysis, we first delete an existing load step called Step-1 and then we create the desired type of load step. If we do this, we easily can run the calculation of all load steps within one calculation loop. Listing 5.6: Implementation of U-Girders’ Main Class UGirder Class, Part Loads 1 2 3 4 5 6 7 8 9 10 11 12 13 # create step def createStep(self,type): self.appendLog("create step of type ’%s’..." % self.input.stepNames[type]) self.input.stepType = type # first we have to delete an old created step, to clear the momory if len(self.model.steps) > 1: del self.model.steps["Step-1"] # then we create the new one # create a linear static step with it’s data if type == self.input.LINEAR: self.model.StaticStep(name="Step-1",previous="Initial") self.createBCs(type) self.createLoads(type) 14 15 16 17 18 19 20 21 22 23 # create a buckling step elif type == self.input.BUCKLING: self.model.BuckleStep(name="Step-1", previous="Initial", numEigen = self.input.numBEigen, maxIterations = self.input.numBIterX, vectors = self.input.numBVectors) self.createBCs(type) self.createLoads(type) 23.4.2015 Analysis of Structures - SS 15 Page 116 24 25 26 27 28 29 30 31 32 33 # create a frequency step elif type == self.input.FREQUENCY: self.model.FrequencyStep(name="Step-1", previous="Initial", eigensolver=SUBSPACE, numEigen = self.input.numFEigen, maxIterations = self.input.numFIterX, vectors = self.input.numFVectors) self.createBCs(type) 34 35 36 37 38 39 40 41 42 43 44 45 # create BC for the U-girder # stepIndex.: step index def createBCs(self,stepIndex): data = self.input self.appendLog("create boudary conditions...") select = self.instance.edges.findAt( ( (data.ws/2.,data.hs, 0.),), ( (data.ws/2.,data.hs,data.len),),) self.model.DisplacementBC(name="vertical line BCs", createStepName="Step-1", region=(select,), u2=0.0) 46 47 48 49 50 51 select = self.instance.vertices.findAt( ( (0.,data.hs,0.), ),) self.model.DisplacementBC(name="rigid body mode BCs", createStepName="Step-1", region=(select,), u1=0.0,u3=0.0,ur2=0.0) 52 53 54 55 56 57 58 59 60 61 62 63 # create loads on the top face of the U girder # stepIndex.: step index def createLoads(self,stepIndex): data = self.input self.appendLog("create loads...") select = self.instance.faces.findAt( ( (data.ws/2.,-data.hs, data.len/2.), ),) region = regionToolset.Region(side1Faces=select) self.model.Pressure(name="flange pressure", createStepName="Step-1", region=region, magnitude=data.p) Figure 5.10 shows the girder with it’s loads and the boundary conditions after the creation of the steps Linear Static and Buckling. Listing 5.7: Implementation of U-Girders’ Main Class UGirder Class, Part Job 1 2 3 4 5 6 # submit and run the job def runJob(self): self.input.jobName = (self.input.prjName + "-%d") % self.input.stepType self.appendLog("create job %s" % self.input.jobName) job = mdb.Job(name=self.input.jobName,model=self.input.prjName) 7 8 9 10 E. Baeck self.input.appendLog("submit job %s" % self.input.jobName) job.submit() job.waitForCompletion() 5.2. U-GIRDER SCRIPT Page 117 Figure 5.10: Loads and Boundary Conditions 11 self.appendLog("job %s done" % self.input.jobName) 12 13 14 15 16 17 # open result database und assign it to the viewport def openDatabase(self): self.odbName = self.input.jobName + ".odb" self.appendLog("open database file %s" % self.odbName) self.odb = session.openOdb(self.odbName) 18 19 20 21 # set the viewport self.viewport = session.viewports["Viewport: 1"]; self.viewport.setValues(displayedObject=self.odb) 22 23 24 25 26 27 28 29 # analyze the result data def analyzeResults(self): # set the font size of the legend self.setFontSize(12) # set the view perspective self.viewport.view.setViewpoint(viewVector=(1, -0.25, -1), cameraUpVector=(0, -1, 0)) 30 31 32 33 34 35 36 self.input.stepType == self.input.LINEAR: self.analyzeLinearStep() elif self.input.stepType == self.input.BUCKLING: self.analyzeBuckling() elif self.input.stepType == self.input.FREQUENCY: self.analyzeFrequency() if 37 38 39 40 41 42 # analyze the linear static step def analyzeLinearStep(self): self.appendLog("analyze the linear static results") data = self.input result = self.result 43 44 45 # calculte the sum of the reaction forces self.result.sumRFo = [0.,0.,0.] 46 23.4.2015 Analysis of Structures - SS 15 Page 118 47 48 49 # - select results # |the last frame frame = self.odb.steps["Step-1"].frames[-1] 50 51 52 53 54 55 56 57 58 59 60 # - select the reaction forces rfo = frame.fieldOutputs[’RF’] # - over all nodes for value in rfo.values: # filter nodes with vanishing RFs if (sqrt( value.data[0]**2 + value.data[1]**2 + value.data[2]**2 ) > 1.e-10): result.nodeRFo[value.nodeLabel] = value.data for i in range(3): result.sumRFo[i] += value.data[i] 61 62 63 64 self.appendLog("sum of the reaction forces: %12.3e %12.3e %12.3e" % tuple(result.sumRFo)) error = (data.load*1.e3 + result.sumRFo[1])/(data.load*1.e3)*100. self.appendLog("error.....................: %12.3f %%" % error) 65 66 67 68 69 70 71 # iterate and print the RF container (dictionary: -> the key is given) self.appendLog("--no ---------RFx ---------RFy ---------RFz") for label in result.nodeRFo: nodeRF = result.nodeRFo[label] self.appendLog("%4d %12.3e %12.3e %12.3e" % \ (label,nodeRF[0],nodeRF[1],nodeRF[2])) 72 73 74 75 76 77 # - select the node displacements dis = frame.fieldOutputs[’U’] # - over all nodes for value in dis.values: result.nodeDis[value.nodeLabel] = value.data 78 79 self.appendLog("maxium center displacement: %12.3e mm" % result.getMaxDisp()) 80 81 82 # optionally create png-Files if data.bPngFiles: 83 84 85 86 87 88 89 90 varList = ("U1","U2","U3") for var in varList: # plot into the viewport self.viewport.odbDisplay.setPrimaryVariable( variableLabel=’U’, outputPosition=NODAL, refinement=(COMPONENT,var)) 91 92 93 94 # in colour on the system faces self.viewport.odbDisplay.display.setValues(plotState=(CONTOURS_ON_DEF,)) self.viewport.view.fitView() 95 96 97 98 # print the image into a file pngFile = data.prjName + "-" + data.stepNames[0] + "-" +var self.printPngFile(pngFile) 99 100 101 102 E. Baeck # analyze the buckling step def analyzeBuckling(self): self.appendLog("analyze the buckling step") 5.2. U-GIRDER SCRIPT 103 104 Page 119 data = self.input result = self.result 105 106 107 # over all eigenforms for i in range(data.numBEigen): 108 109 110 111 112 113 # plot into the viewport self.viewport.odbDisplay.setPrimaryVariable( variableLabel=’U’, outputPosition=NODAL, refinement=(INVARIANT,’Magnitude’)) 114 115 116 117 self.viewport.odbDisplay.display.setValues(plotState=(CONTOURS_ON_DEF,)) self.viewport.odbDisplay.setFrame(step="Step-1", frame=i+1) self.viewport.view.fitView() 118 119 120 pngFile = data.prjName + "-" + data.stepNames[1] + ("-E%2.2d" % (i+1,)) self.printPngFile(pngFile) 121 122 123 124 125 126 # analyze the frequency step def analyzeFrequency(self): self.appendLog("analyze the frequency step") data = self.input result = self.result 127 128 129 # over all eigenforms for i in range(data.numFEigen): 130 131 132 133 134 135 # plot into the viewport self.viewport.odbDisplay.setPrimaryVariable( variableLabel=’U’, outputPosition=NODAL, refinement=(INVARIANT,’Magnitude’)) 136 137 138 139 self.viewport.odbDisplay.display.setValues(plotState=(CONTOURS_ON_DEF,)) self.viewport.odbDisplay.setFrame(step="Step-1", frame=i+1) self.viewport.view.fitView() 140 141 142 pngFile = data.prjName + "-" + data.stepNames[2] + ("-E%2.2d" % (i+1,)) self.printPngFile(pngFile) 143 144 145 146 147 148 149 150 151 152 # set the fontsize # - size....: font size in [pt] def setFontSize(self,size): fsize = int(size*10) self.viewport.viewportAnnotationOptions.setValues( triadFont=’-*-verdana-medium-r-normal-*-*-%d-*-*-p-*-*-*’ % fsize, legendFont=’-*-verdana-medium-r-normal-*-*-%d-*-*-p-*-*-*’ % fsize, titleFont=’-*-verdana-medium-r-normal-*-*-%d-*-*-p-*-*-*’ % fsize, stateFont=’-*-verdana-medium-r-normal-*-*-%d-*-*-p-*-*-*’ % fsize) 153 154 155 156 157 158 # print the viewports content into a png file # - pngFile...: file name # - myViewport: data def printPngFile(self,pngFile): session.printOptions.setValues(vpBackground=ON) 23.4.2015 Analysis of Structures - SS 15 Page 120 session.printToFile(fileName=pngFile, format=PNG, canvasObjects=(self.viewport,)) 159 160 5.2.7 Run the UGirder Code If we organize the girder code using classes the main program to run it will be very lightweight (see listing 5.8). After setting up the work directory, where we will find the created files, we create the mesh of the system. After this we run a loop over all implemented load steps and run the linear static, the buckling analysis and the frequency analysis. After the calculation is done the figures are drawn automatically and stored into png files. Listing 5.8: Implementation a Main Code for U-Girders to Automate the Calculation 1 __author__ = ’baeck’ 2 3 4 5 6 7 ’’’ we can start the script using the followning command: execfile(r’[path]\mainUGirder.py’) execfile(r’c:\unidue\CM\Cm-AoS\AOS-BookOfExamples\Py\Code\Abaqus\UGirder\mainUGirder.py’) ’’’ 8 9 10 11 # set the workdirectory import os os.chdir(r’c:\unidue\CM\Cm-AoS\AOS-BookOfExamples\Py\Code\Abaqus\UGirder’) 12 13 14 15 # for testing only import UGirder reload(UGirder) 16 17 18 # import UGirder and it’s friends from UGirder import UGirder 19 20 21 22 # create the geometric system, the mesh sys = UGirder() sys.createSystem() 23 24 25 26 27 28 29 # create steps, run job and analyse for i in range(3): sys.createStep(i) sys.runJob() sys.openDatabase() sys.analyzeResults() E. Baeck 5.2. U-GIRDER SCRIPT 5.2.7.1 Page 121 Results of the Linear Static Step Figure 5.11 shows the resulting displacements in 1(x), 2(y) and 3(z) direction of a linear analysis. Figure 5.11: Displacement in 1,2 and 3 Direction of a Linear Analysis 23.4.2015 Analysis of Structures - SS 15 Page 122 5.2.7.2 Results of the Buckling Step Figure 5.12 shows the buckling modes 1 uto 3. Figure 5.12: Buckling Modes 1 to 3 E. Baeck 5.2. U-GIRDER SCRIPT Page 123 Figure 5.13 shows the buckling modes 4 uto 6. Figure 5.13: Buckling Modes 4 to 6 23.4.2015 Analysis of Structures - SS 15 Page 124 5.2.7.3 Results of the Frequency Step Figure 5.14 shows the dynamic modes 1 uto 3. Figure 5.14: Dynamic Modes 1 to 3 E. Baeck 5.2. U-GIRDER SCRIPT Page 125 Figure 5.15 shows the dynamic modes 4 uto 6. Figure 5.15: Dynamic Modes 4 to 6 23.4.2015 Page 126 E. Baeck Analysis of Structures - SS 15 Part III Appendix 127 Appendix A Some Special Problems In this chapter we talk about some special problems in Python. A.1 Modules and Packages If a module is loaded into the Python interpreter to run a script, all changes within the loaded modules became active, if the script is reloaded. To reload a module on the fly, we have to apply the function reload(). If a module should be reloaded, we have to import it first without the from key. After the module is loaded, the module name is declared and we can start the reload() command. After the reload() we can import the module with the from key. 1 2 3 # forced reload for the developer step import InputData # this binds the module to the symbol reload(InputData) # now we reload in any case 4 5 6 7 # from InputData import InputData from InputData import InputData # this is our standard import data= InputData() 129 Page 130 E. Baeck Analysis of Structures - SS 15 Appendix B Some Theory B.1 Section Properties Within this chapter the formulas for the section properties of a thin walled model are given. A thin walled model for a profile section consists of a set of lines which describes the profile section geometry at the centerline. B.1.1 The Area of a Profile Section The Area is approximately the sum of the areas of the lines of the thin walled model. Z eµ · dA ≈ A= A with: Li ti eµ,i B.1.2 n X eµ,i · Li · ti (B.1) i=1 the length of line i the thickness of line i the relative elasticity of line i (1 for only one material) First Moments of an Area The first moments of an area are the area integrals given below. The (y,z) values are related to an given coordinate system. Z eµ · z · dA ≈ Sy = A Z eµ · y · dA ≈ Sz = A with: Ai yi zi n X i=1 n X eµ,i · z i · Ai eµ,i · y i · Ai i=1 the area of a line i the y coordinate of the center of line i the z coordinate of the center of line i 131 (B.2) Analysis of Structures - SS 15 Page 132 B.1.3 Second Moments of an Area or Moments of Inertia The moments of inertia can be calculated with the formulas below. If we have a given arbitrary coordinate system in general we have three values of inertia the Iy , the Iz and the mixed Iyz . If we use the main coordinate system, the mixed moment of inertia is vanishing, so we use the symbols Iξ and Iη . Z 2 eµ · z · dA ≈ Iy = n X A Z n X A eµ,i · (yb,i − ya,i )2 /12) + y 2i · Ai i=1 n X Z eµ · y · z · dA ≈ Iyz = A with: Ai yi zi ya,i za,i yb,i zb,i (zb,i − za,i )2 /12) + z 2i · Ai i=1 eµ · y 2 · dA ≈ Iz = eµ,i · eµ,i · (((yb,i − ya,i )(zb,i − za,i )/12) + y i · z i ) · Ai ) (B.3) i=1 the area of a line i the y coordinate of the center of line i the z coordinate of the center of line i the y coordinate of the first point of line i the z coordinate of the first point of line i the y coordinate of the second point of line i the z coordinate of the second point of line i R To solve an integral like Iy = A z 2 · dA for a polyline we can split up the integral into the sum of integrals over the polyline segments. Z Iy = A z 2 · dA = n Z X i=1 z 2 · dA (B.4) Ai To solve an integral for a polyline segment we simple calculate it for the center of mass, because a simple shift only will give us an additional term, the Steiner term. If we now want to calculate the polyline integral at the center of mass we rotate the coordinate system by an angle ϕ into the line’s longitudinal direction, because the transversal dimension, the thickness, is constant and so the respective integral will be trivial. E. Baeck B.1. SECTION PROPERTIES Page 133 Thus we make the following substitution. (y, z) ⇒ (η, ξ) (B.5) z = ξ/cos(ϕ) (B.6) With this substitution we will get the following integral. Z η=+t Z ξ=+L/2 Iy,i = ξ2 · dη · dξ cos(ϕ)2 η=−t ξ=−L/2 Z ξ=+L/2 ξ2 =t· · dξ cos(ϕ)2 ξ=+L/2 ξ3 1 = t· · 3 cos(ϕ)2 ξ=−L/2 ξ=−L/2 =t· L3 1 · 12 cos(ϕ)2 (zb,i − za,i )2 = · Ai 12 B.1.4 with t · L = Ai (B.7) Center of Mass The coordinates of the center of mass are calculated with the arithmetic mean. Because the numerator of the arithmetic mean is identical with the first moment of the area (see section B.1.2) and the denominator is identical with the area of the profile, which is calculated in section B.1.1 we can use this values. R y · dA Sz yc = AR = A dA R A z · dA Sy zc = AR = (B.8) A A dA B.1.5 Moments of Inertia with Respect to the Center of Mass If we know the center of mass coordinates given in section B.1.4 we can calculate the moments of inertia with respect to the center of mass using Steiner’s Theorem as follows. Iy,c = Iy − zc2 · A Iz,c = Iz − yc2 · A Iyz,c = Iyz − yc · zc · A (B.9) 23.4.2015 Analysis of Structures - SS 15 Page 134 B.1.6 Main Axis Transformation To get the moments of inertia Iη and Iξ we have to transform the moments of inertia into the main coordinate system. Using this coordinate system the mixed moment of inertia is vanishing. The main axis transformation is given with equation B.10.1 Idel = Iy,c − Iz,c Isum = Iy,c + Iz,c q 2 + 4 · I2 Isqr = IDel yz,c −2 · Iyz,c 1 · arctan( ) 2 Idel 1 Iη = · (Isum + Isqr ) 2 1 Iξ = · (Isum − Isqr ) 2 ϕ= 1 (B.10) The rotation angle ϕ should be shifted into the intervall [−π/2... + π/2]. To avoid a zero division calculating the rotation angle ϕ a special version of the atan function should be used, which is able to handle the pole problem. In Python like in C this function is called atan2(x, y), which calculates the atan( xy ). E. Baeck Appendix C Some Python IDEs C.1 The Aptana - IDE There are lot of IDEs available for the development of Python software. Most of them are commercial. One very nice IDE especially for large development projects with a lot of Python files is called Aptana. Apatana is a special version of the free Eclipse IDE. For this IDE there is a plugin available, which is called PyDev. To use this IDE first you have to install the basis version of Aptana and then you should install the plugin and select the desired Python version, which should be installed before. An example project within the Aptana is shown in figure C.1. Figure C.1: Aptana IDE 135 Analysis of Structures - SS 15 Page 136 C.2 The PyCharm - IDE C.2.1 General Statements PyCharm is an IDE (integrated development environment) which was developed from JetBrains1 . You can download a free community edition, which we will use in our lecture. The IDE is available for Windows, Linux and in MacOS. In contrast to the little PyWin-IDE. PyCharm is working like modern IDEs with projects, i.e. everything you want to develop, is put into a container, which is called a project. This avoids the sometimes up coming confusion, if you are working only with single files. C.2.2 A Hello-Example If we want to implement the famous Hello World example, known as a general start up, we have to do this in two steps. 1. We have to create a project within a project directory. 2. We have to create a new Python file, which should print the desired output to the screen. First of all we have to start the IDE clicking on the icon on the desktop or running the exe-file from the installation folder. The start up page is display like shown in figure C.2. Figure C.2: Start up Dialog of PyCharm After having clicked on ”Create New Prjoject” the dialogs come up, which specifies the project, see figure C.3. We have set up a new project name. The name is used to specify the project folder. You can 1 You can download the software using the following link https://www.jetbrains.com/pycharm/download/ E. Baeck C.2. THE PYCHARM - IDE Page 137 edit the default project folder name. In the third control you have to select the desired Python interpreter. In our case the version 2.7.3 was selected. Figure C.3: Creating our New Hello Project After having created the empty Hello project we see the following page, see figure C.4. Figure C.4: Empty Hello Project Now we have to create a new Python file, which should print the famous message to the screen. So we select the menu item File/New... and from the displayed list we select the item file. Then a dialog is displayed to enter the file’s name. Here we don’t need to input the absolute file name with it’s folder name. This we see in figure C.5. Figure C.5: Enter the File’s Name After having created a new empty file with the name hello.py, we input the one and only statement of our new application, the statement print "Hello World!". This we see in figure C.6. To run the application, we have to select the project Hello and click the green run button. If this is done, a new window if not already opened will be displayed with the console output, i.e. with the message ”Hello World!”. This we can see in figure C.7. 23.4.2015 Analysis of Structures - SS 15 Page 138 Figure C.6: One and Only Statement of Our New Application Figure C.7: Run the Hello Application E. Baeck Appendix D Conventions D.1 The Java Code Conventions The following code convention [7] is published by Oracle (successor of Sun Microsystems, Inc). We apply this convention to choose names for our software items. 1. Classes Class names should be nouns, in mixed case with the first letter of each internal word capitalized. Try to keep your class names simple and descriptive. Use whole words-avoid acronyms and abbreviations (unless the abbreviation is much more widely used than the long form, such as URL or HTML). 2. Methods Methods should be verbs, in mixed case with the first letter lowercase, with the first letter of each internal word capitalized. 3. Variables Except for variables, all instance, class, and class constants are in mixed case with a lowercase first letter. Internal words start with capital letters. Variable names should not start with underscore _ or dollar sign $ characters, even though both are allowed. Variable names should be short yet meaningful. The choice of a variable name should be mnemonic- that is, designed to indicate to the casual observer the intent of its use. One-character variable names should be avoided except for temporary ”throwaway” variables. Common names for temporary variables are i, j, k, m, and n for integers; c, d, and e for characters. 4. Constants The names of variables declared class constants and of ANSI constants should be all uppercase with words separated by underscores ("_"). (ANSI constants should be avoided, for ease of debugging.) 139 Page 140 E. Baeck Analysis of Structures - SS 15 Appendix E Parallel Computing E.1 Threads The most general parallelisation of code is given in the usage of so called threads. A thread is like an application, which is started and which is running then independent of it’s creating program. Threads are often used, to run some time consuming events in the background of an interactive program. If this activity would not run independent in the background, it would block all interactive events of the application, like clicking on buttons or scrolling the window contents and the user would have the feeling, that this application is hanging. Threads run parallel on one processor using a task switch strategy. For example, if we run a browser and a mailing application on an old computer, we get the feeling, that they are be executed in parallel. With on processor we can not do this. Applications are executed piecewise sequentially. If the hardware however comes with more than one processor, in general the operating system, i.e. Windows in our case, is able to distribute this threads onto all available processor, so that they can be executed in parallel. This we can see, checking the processor loads. The disadvantage of this kind of threads is, that in general the end of a thread have to be caught by the caller, so that the caller is able to use the outcome of the thread. This in general costs an effort of administration. E.2 A Multi-Processing Pool In contrast to general threads we can use a so called Multi-Processing-Pool. This pool is filled this threads of the same kind. If the calculation is started, all this threads are executed on a given set of processors. This execution is sequential from the view of the calling program, i.e. the calling program is calling the Multi-Processing-Pool being executed and will halt until all threads are executed, so that we will not need a code which is synchronizing the execution of the threads. A typical use case for this is a simple loop with independent cycles. A really nice example, which shows the usage of a Multi-Processing-Pool is the calculation of the so called Mandelbrot set1 . This calculation can be split up in several independent cycles. To show the 1 Benoˆıt B. Mandelbrot, described the Mandelbrot set as a number of complex numbers. 141 Analysis of Structures - SS 15 Page 142 performance of parallel execution we introduce the TimeCheck class given in the listing . The Mandelbrot set is given by the following iteration formula. 2 zn = zn−1 +c (E.1) z, c ∈ C with If the absolute value is less equal two it’s a Mandelbaum set point. Otherwise it’s not. In figure E.1 the Points of the Mandelbraum set are plotted in the complex number plane. E.2.1 A Single Processor Solution The function mandelbrotCalcRow calculates the the data for one row of the Mandelbrot set picture. We put this function partialCalcRow with its function parameters xrange(h) into a map, which calculates the values for all function parameters. The function partial is used to reduce the number of arguments of the function mandelbrotCalcRow simply to one, i.e. the last non set, i.e. the ypos argument. So the parameter space is reduced from 4 to 1. For this dimension, i.e. ypos, the map is created. On a i7 processor we need 21 seconds to get the result. Listing E.1: Mandelbrot Single Processor Solution 1 2 3 import matplotlib.pyplot as plt from functools import partial from TimeCheck import TimeCheck # is used to show the results # to create a one dimensional function # performance checker 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # calculation of one mandelbrot row def mandelbrotCalcRow(yPos, h, w, max_iteration = 1000): y0 = yPos * (2/float(h)) - 1 # rescale to -1 to 1 row = [] for xPos in range(w): x0 = xPos * (3.5/float(w)) - 2.5 # rescale to -2.5 to 1 iteration, z = 0, 0 + 0j c = complex(x0, y0) while abs(z) < 2 and iteration < max_iteration: z = z**2 + c iteration += 1 row.append(iteration) return row 18 19 20 21 22 23 24 # creates a set of calculations def mandelbrotCalcSet(h, w, max_iteration = 1000): partialCalcRow = partial(mandelbrotCalcRow, h=h, w=w, max_iteration = max_iteration) mandelImg = map(partialCalcRow, xrange(h)) return mandelImg 25 26 27 28 29 30 # main program calling the mandelbrot calculations # create the timecheck object s = TimeCheck() # and set current time s.set() 31 32 # run the mandelbrot calculation E. Baeck E.2. A MULTI-PROCESSING POOL 33 34 35 Page 143 mandelImg = mandelbrotCalcSet(400, 400, 1000) # print the performance data s.get("single processor performance") 36 37 38 39 plt.imshow(mandelImg) plt.savefig(’mandelimg.png’) # plt.show() Listing E.2: Output for a i7 Processor 1 E.2.2 21.912 : single processor performance A Multi Processor Solution If we now want to use the power of our i7 processor, i.e. if we want to use the 4 available cores, we have to distribute the calculation load of the Mandelbrot set rows onto this cores. This we see in listing E.3. Listing E.3: Mandelbrot Multi Processor Solution 1 2 3 4 import multiprocessing import matplotlib.pyplot as plt from functools import partial from TimeCheck import TimeCheck # # # # is used to get multi processor support is used to show the results to create a one dimensional function performance checker 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # calculation of one mandelbrot row def mandelbrotCalcRow(yPos, h, w, max_iteration = 1000): y0 = yPos * (2/float(h)) - 1 # rescale to -1 to 1 row = [] for xPos in range(w): x0 = xPos * (3.5/float(w)) - 2.5 # rescale to -2.5 to 1 iteration, z = 0, 0 + 0j c = complex(x0, y0) while abs(z) < 2 and iteration < max_iteration: z = z**2 + c iteration += 1 row.append(iteration) return row 19 20 21 22 23 24 25 # creates a set of calculations and distributes it onto 4 processors def mandelbrotCalcSet(h, w, max_iteration = 1000): # make a helper function that better supports pool.map by using # only 1 parameter partialCalcRow = partial(mandelbrotCalcRow, h=h, w=w, max_iteration = max_iteration) 26 27 28 # creates a pool of process, controls worksers pool =multiprocessing.Pool(processes=4) 29 30 31 32 33 34 35 # the pool.map only accepts one iterable, so use the partial function # so that we only need to deal with one paramter. # the pool method map runs the set in parallel mandelImg = pool.map(partialCalcRow, xrange(h)) pool.close() # we are not adding any more processes and close pool.join() # tell it to wait until all threads are done before going on 36 23.4.2015 Analysis of Structures - SS 15 Page 144 return mandelImg 37 38 39 40 # check, if thread is main tread! if __name__==’__main__’: 41 # main program calling the mandelbrot calculations # create the timecheck object s = TimeCheck() # and set current time s.set() 42 43 44 45 46 47 mandelImg = mandelbrotCalcSet(400, 400, 1000) # print the performance data s.get("quadcore performance") 48 49 50 51 # show result image plt.imshow(mandelImg) plt.savefig(’mandelimg.png’) 52 53 54 Listing E.4: Output for a i7 Processor 1 9.941 : quadcore performance We see that the function mandelbrotCalcRow in both cases is the same. One difference is, that in multi processor case we use the pool and it’s map. Another difference is, that, if the pyfile is executed, the main run must be marked, that the main program is not multiple executed. We see too, that obvious we only reach a performance jump of 100%, i.e. the administration of the load distribution needs a lot of power. This comes from the fact, that the python interpreter has to load the file multiple, which should be executed in parallel. If the calculation really splits up into independent cycles, we would expect nearly a factor of 4 instead of 2. Figure E.1: Mandelbrot Set Figure E.1 shows the result of our calculation. Every cycle is calculating one row of this picture. E. Baeck Appendix F Some Special Abaqus-GUI-Features In this chapter we talk about some special features of the Abaqus-GUI. F.1 Viewport Annotations F.1.1 The Legend’s Font Size Sometimes the standard parameters of the Abaqus GUI are really unlucky. So the standard font is set to 8 points, i.e. it’s impossible to read the text inside the legend, if we create a standard figure with the following code. The script will plot the calculated displacements colored onto the displaced system. In a loop we select the displacement related to all coordinate axis. 1 2 3 # - create png-files of the displacement components u1, u2, u3 (x,y,z) varlist = ("U1","U2","U3") for var in varlist: 4 5 6 7 8 9 # - plot the results into the window myViewport.odbDisplay.setPrimaryVariable( \ variableLabel=’U’, outputPosition=NODAL, refinement = (COMPONENT,var)) myViewport.view.fitView() 10 11 12 13 14 15 # - and then into an png-file pngfile = prjname + "-" + var session.printOptions.setValues(vpBackground=ON) session.printToFile(fileName=pngfile, format=PNG, canvasObjects=(myViewport,)) If we record the selection of a proper font we can use this code to set the font size automatically. The following code snippet will do this (see figure F.2). 1 2 3 4 5 session.viewports[’Viewport: 1’].viewportAnnotationOptions.setValues( triadFont=’-*-verdana-medium-r-normal-*-*-100-*-*-p-*-*-*’, legendFont=’-*-verdana-medium-r-normal-*-*-100-*-*-p-*-*-*’, titleFont=’-*-verdana-medium-r-normal-*-*-100-*-*-p-*-*-*’, stateFont=’-*-verdana-medium-r-normal-*-*-100-*-*-p-*-*-*’) 145 Analysis of Structures - SS 15 Page 146 The viewport annotations can be changed with Viewport/Viewport Annotation Options... from the menu. To change the font size, we select the tab Legend. Within this tab we see that there are a lot of attributes to specify the legends layout. One of them is the sub-dialog called Set Font.... If we click this button, the dialog to set the fonts will be displayed. Figure F.1: The Legends attributes Now we can select the desired font attributes like type or size. In the last section of this dialog we can assign this attributes to one or more objects. Figure F.2: Select and Assign Font Attributes E. Baeck F.2. SPECIFY VIEW F.2 Page 147 Specify View A view can be specified by setting the view direction, called Viewpoint and the Up vector. The Viewpoint specifies the view direction. We should input a vector of this direction. Only the vector direction is important not the vector length. The second vector specifies the up direction, so we have to specify this direction with a vector again, whose direction only is important. We can input this data within the following dialog of the View menu (see F.3).. Figure F.3: Specify the View In following code we can see, that we select from the session’s viewport container the standard viewport "Viewport: 1". It’s view member provides a method to specify the directions discussed above. 1 2 session.viewports[’Viewport: 1’].view.setViewpoint(viewVector=(1, -1, 1), cameraUpVector=(0, -1, 0)) 23.4.2015 Page 148 E. Baeck Analysis of Structures - SS 15 Bibliography [1] H.P. Langtangen: A Primer on Scientific Programming with Python Springer-Verlag Berlin Heidelberg, 2009 [2] NumPy Community: NumPy User Guide Release 1.4.1, April 2010 [3] SciPy Community: SciPy Reference Guide Release 0.8.dev, February 2010 [4] ISO/IEC 19501:2005 Information technology – Open Distributed Processing – Unified Modeling Language (UML) Version 1.4.2 [5] D. G. Taggart University of Rhode Island, 2009 [6] O. Lueger Lexikon der Technik, 1904 [7] Java Code Conventions Oracle Inc., Sun Microsystems, Inc., September 12, 1997 149 Index :, 31 Abaqus area, 133 arithmetic mean, 135 as, 17 abaqusMacros, 99 BaseWire, 100 close, 102 ConstrainedSketch, 100 DisplacementBC, 101 Basic, 24 bit’s values, 19 break, 28 bytecode, 5 element type T2D2, 101 elemType, 101 fieldOutputs, 102 frame, 102 generateMesh, 101 getSequenceFromMask, 100 instance, 100 job, 101 material, 100 mesh, 101 model, 99 openOdb, 102 part, 100 regionToolset, 101 rootAssembly, 100 SectionAssignment, 100 seedEdgeByNumber, 101 session, 101 setElementType, 101 StaticStep, 101 step, 102 submit, 101 TrussSection, 100 values, 102 vertices, 101 viewport, 101 visualization, 102 waitForCompletion, 101 C, 5, 19, 23, 24, 27 C#, 5 Camel Case, 15 center of mass, 135 Charles Simonyi, 15 chdir, 99 class AList, 75, 79 Base, 67 Element, 71 Node, 69 Profile, 75 ProfileDB, 88, 89 UProfile, 75, 84 classes, 55 code blocks, 25 complement, 18 ComTypes, 7 constructor, 55 continue, 28 cos, 17 count, 41 datetime, 48 day, 48 def, 31 derivative, 36 destructor, 56 dictionary, 42, 88 append, 41 Aptana, 137 e/E format, 23 150 INDEX except, 81 exception, 84 extend, 41 f format, 23 factorial, 27 factorial(170), 28 factorial(400), 28 file close, 51 open, 50 read, 51 write, 50 Finite Element Model, 95 first moment, 133 float, 24 font size, 147 for, 27 FORTRAN, 5 Fortran, 24 Page 151 MacOS, 138 main axis, 136 Mandelbrot Set, 143 Mandelbrot/Multi Processor Solution, 145 Mandelbrot/Single Processor Solution, 144 map, 42 mathplotlib, 46 microsecond, 48 minute, 48 moment of inertia, 134 month, 48 Monty Python, 5 name, 24 negative number, 18 Newton, 35 Newton’s Algorithm, 39, 62 now, 48 NumPy, 7 OOP, 53, 107 g/G format, 23 global variables, 26 hour, 48 Hungarian Notation, 15 IDE, 137 if, 32 import, 17 indent, 25 index, 41 insert, 41 int, 24 Interactive window, 13 iteration, 36 Java, 5 Java Code Conventions, 141 legend, 147 Lib, 6 Lib/site-packages, 87 LIFO, 42 Linux, 138 list, 40, 41, 51 Logging, 52 long, 24 package, 17, 87 Parameters, 31 Performance-Checker, 49, 58 pi, 17 pop, 41 printf, 23 private, 55, 56 push, 42 PyDev, 137 pylab, 46 PythonWin, 12 raise, 84 random, 46 Random Numbers, 46 readlines, 51 remove, 41 reserved words, 16 return, 31 reverse, 41 SciPy, 9 second, 48 second moment, 134 Setup, 6 23.4.2015 Page 152 sin, 17 site-packages, 6 sort, 41 stack, 42 strings, 40 Sun Microsystems, Inc., 15 The Beatles, 42 timedelta, 48 Tkinter, 75 try, 81 tuple, 31, 40 type, 24 type checking, 35 UML, 53 aggregation, 54 class diagram, 53 composition, 54 inheritance diagram, 54 note diagram, 53 note diagram assignment, 53 version, 6 viewport annotations, 148 while, 27 Windows, 138 year, 48 E. Baeck Analysis of Structures - SS 15