Reading Sample Rheinwerk Computing Python For Engineering and Scientific Computing
Reading Sample Rheinwerk Computing Python For Engineering and Scientific Computing
Reading Sample Rheinwerk Computing Python For Engineering and Scientific Computing
Contents
1 Introduction 17
2 Program Structures 29
7
2559.book Seite 8 Freitag, 16. Februar 2024 7:07 19
Contents
8
2559.book Seite 9 Freitag, 16. Februar 2024 7:07 19
Contents
9
2559.book Seite 10 Freitag, 16. Februar 2024 7:07 19
Contents
10
2559.book Seite 11 Freitag, 16. Februar 2024 7:07 19
Contents
5.14 Project Task: Bending a Beam That Is Fixed at One End ...................................... 265
5.14.1 Second Moment of Area ...................................................................................... 265
5.14.2 Equation of the Bending Line ............................................................................. 267
5.15 Project Task: Reaction Kinetics ........................................................................................ 270
11
2559.book Seite 12 Freitag, 16. Februar 2024 7:07 19
Contents
12
2559.book Seite 13 Freitag, 16. Februar 2024 7:07 19
Contents
7.7 Project Task: Animation of Two Coupled Simple Pendulums ........................... 395
13
2559.book Seite 14 Freitag, 16. Februar 2024 7:07 19
Contents
11.1 Interactions with Command Buttons, Textboxes, and Labels .......................... 466
11.1.1 Labels .......................................................................................................................... 467
11.1.2 Textboxes and Command Buttons ................................................................... 468
11.2 The Layout Manager of Tkinter ....................................................................................... 468
11.2.1 The pack Method .................................................................................................... 470
14
2559.book Seite 15 Freitag, 16. Februar 2024 7:07 19
Contents
Appendices 497
15
2559.book Seite 91 Freitag, 16. Februar 2024 7:07 19
Chapter 3
Numerical Calculations Using NumPy 3
In this chapter, you’ll learn how to perform operations on vectors and
matrices and solve systems of linear equations using NumPy.
The acronym NumPy stands for numeric Python. As this name suggests, this module
provides functions for numerical calculations. Besides the number of functions pro-
vided, the short runtime of the NumPy functions is particularly noteworthy. You
should always import the NumPy module using the import numpy as np import state-
ment. Assigning the np alias has become the accepted convention. NumPy forms the
basis for almost all scientific calculations and is therefore often used in combination
with the Matplotlib and SciPy modules.
np.arange(start,stop,step,dtype=None)
You don’t need to specify the data type, which is determined automatically by NumPy.
As a rule, numbers of the float type are processed. Three different float data types are
possible:
91
2559.book Seite 92 Freitag, 16. Februar 2024 7:07 19
The linspace() function specifies the number of elements (num) instead of the incre-
ment (step). The default value is 50. The general syntax for linspace() is:
01 #01_1dim_array.py
02 import numpy as np
03 x1=list(range(10))
04 x2=np.arange(10)
05 x3=np.arange(1,10,0.5)
06 x4=np.linspace(1,10,10)
07 x5=np.linspace(1,10,10,endpoint=False)
08 print("Python list:",type(x1) ,"\n",x1)
09 print("arange() Increment 1:",type(x2),"\n",x2)
10 print("arange() Increment 0.5:",type(x3),"\n",x3)
11 print("linspace() Increment 1:",type(x4),"\n",x4)
12 print("linspace() Increment 0.9:",type(x5),"\n",x5)
Output
Analysis
Line 03 generates the list x1 from Python function range(10). Line 08 outputs the num-
bers from 0 to 9 for x1. The preset increment is 1.
Line 04 creates an array x2 with NumPy function arange(). Line 09 also outputs the
numbers from 0 to 9 for x2. The increment of 1 is also preset.
92
2559.book Seite 93 Freitag, 16. Februar 2024 7:07 19
Line 05 creates a NumPy array x3 with increment 0.5. The final value is not output (line
10).
In lines 06 and 07, two arrays are created via NumPy function linspace(). If the end-
point=False property is set, then the last element won’t be output, and the increment is
3
0.9 (line 12). The default is endpoint=True.
The NumPy functions arange() and linspace() are of type numpy.ndarray. The nd prefix
stands for multi-dimensional arrays (n-dimensional).
01 #02_runtime_comparison.py
02 import time as t
03 import numpy as np
04 #Python list
05 def version1(n):
06 t1=t.time()
07 x1=list(range(n)) #generate list
08 x2=list(range(n))
09 sum=[]
10 for i in range(n):
11 sum.append(x1[i]+x2[i])
12 return t.time() - t1
13 #NumPy arange()
14 def version2(n):
15 t1=t.time()
16 x1=np.arange(n)
17 x2=np.arange(n)
18 sum=x1+x2
19 return t.time() - t1
20 #NumPy linspace()
21 def version3(n):
22 t1=t.time()
23 x1=np.linspace(0,n,n)
24 x2=np.linspace(0,n,n)
25 sum=x1+x2
26 return t.time() - t1
27
28 nt=1000000
93
2559.book Seite 94 Freitag, 16. Februar 2024 7:07 19
29 runtime1=version1(nt)
30 runtime2=version2(nt)
31 runtime3=version3(nt)
32 factor1=runtime1/runtime2
33 factor2=runtime1/runtime3
34 #Output
35 print("Runtime for Python range()...:",runtime1)
36 print("Runtime for NumPy arange()..:",runtime2)
37 print("Runtime for NumPy linspace():",runtime3)
38 print("arange() is%4d times faster than range()" %factor1)
39 print("linspace() is%4d times faster than range()" %factor2)
Output
Analysis
The NumPy function arange() is about 51 times faster, and the NumPy function lin-
space() is about 71 times faster than the Python list generated by the Python function,
range(). The time measurements are only rough estimates. With each new program
start and with different hardware, the results will turn out differently.
In conclusion, for the numerical analysis of large data sets, you should use NumPy
arrays.
Matrices
Matrices are represented by NumPy arrays.
94
2559.book Seite 95 Freitag, 16. Februar 2024 7:07 19
The array function is a member of the ndarray class, as are the NumPy functions
arange() and linspace(). To test the operations on arrays, it is convenient to automate
the creation of two-dimensional arrays. You can use the NumPy method obj.reshape()
to convert a one-dimensional array into a two-dimensional array. Listing 3.3 creates an
m×n matrix from a one-dimensional array. The program also determines the type (i.e.,
the shape) of the array with the shape property and shows how a matrix is transposed.
01 #03_2dim_array.py
02 import numpy as np
03 m=3 #lines
04 n=4 #columns
05 a=np.arange(m*n).reshape(m,n)
06 b=a.reshape(n*m,)
07 print("Type of the array",a.shape,"\n",a)
08 print("Linearize\n",b)
09 print("Transpose\n",a.T)
Output
95
2559.book Seite 96 Freitag, 16. Februar 2024 7:07 19
[[ 0 4 8]
[ 1 5 9]
[ 2 6 10]
[ 3 7 11]]
Analysis
In lines 03 and 04, you can change the number of lines and columns of the array.
In line 05, the NumPy method reshape(m,n) converts the one-dimensional array into a
two-dimensional array.
In line 06, the a.reshape(n*m,) method linearizes the two-dimensional array a. The
a.reshape(m,n) statement is called a method here because reshape() requires an object
to execute. The object notation a.reshape(m,n) can be translated into everyday lan-
guage using the phrase “create an array object with m lines and n columns from array
object a.”
In line 07, the shape property determines the type of array a.
In line 09, array a is transposed via a.T. You can also transpose an array using the
np.transpose(a) statement.
3.1.3 Slicing
Slicing allows you to read selected portions of elements from a two-dimensional array.
With the general syntax a[start:stop:step,start:stop:step], a subrange of an array a
defined by the parameters start,stop,step is read from the m-th line and the n-th col-
umn. The default value of step is 1. Using a[m,:] you can read the m-th line, and using
a[:,n], you can read the n-th column of the array a. Listing 3.4 shows in a 4×4 matrix
how slicing works for reading columns. The matrix is created using the NumPy method
reshape().
01 #04_slicing.py
02 import numpy as np
03 m=4 #lines
04 n=4 #columns
05 a=np.arange(m*n).reshape(m,n)
06 #Output
07 print(a)
08 print("First column\n",a[:,0])
09 print("Second column\n",a[:,1])
10 print("First line\n", a[0,:])
11 print("Second line\n", a[1,:])
12 print("a[1:3,0:2]\n", a[1:3,0:2])
96
2559.book Seite 97 Freitag, 16. Februar 2024 7:07 19
Output
[ 0 1 2 3]
[ 4 5 6 7]
3
[ 8 9 10 11]
[12 13 14 15]]
First column
[ 0 4 8 12]
Second column
[ 1 5 9 13]
First line
[0 1 2 3]
Second line
[4 5 6 7]
a[1:3,0:2]
[[4 5]
[8 9]]
Analysis
In lines 03 and 04, the number of lines and columns for the matrix created in 05 is
defined. In line 05, the NumPy method reshape(m,n) creates a 4×4 matrix from a
sequence of 16 integers.
In lines 08 to 11, individual columns and lines are read. Note that the count starts at
index 0.
In line 12, a range of the matrix is read.
01 #05_numpy_functions.py
02 import numpy as np
03 #import math
04 x=np.arange(-3,4,1)
05 #y1=math.sin(x)
06 y1=np.sin(x)
07 y2=np.exp(x)
08 y3=np.sinh(x)
09 y4=np.cosh(x)
10 y5=np.hypot(3,4)#diagonal
97
2559.book Seite 98 Freitag, 16. Februar 2024 7:07 19
11 y1,y2,y3,y4=np.round((y1,y2,y3,y4),decimals=3)
12 #Output
13 print("x values:\n",x)
14 print("sin function:\n",y1)
15 print("e-function:\n",y2)
16 print("sinh function:\n",y3)
17 print("cosh function:\n",y4)
18 print("Hypotenuse:",y5)
Output
x values
[-3 -2 -1 0 1 2 3]
sin function:
[-0.141 -0.909 -0.841 0. 0.841 0.909 0.141]
e-function:
[0.05 0.135 0.368 1. 2.718 7.389 20.086]
sinh function
[-10.018 -3.627 -1.175 0. 1.175 3.627 10.018]
cosh function
[10.068 3.762 1.543 1. 1.543 3.762 10.068]
Hypotenuse: 5.0
Analysis
The program calculates value tables for a sin, an e, a sinh, and a cosh function. The value
range is between -3 and +3 (line 04). The increment is 1. The fact that the upper limit for
the x values breaks off at +3, although 4 was specified as the upper limit in the source
code, may be confusing at first. The NumPy documentation provides the explanation:
For both integer and non-integer increments, the interval end of the value range is not
included. Thus, x < 4 is always valid. In exceptional cases, rounding effects may cause
the end of the interval to be included.
NumPy functions can be accessed via the dot operator with the np alias. If the com-
ments in lines 03 and 05 are removed, the following error message appears after the
program start:
y1=math.sin(x)
TypeError: only size-1 arrays can be converted to Python scalars
This message means that value tables for the mathematical functions from the math
module may only be created with loop constructs. For each new calculation of a func-
tion value for math.sin(x), the loop must be run again. If, on the other hand, value
98
2559.book Seite 99 Freitag, 16. Februar 2024 7:07 19
tables are created using the NumPy functions arange() or linspace() and the pre-
defined mathematical functions from the NumPy module, then a for or while loop is
no longer needed. All mathematical NumPy functions return an ndarray. Each discrete
value of the variables (i.e., y1 to y4) can thus be accessed via the index operator. The 3
expenditures on lines 14 to 17 demonstrate this result: For each x argument, the corre-
sponding function value is output.
The statement in line 11 is interesting. At this point, the NumPy function round()
rounds the outputs for all four function values to three digits by passing it a tuple of
four elements. The round() function returns a tuple with four elements as well.
01 #06_numpy_statistics.py
02 import numpy as np
03 lines=5
04 columns=10
05 np.random.seed(1)
06 x=np.random.normal(8,4,size=(lines,columns))
07 mw=np.mean(x)
08 md=np.median(x)
09 v=np.var(x)
10 staw=np.std(x)
11 minimum=np.amin(x)
12 maximum=np.amax(x)
13 min_index=np.where(x==np.amin(x))
14 max_index=np.where(x==np.amax(x))
15 #min_index=np.argmin(x)
16 #max_index=np.argmax(x)
17 #Output
18 print("Random numbers\n",np.round(x,decimals=2),"\n")
19 print("Smallest number...........:",minimum)
20 print("Largest number............:",maximum)
21 print("Index of the smallest number:",min_index)
22 print("Index of the largest number..:",max_index)
23 print("Mean....................:",mw)
99
2559.book Seite 100 Freitag, 16. Februar 2024 7:07 19
24 print("Median..................:",md)
25 print("Variance................:",v)
26 print("Standard deviation......:",staw)
27 print("Type of x:",type(x))
28 print("Type of mw:",type(mw))
Output
Random numbers
[[14.5 5.55 5.89 3.71 11.46 -1.21 14.98 4.96 9.28 7. ]
[13.85 -0.24 6.71 6.46 12.54 3.6 7.31 4.49 8.17 10.33]
[ 3.6 12.58 11.61 10.01 11.6 5.27 7.51 4.26 6.93 10.12]
[ 5.23 6.41 5.25 4.62 5.32 7.95 3.53 8.94 14.64 10.97]
[ 7.23 4.45 5.01 14.77 8.2 5.45 8.76 16.4 8.48 10.47]]
Analysis
In line 06, the NumPy function random.normal(8,4,size=(lines,columns)) generates 50
normally distributed random numbers as a matrix with five lines and ten columns.
This function expects the center of the distribution as a first argument, a rough specifi-
cation for the spread of the random numbers to be generated as a second argument
and a tuple for the number of lines and columns as the third argument.
To ensure that the same random numbers are generated each time the program is
restarted, line 05 contains the random.seed() function. If new random numbers should
also be generated at each new program start, this function must be commented out or
deleted.
Lines 07 to 10 calculate the desired statistical measures: the mean mw, median md, vari-
ance v, and standard deviation staw.
An interesting task is to find the array index for the smallest and the largest random
number in lines 13 and 14. In line 13, the where(x==np.amin(x)) function determines the
position in the array with the smallest random number: [0.5] (output in line 21). The
100
2559.book Seite 101 Freitag, 16. Februar 2024 7:07 19
3.2 Vectors
same applies to the determination of the index of the largest random number. The pro-
gram outputs the index [4.7] for this number in line 22. A check against the random
numbers output in line 18 confirms the results. A simpler way to determine the loca-
tion in the array where the smallest or largest random number is located is to use the 3
functions in lines 15 and 16, which have been commented out.
All calculated statistical measures are of type Float64 (line 28). So, you have double pre-
cision with 52-bit mantissa and 11-bit exponent.
3.2 Vectors
Vectors (Latin vector; English carrier, driver) are physical quantities that, in contrast to
scalar quantities, are characterized by a direction in addition to a magnitude. Examples
of directed magnitudes include velocities, forces, or field strengths. In physics and
mathematics, vectors are graphically represented as arrows, as shown in Figure 3.1.
As shown in Figure 3.1, vectors can be shifted arbitrarily in the plane, provided that
their magnitudes and directions do not change. The same statement is true in three-
dimensional space. The vectors shown have the same x and y components of x = 6 and
y = 4. In mathematics, the formulation (6,4) is common. The angle is about 33.7° in each
case.
101
2559.book Seite 102 Freitag, 16. Februar 2024 7:07 19
If you add up vector F1 = (–6,4), vector F2 = (4,–8), and vector F3 = (4,2), you get the result-
ing vector Fres = (2,–2). In the language of mathematics:
Vectors can be created from tuples or lists using the array function. Listing 3.7 shows
the implementation of a vector addition via tuples.
01 #07_vectoraddition.py
02 import numpy as np
03 F1=-6,4
04 F2=4,-8
05 F3=4,2
06 F1=np.array(F1)
07 F2=np.array(F2)
08 F3=np.array(F3)
09 Fres=F1+F2+F3
10 F_1=np.sqrt(F1[0]**2+F1[1]**2)
11 F_2=np.sqrt(F2[0]**2+F2[1]**2)
12 F_3=np.sqrt(F3[0]**2+F3[1]**2)
13 F_res=np.sqrt(Fres[0]**2+Fres[1]**2)
14 angle=np.arctan(Fres[0]/Fres[1])
15 angle=np.degrees(angle)
16 #Output
17 print("Coordinates of F1:",F1)
18 print("Coordinates of F2:",F2)
102
2559.book Seite 103 Freitag, 16. Februar 2024 7:07 19
3.2 Vectors
19 print("Coordinates of F3:",F3)
20 print("Magnitude of F1 :",F_1)
21 print("Magnitude of F2 :",F_2)
22 print("Magnitude of F3 :",F_3) 3
23 print("Resulting force :",Fres)
24 print("Magnitude of Fres:",F_res)
25 print("Angle of Fres :",angle,"°")
Output
Analysis
In lines 03 to 05, the x-y components of the three forces are passed as tuples to vari-
ables F1 to F3. The statements in lines 06 to 08 each create a one-dimensional NumPy
array from the force components.
In line 09, the vector addition takes place. The forces are added element by element.
The internal processes remain hidden from the user. Internally, the program calculates
Fres[0]=F1[0]+F2[0]+F3[0] and Fres[1]=F1[1]+F2[1]+F3[1]. Line 23 outputs the result.
The program calculates the magnitudes of the three forces using the Pythagorean the-
orem (lines 10 to 12). Line 14 calculates the angle of the resulting force using NumPy
function arctan(Fres[0]/Fres[1]). The degrees(angle) NumPy function in line 15
ensures that the angle is converted to degrees.
The output of the program in lines 17 to 25 can be easily checked using Figure 3.2. The
units have been deliberately omitted.
103
2559.book Seite 104 Freitag, 16. Februar 2024 7:07 19
From this definition, the coordinate form of the scalar product can be derived using the
cosine theorem:
The scalar product is calculated as the sum of the products of force and displacement
components.
In abbreviated notation, the following applies to the definition of the scalar product:
The magnitude of the force is calculated from the square root of the scalar product of
the force vector with itself:
And the magnitude of the displacement is calculated from the square root of the scalar
product of the displacement vector with itself:
For the angle between force vector and path vector, the following applies:
For the specified components of the force and path vectors, a work of 5 Nm is per-
formed. The NumPy function dot(F,s) calculates the scalar product. Listing 3.8 shows
how the mechanical work is calculated from the scalar product of the force and path
vectors.
01 #08_scalarproduct.py
02 import numpy as np
03 F=2,7,-3
04 s=-2,3,4
05 F_B=np.sqrt(np.dot(F,F))
06 s_B=np.sqrt(np.dot(s,s))
07 cos_Fs=np.dot(F,s)/(F_B*s_B)
08 angle=np.degrees(np.arccos(cos_Fs))
09 W=np.dot(F,s)
10 #Output
104
2559.book Seite 105 Freitag, 16. Februar 2024 7:07 19
3.2 Vectors
Output
Analysis
In lines 03 and 04, three force and three path components are passed as tuples to the F
and s variables, respectively. The first element of a tuple contains the x-component, the
second the y-component, and the third the z-component of the force (F) and path (s)
vectors.
Lines 05 and 06 calculate the magnitudes of the vectors with the scalar product of the
NumPy function dot(F,F) and dot(s,s), respectively. The angle between the force vec-
tor and the path vector is also calculated using the dot function (line 07). Line 09 calcu-
lates the mechanical work W with the scalar product W=np.dot(F,s). The program
internally calculates the mechanical work by the element-wise multiplication as
required by the definition of the scalar product: W=F[0]s[0]+F[1]s[1]+F[2]s[2].
Line 14 outputs the mechanical work W performed on a mass point shift in space. The
result of 5 Nm matches the previously determined value.
From this definition, the coordinate form of the cross product can be derived:
105
2559.book Seite 106 Freitag, 16. Februar 2024 7:07 19
Listing 3.9 calculates the torque from the force and the lever vector in three-
dimensional space with the NumPy function cross(F,l).
01 #09_crossproduct.py
02 import numpy as np
03 F=2,7,-3
04 l=-2,3,4
05 F_B=np.sqrt(np.dot(F,F))
06 l_B=np.sqrt(np.dot(l,l))
07 cos_Fl=np.dot(F,l)/(F_B*l_B)
08 angle=np.degrees(np.arccos(cos_Fl))
09 M=np.cross(F,l)
10 M_B=np.sqrt(np.dot(M,M))
11 #Output
12 print("Magnitude of force :",F_B,"N")
13 print("Magnitude of lever arm:",l_B,"m")
14 print("Angle between F and l : ",angle,"°")
15 print("Torque M :",M,"Nm")
16 print("Magnitude of torque :",M_B,"Nm")
Output
Analysis
The force vector F and the vector of the lever arm l are again defined as tuples in lines
03 and 04.
In line 09, the program calculates the cross product using the NumPy function, M=
np.cross(F,l). The result is again a vector [37 -2 20] Nm (output in line 15). The magni-
tude of 42.1 Nm of the torque corresponds to the area of the parallelogram spanned by
the force vector F and the vector of the lever arm l.
106
2559.book Seite 107 Freitag, 16. Februar 2024 7:07 19
3.2 Vectors
Listing 3.10 calculates the volume of a cuboid using the triple product dot(c,
np.cross(a,b)).
01 #10_tripleproduct.py
02 import numpy as np
03 a=2,0,0
04 b=0,3,0
05 c=0,0,4
06 a_B=np.sqrt(np.dot(a,a))
07 b_B=np.sqrt(np.dot(b,b))
08 c_B=np.sqrt(np.dot(c,c))
09 V=np.dot(c,np.cross(a,b))
10 #Output
11 print("Magnitude of a:",a_B)
12 print("Magnitude of b:",b_B)
13 print("Magnitude of c:",c_B)
14 print("Triple product:",V)
Output
Magnitude of a: 2.0
Magnitude of b: 3.0
Magnitude of c: 4.0
Triple product: 24
Analysis
The components of the three vectors a, b, and c were chosen to form a cuboid with the
following sides: a=2, b=3 and c=4.
Line 09 calculates the triple product of NumPy functions dot() and cross(). The dot
function is passed the variable c for the height of the box and the cross(a,b) function
for the calculation of the base area as arguments.
The output in line 14 returns the correct result of 24 space units.
107
2559.book Seite 108 Freitag, 16. Februar 2024 7:07 19
Listing 3.11 calculates the dyadic product for the given matrices.
01 #11_outer.py
02 import numpy as np
03 A=np.array([[1,2,3]])
04 B=np.array([[4],[5],[6]])
05 C=np.outer(A,B)
06 print("Matrix A")
07 print(A)
08 print("Matrix B")
09 print(B)
10 print("Dyadic product")
11 print(C)
Output
Matrix A
[[1 2 3]]
Matrix B
[[4]
[5]
[6]]
Dyadic product
[[ 4 5 6]
[ 8 10 12]
[12 15 18]]
Analysis
In line 03, a row vector A is defined and in line 04, a column vector B is defined. The
dyadic product is calculated by NumPy function outer(A,B) in line 05. The result
matches the manually calculated value.
108
2559.book Seite 109 Freitag, 16. Februar 2024 7:07 19
A simple example will demonstrate the matrix multiplication using a schema (see
Table 3.1). The following two matrices are to be multiplied:
The first matrix is entered in the first and second columns and the third and fourth
rows of a table. The second matrix is entered in the third and fourth columns and in the
first and second rows of the table.
5 6
7 8
1 2 1 · 5 + 2 · 7 = 19 1 · 6 + 2 · 8 = 22
3 4 3 · 5 + 4 · 7 = 43 3 · 6 + 4 · 8 = 50
The first row of the first matrix is multiplied element by element by the first column of
the second matrix. The two products are added up. The second row of the first matrix is
multiplied by the first column of the second matrix. The two products are added up
again. The second column is calculated according to the same schema.
NumPy provides the array([[a11,a12],[a21,a22]]) function for generating the matri-
ces. You can adjust the number of rows and columns as needed.
The easiest way to perform matrix multiplication is to use the infix operator @. Alterna-
tives are matmul(A,B) or multi_dot([A,B,C,...]).
Listing 3.12 shows how you can perform matrix multiplication using the numbers from
our earlier example.
01 #12_mulmatrix1.py
02 import numpy as np
109
2559.book Seite 110 Freitag, 16. Februar 2024 7:07 19
Output
<class 'numpy.ndarray'>
Matrix A
[[1 2]
[3 4]]
Matrix B
[[5 6]
[7 8]]
Product A*B
[[19 22]
[43 50]]
Product B*A
[[23 34]
[31 46]]
Analysis
Lines 03 to 06 define matrices with two rows and two columns each. The values of the
individual coefficients are stored in variables A and B.
Line 07 performs the matrix multiplication C=A@B, while line 08 performs the multipli-
cation with an interchanged order of factors D=B@A.
The product for C is correctly output line 13 and matches the value that was manually
calculated in Table 3.1. The result from line 14, on the other hand, deviates from this.
This result is also correct, as you can easily check by recalculation. (You thus learn from
this that the commutative law does not apply to a matrix product.)
110
2559.book Seite 111 Freitag, 16. Februar 2024 7:07 19
I1 = 7 A 2A R2 = 2 Ω I2 = 1 A
5A 4V 1A
R1 R3
U1 = 5 V U2 = 1 V
1Ω 1Ω
Any passive two-port network can be described in general terms by a linear system of
equations with a matrix of four parameters and the column vectors from voltages or
currents.
For the transverse resistors R1 and R3, the following A parameters can be determined
from the circuit shown in Figure 3.3:
111
2559.book Seite 112 Freitag, 16. Februar 2024 7:07 19
To obtain the system matrix of the entire circuit shown in Figure 3.3, you need to mul-
tiply all three partial matrices with each other.
Listing 3.13 performs the matrix multiplication from the three partial matrices for the
π-substitution circuit. You can of course change the values of the resistors for further
testing.
01 #13_mulmatrix2.py
02 import numpy as np
03 R1=1
04 R2=2
05 R3=1
06 U2=1
07 I2=1
08 A1q=np.array([[1, 0],
09 [1/R1, 1]])
10 Al=np.array([[1, R2],
11 [0, 1]])
12 A2q=np.array([[1, 0],
13 [1/R3, 1]])
14 A=A1q@Al@A2q
15 b=np.array([[U2],[I2]])
16 E=A@b
17 U1,I1=E[0,0],E[1,0]
18 print("Chain shape A\n",A)
19 print("Input variables\n",E)
20 print("Input voltage U1=%3.2f V" %U1)
21 print("Input current I1=%3.2f A" %I1)
Output
Chain shape A
[[3. 2.]
[4. 3.]]
Input variables
[[5.]
[7.]]
Input voltage U1=5.00 V
Input current I1=7.00 A
Analysis
The values for the output voltage U2; the output current I2; and the three resistors R1, R2,
and R3 were taken from the specifications of the circuit shown in Figure 3.3.
112
2559.book Seite 113 Freitag, 16. Februar 2024 7:07 19
In lines 08 to 13, the three partial matrices A1q, Al, and A2q are defined for the transverse
resistances R1 and R3 and the series resistance R2. Line 14 performs the matrix multipli-
cation A=A1q@Al@A2q. Pay attention to the correct sequence of factors. As shown earlier
in Listing 3.12, the commutative law does not apply to matrix multiplication! Changing 3
the order of the partial matrices would also represent a different circuit structure.
Line 15 creates the column vector b=np.array([[U2],[I2]]) for the output variables. In
line 16, system matrix A is multiplied by column vector b. The result of the matrix mul-
tiplication is assigned to column vector E.
The input voltage must be 5 V so that a voltage of U2 = 1 V is present at the output of the
π-substitute circuit. A current of I1 = 7 A must flow at the input of the circuit so that a
current of I2 = 1 A flows at the output. You can check the results using the circuit shown
in Figure 3.3.
For the transverse resistors R1 and R3, the B parameters can be determined from the cir-
cuit shown in as follows:
In general, the B parameters can be determined from the inverse matrix of A. The fol-
lowing applies:
Listing 3.14 calculates the output voltage U2 and output current I2 of a π-substitute cir-
cuit with the B catenary parameters.
01 #14_mulmatrix3.py
02 import numpy as np
03 R1=1
04 R2=2
05 R3=1
06 U1=5
07 I1=7
113
2559.book Seite 114 Freitag, 16. Februar 2024 7:07 19
08 B1q=np.array([[1, 0],
09 [-1/R1, 1]])
10 B2l=np.array([[1, -R2],
11 [0, 1]])
12 B3q=np.array([[1, 0],
13 [-1/R3, 1]])
14 B=B1q@B2l@B3q
15 b=np.array([[U1],[I1]])
16 E=B@b
17 U2,I2=E[0,0],E[1,0]
18 print("Chain shape B\n",B)
19 print("Output variables\n",E)
20 print("Output voltage U2=%3.2fV" %U2)
21 print("Output current I2=%3.2fA" %I2)
Output
Chain shape B
[[ 3. -2.]
[-4. 3.]]
Output variables
[[1.]
[1.]]
Output voltage U2=1.00V
Output current I2=1.00A
Analysis
Basically, the program is structured in the same way as shown in Listing 3.13, except
that the parameters in the secondary diagonal have a negative sign.
The result for the output voltage U2 and the output current I2 matches the values deter-
mined using Kirchhoff’s circuit laws in the circuit shown in .
3.3.2 Usage Example: Calculating the Energy of a Rotating Rigid Body in Space
The next example shows the multiplication of the row vector of an angular velocity
with an inertia tensor I (3×3 matrix) and the column vector of an angular velocity.
For the rotational energy, the following applies:
114
2559.book Seite 115 Freitag, 16. Februar 2024 7:07 19
The superscript T means that the vector of angular velocity must be transposed, that is,
the column vector is converted into a row vector. In component notation, you obtain
the following:
3
The product of the mass m and the matrix with the location coordinates is referred to
as the inertia tensor. If you perform the matrix multiplication, you’ll get the rotational
energy, which is stored in the rotating body.
For a case where mass m with radius x = r rotates around the z-axis in the x-y-plane, the
following applies in a simplified way:
Listing 3.15 calculates the rotational energy of a point mass of mass m = 6 kg rotating in
space around the z-axis with an angular velocity of .
01 #15_mulmatrix4.py
02 import numpy as np
03 x=1 #distance in m
04 y=0
05 z=0
06 wx=0
07 wy=0
08 wz=1 #angular velocity
09 m=6 #mass in kg
10 w_Z=np.array([wx,wy,wz])
11 I=m*np.array([[y**2+z**2, -x*y, -x*z],
12 [-x*y, x**2+z**2, -y*z],
13 [-x*z, -y*z, x**2+y**2]])
14 w_S=np.array([[wx],
15 [wy],
16 [wz]])
17 #Calculation of the rotational energy
18 Erot=0.5*w_Z@I@w_S
19 #Erot=0.5*w_S.T@I@w_S
20 Er=Erot[0]
115
2559.book Seite 116 Freitag, 16. Februar 2024 7:07 19
21 #Output
22 print("Rotational energy: %3.2f joules" %Er)
Output
Analysis
The rotational energy is calculated according to the rule: “row vector multiplied by 3×3
matrix multiplied by column vector.” Following this sequence is mandatory because
the commutative law does not apply with matrices! Line 10 contains the row vector of
angular velocity, lines 11 to 13 contain the 3×3 matrix of the inertia tensor, and lines 14
to 16 contain the column vector of the angular velocity.
The statement in line 18 performs the matrix multiplication and stores the result in the
Erot variable. Alternatively, you can comment out lines 10 and 18 and remove the com-
ment in line 19. In this line, the column vector from line 14 is transposed into a row vec-
tor using the T property.
116
2559.book Seite 117 Freitag, 16. Februar 2024 7:07 19
To determine solution vector x, the inverse matrix A-1 must be formed and multiplied
by the inhomogeneity vector b:
3
Based a simple example, let’s walk you through the solution of a simple system of equa-
tions with three unknowns:
Listing 3.16 solves a linear system of equations for three unknowns using NumPy func-
tion solve(A,b).
01 #16_equation_system.py
02 import numpy as np
03 from numpy.linalg import solve
04 #coefficient matrix
05 A = np.array([[1, 1, 1],
06 [2, -2, 3],
07 [3, -4, 2]])
08 #inhomogeneity vector
09 b = np.array([6, 7, 1])
10 #solution
11 solution=solve(A,b)
12 #Output
13 print("Solution of a linear system of equations")
14 print("Coefficient matrix\n",A)
15 print("Inhomogeneity vector\n",b)
16 print("Solution:\n",solution)
Output
117
2559.book Seite 118 Freitag, 16. Februar 2024 7:07 19
Analysis
Line 03 imports the linalg submodule with the solve function.
In lines 05 to 07, the coefficient matrix A of the equation system is generated as a two-
dimensional NumPy array.
In line 09, the inhomogeneity vector array([6,7,1]) is assigned to variable b.
In line 11, NumPy function solve(A,b) calculates the solution of the linear system of
equations. The solution vector is stored in variable solution.
The solution vector contains floats although the coefficient matrix and the inhomoge-
neity vector consist of integers. If you use
print(type(A[0,0]))
print(type(b[0]))
print(type(solution[0]))
<class 'numpy.int64'>
<class 'numpy.int64'>
<class 'numpy.float64'>
When a mathematical operation on arrays produces floats, then all the integers in the
array are converted to floats. If you declare a single arbitrary integer of an array as a
float (e.g., 2. instead of 2), then all other elements of the array are automatically con-
verted to floats.
Using the network shown in Figure 3.4 as an example, try reading a system of equations
directly from the circuit using mesh analysis.
118
2559.book Seite 119 Freitag, 16. Februar 2024 7:07 19
Z1 Z3
U1 I1 Z2 I2 U2
Z4 Z5
U3 U4
I3 Z7 I4
Z6 Z8
The coefficient matrix is entered in Table 3.2. This table consists of four rows and five
columns. The fifth column is for the vector of source voltages.
I1 I2 I3 I4 U
1 Z1 + Z2 + Z4 –Z2 –Z4 0 U1
3 –Z4 0 Z 4 + Z6 + Z 7 –Z7 U3
The sums of the impedances from the individual meshes are shown along the main
diagonal. The secondary diagonals track the common impedances of two meshes. If
two meshes have no common impedances, a 0 is entered in the table. All coefficients of
the secondary diagonals have a negative sign and are reflected on the main diagonal of
the impedance matrix. As shown in Listing 3.17, the coefficient matrix of rows 1 to 4 and
columns 1 to 4 from Table 3.2 is transferred directly into a NumPy array.
119
2559.book Seite 120 Freitag, 16. Februar 2024 7:07 19
01 #17_mesh4c.py
02 import numpy as np
03 import numpy.linalg
04 U1=230
05 U2=-230
06 U3=230
07 U4=-230
08 Z1=1+2j
09 Z2=2-4j
10 Z3=3+4j
11 Z4=2+5j
12 Z5=1+5j
13 Z6=2+5j
14 Z7=4-5j
15 Z8=1+5j
16 Z=np.array([[Z1+Z2+Z4,-Z2,-Z4, 0],
17 [-Z2,Z2+Z3+Z5, 0,-Z5],
18 [-Z4, 0,Z4+Z6+Z7,-Z7],
19 [0,-Z5,-Z7,Z5+Z7+Z8]])
20 U=np.array([U1,-U2,U3,-U4])
21 current=np.linalg.solve(Z,U) #numpy.ndarray
22 for k, I in enumerate(current,start=1):
23 print("I%d = (%0.2f, %0.2fj)A" %(k,I.real,I.imag))
Output
I1 = (33.16, -52.04j)A
I2 = (19.63, -49.35j)A
I3 = (20.09, -41.98j)A
I4 = (18.09, -51.66j)A
Analysis
Lines 04 to 15 contain the values for the voltages and impedances of the network. The
coefficient matrix Z is defined in lines 16 to 19. The rows and columns of the matrix are
arranged in a NumPy array([[],...,[]]) according to Table 3.2. For the calculation of
the solution vector I, the inhomogeneity vector U must still be defined in line 20. The
solution is calculated using NumPy function linalg.solve(Z,U) in line 21. The solution
vector current contains the four mesh currents I[0], I[1], I[2], and I[3].
The Python function enumerate(current) allows for the output of the individual mesh
currents within a for loop (line 23). Each individual mesh current I is marked with the
index k. With each iteration, the enumerate(current) function returns a tuple contain-
ing the index k and the corresponding element I of the current array.
120
2559.book Seite 121 Freitag, 16. Februar 2024 7:07 19
G G
h h
I G
q ı
1 2
G G
b b
G
ı 4
3
G G
h h
The voltage drops between the nodes are calculated using the node potential method.
You can read the system of equations directly from the circuit and represent it as a
matrix:
121
2559.book Seite 122 Freitag, 16. Februar 2024 7:07 19
The following applies to the conductance of the lightning and down conductors:
We can use Ohm’s law to calculate the currents in the lightning and down conductors
from the potential differences of the node voltages and the conductances of the light-
ning and down conductors.
Listing 3.18 solves the system of equations for the four unknown nodal voltages using
NumPy function U=linalg.solve(G,I).
01 #18_project_lightning_protection.py
02 import numpy as np
03 Iq=1e5 #current of the lightning in A
04 g=10 #conductance for steel S*m/mm^2
05 A=50 #conductor cross section in mm^2
06 l=10 #length in m
07 b=5 #width in m
08 h=3 #height in m
09 Gh=g*A/h #conductance for height in S
10 Gl=g*A/l #conductance for length in S
11 Gb=g*A/b #conductance for width in S
12 G=np.array([[Gb+Gh+Gl, -Gl, -Gb, 0],
13 [-Gl, Gb+Gh+Gl, 0,-Gb],
14 [-Gb, 0, Gb+Gh+Gl,-Gl],
15 [ 0,-Gb,-Gl, Gb+Gh+Gl]])
16 I=np.array([Iq,0,0,0])
17 U=np.linalg.solve(G,I)
18 I10=U[0]*Gh
19 I20=U[1]*Gh
20 I30=U[2]*Gh
21 I40=U[3]*Gh
22 I12=(U[0]-U[1])*Gl
23 I13=(U[0]-U[2])*Gb
24 I34=(U[2]-U[3])*Gl
25 I24=(U[1]-U[3])*Gb
26 print("--Voltage drops of down conductors--")
27 print("Voltage U10: %3.2f V" %U[0])
28 print("Voltage U20: %3.2f V" %U[1])
29 print("Voltage U30: %3.2f V" %U[2])
30 print("Voltage U40: %3.2f V" %U[3])
31 print("--Currents in down conductors--")
32 print("Current I10: %3.2f A" %I10)
33 print("Current I20: %3.2f A" %I20)
34 print("Current I30: %3.2f A" %I30)
122
2559.book Seite 123 Freitag, 16. Februar 2024 7:07 19
Output
Analysis
Line 03 specifies the peak lightning current value of 100,000 A. The cross-section of the
lightning and down conductors is usually 50 mm2 (line 05). Lines 06 to 08 define the
length, width, and height of the building in meters.
In lines 09 to 11, the conductances of the lightning and down conductors are calculated.
The coefficient matrix of the conductances is written in lines 12 to 15. In line 16, the
inhomogeneity vector specifies that lightning strikes node 1. The solution vector for
the voltage drops is calculated in line 17 using NumPy function linalg.solve(G,I) and
assigned to variable U. The calculation of the currents in the down conductors is per-
formed in lines 18 to 21. Lines 22 to 25 calculate the currents in the lightning conductors
from the potential differences.
The outputs in lines 26 to 40 show that very high currents can flow with a maximum
current density of about 1,218 A/mm2. These high current densities are still acceptable
because the current only flows for a few milliseconds.
123
2559.book Seite 124 Freitag, 16. Februar 2024 7:07 19
3.6 Tasks
1. Calculate the volume of a parallelepiped using a determinant and the dot(cross(a,
b),c) function.
2. A catenary circuit is composed of three voltage dividers (longitudinal link R1, cross
link R2). All resistors have the same value of 1Ω. The output voltage is U2 = 1 V. Using
the catenary parameter method, calculate the input voltage U1 and the input current
I1.
3. Calculate the dyadic product for:
4. The expanded coefficient matrix of a linear system of equations is given as the fol-
lowing:
Solve this system of equations using NumPy function solve(). The coefficient
matrix and the inhomogeneity vector should be determined by means of slicing
from the extended coefficient matrix.
5. A linear system of equations with a large number of unknowns (50 to 1000) is to be
solved using NumPy function solve(). Generate the coefficient matrix and the inho-
mogeneity vector using NumPy function random.normal(). Test the limits of solve()
by gradually increasing the number of unknowns.
6. Calculate all the mesh currents for the catenary circuit from Task 2 using the mesh
analysis method. The input voltage is 13 V.
7. Calculate all node voltages for the catenary circuit from Task 2 using the node poten-
tial method. The input current has a value of 8 A.
124