Chapter 1 1.1 Express the following numbers in normalized scientific notation. (a) 0.0001005
(b) 3.14159
(c) 62459100 (d) 602.2×1021
=================================================================== (a)
1.005×10-4
(b)
3.14159×100
(c)
6.24591×107
(d)
6.022×1023
1.2 Carry out the following computations using a calculator or spreadsheet. Express the results to the appropriate number of significant figures. (a)
6.001 × 0.15_
(b)
0.154)6.001 (c)
1.25×10-2 7.61×10-1 + 3.06×10-3
(d) 1.017×105 3.64×107
================================================================== (a)
0.90015 0.90
(b)
38.9675... 39.0
(c)
0.77656 0.777
1.3 Round the following to the place indicated. (a) 1047560 to 1000
(b) 1.047 to 0.1
(c) 7450 to 100
(d) −15.43 to 1
================================================================== (a) 1048000 (b)
1.0
(c)
7400
(d) -15 1.4 Rework problem 1.2b using long division. ==================================================================== 0.154
_38.9675... ) 6.001 -4.620 0.1490 -0.1386 0.01040 -0.00924 0.001160 -0.001078 0.000082 -0.000077 0.000005 and so on
1.5 For the components and sum shown below, calculate the percentage each component is of the total. Express your percentages with appropriate significant figures and ensure they sum to 100%. 375 44 145 14 201 779
=================================================================== 375 44 145 14 201 779
0.481386 0.056483 0.186136 0.017972 0.258023 1.0
48% 6% 18% 2% 26% 100%
0.186... is rounded down because it has the least impact, about 0.1 above 0.5
1.6 Compute the following functions using a calculator or spreadsheet. (b) tan − (c) cos ( 47.9 ) (d) tan −1 ( 25) 4 ==================================================================
(a) sin (1.84 )
(a)
-0.4817...
(b)
-1
(c)
cos(47.9*/180) = 0.6704...
(d)
1.5308...
1.7
Determine the roots of the following quadratic equations. (a) x 2 + 0.20 x + 0.70 = 0
(b) x 2 − 28.4 x + 201.64 = 0
(c) 24 x 2 − 13 x = 0
(d) x 2 + 187 x − 24.0 = 0
=================================================================== (a)
-0.1 j 0.8307...
(b)
14.2, 14.2
(c)
0, 13/24 0.5417
(d)
-187.13, 0.1282...
(two equal roots)
1.8 Write down an approximate equation for the waveform shown in Figure P1.8.
Figure P1.8 ================================================================== T = 15 − 3.5 = 11.5 2 0.546 rad/s T A = 1.5
=
1 1.5 sin ( 0 + ) = 1 = sin −1 0.730 1.5 y = 1.5 sin ( 0.546 t + 0.730 )
1.9 In the U.S., vehicle fuel economy is typically reported in miles per gallon (mpg). In Europe, the common measure is liters per 100 km (L/100km). Americans would know a decent fuel economy to be 40mpg. Help Europeans understand this by converting it to L/100km). ================================================================== 40
mi 1.609 km gal km 1 L 100 L L 17.0 5.88 gal mi 3.785 L L 17.0 km 17.0 100 km 100 km
1.10 Carry out the following unit conversions. (a) 150 kph = ?? mph (a moderate speed on the autobahn) (b) 7ft – 6 inches = ?? m (this is the altitude of Yao Ming – who is he?) (c) 45ºC = ?? ºF (typical summer temperature in the Middle East) (d) 12.4 psi = ?? Pa (typical atmospheric pressure at 1800 m altitude) ================================================================= mi 93mph 1.609 km
(a)
150 kph
(b)
7 ft
(c)
45 C=45 1.8+32=113 C
(d)
12.5 psi
0.3048m 0.0254 m + 6in 2.29 m ft in
6895 Pa 86, 200 Pa psi
1.11 In most countries, other than the U.S., a person's weight is reported in kilograms. Convert your weight (or a weight you would like to have) from pounds to kilograms (or vice versa). Also, in the U.K., weight is often cited in stone. Convert that weight to stone also. =================================================================
200 lb m
0.4536 kg 90.7 kg lb m
200 lb m
stone 14.3stone 14 lb m
1.12 A crude oil supertanker typically has a capacity of 100,000dwt. The shipping unit dwt is called a deadweight ton and corresponds to a metric ton or 1,000kg of capacity. Crude oil typically has a density of about 835kg/m3. Determine the amount of liquid fossil fuels combusted in the U.S. or a country of your choice on a monthly basis in terms of crude oil. Determine also what percentage of the crude oil is imported via ship. How many supertankers must unload at ports per month? ==================================================================== Crude oil consumption in the U.S. 19,000,000 bbl/d World Crude Oil Consumption by Country (Thousand Barrels per Day) (indexmundi.com)
1.9 107
bbl 42 gal 3.785 10−3 m3 31day m3 9.4 107 day bbl gal mo mo
9.4 107
m3 835 kg dwt dwt 7.8 107 3 mo m 1000 kg mo
U.S imports in 2021 per day 7,000,000 bbl/d U.S. Imports of Crude Oil (Thousand Barrels per Day) (eia.gov)
Subtract imports from Canada and Mexico (pipeline) 4,750,000 bbl/d Tanker imports 2,250,000 bbl/d
2.25 106 100% 12% Percentage imported by tanker 19 106 7.8 107
dwt tanker tanker 12% 94 5 mo 1 10 dwt mo
1.13 In most countries of the world, mountain heights are given in meters; whereas, in the U.S., feet are typically used. What are the altitudes, in both feet and meters, of the following well-known peaks in the world? Describe the location of each peak too. Mont Blanc, Denali, Mount Everest, Kilimanjaro, Aconcagua, Mount Fuji. ================================================================== Mont Blanc, French Alps: Denali, USA (Alaska): Mount Everest, Nepal: Kilimanjaro, Tanzania: Aconcagua, Argentina: Mount Fuji, Japan:
4,808 m 6,190 m 8,849 m 5,895 m 6,961 m 3,776 m
15,774 ft 20,310 ft 29,032 ft 19,341 ft 22,838 ft 12,389 ft
1.14 The price of regular, unleaded gasoline varies significantly from location to location. In the U.S, it is given in dollars per gallon. In most other countries, it is in their currency per liter. Pick a location in the U.S. and a European country. Compare current fuel prices using both bases. ==================================================================== U.S., average price per gallon, regular unleaded, 1/27/22: $3.34/gal Norway, 19.62 NOK/L, 1/24/22 Exchange rate, 1/27/22: 1 dollar = 8.98 NOK (Norwegian Krone) $US gal $US 8.98 NOK NOK 0.88 7.90 gal 3.785 L L $US L NOK 3.785 L NOK $US $US Norway: 19.62 74.26 8.27 L gal gal 8.98 NOK gal
US: 3.34
Gasoline is 2-1/2 times more expensive in Norway than the U.S. It is notable that the tax on gasoline in Norway is about 75% of the price.
1.15 For a triangle with sides of length a, b and c, there is an inscribed circle of radius r and a circumscribed circle of radius R. The inscribed circle is the largest circle that fits inside the triangle and touches all three sides. The circumscribed circle is the smallest circle that contains the triangle and touches all three vertices. For a = 10, b = 8, and c = 5, determine r and R. ================================================================== Inscribed circle:
s=
a+b+c 2
s = 11.5
r=
( s − a )( s − b )( s − c )
r 1.72
Circumscribed circle: R=
s ( s − b )( s − c ) (s − a)
R = 13.2
s
1.16 A tank is to be constructed that will hold 500m3 of crude oil when filled. The shape of the tank is to be a right circular cylinder, including base, with a right conical section mounted on top (See Figure P1.16) with height equal to radius for each part. The material and labor costs to construct the cylindrical portion including the base are $300 per m2, and the costs for the conical section are $400 per m2. Find the cost of this tank.
Figure P1.16 Cylindrical tank with conical top. ================================================================== Volume of cylindrical part: Vcyl = r 2 r = r 3
1 1 Volume of conical part: Vcon = r 2 r = r 3 3 3 4 Total volume: Vtot = r 3 = 500 m3 r 4.92 m 3 Area of cylindrical part including the base: Acyl = r 2 + 2 r r 228m2 Area of conical part: Acon = r r 2 + r 2 107.7 m 2 Cost: 228m 2
$300 $400 + 107.7 m 2 2 = $68, 400 + $43, 080 = $111, 480 2 m m
1.17 The sector and segment of a circle are related to various dimensions, shown in Figure P1.17.
Figure P1.17 Given values of and R , s = R d = R cos 2 h = R−d c = 2 R sin 2
Formulas for area are 1 Sector: Asec t = R s 2 1 2 Segment: Aseg = R − sin ( ) 2
(sector is "pie slice") (segment is between c and s curves)
If R = 3.6 and s = 1.78, determine the two areas. ===================================================================
=
s 0.494 rad 28.3 R
1 Asect = R s 3.204 2 1 Aseg = R 2 − sin ( ) 0.129 2
Chapter 2 2.1 Express the following base-10 numbers in binary. (a) 18191
(b) 5389
(c) 66.3125
===================================================================== (a) Power 14 10 9 8 3 2 1 0
2^Power Remainder 16384 1024 512 256 8 4 2 1
18191 1807 783 271 15 7 3 1 0 100011100001111
(b) Power
2^Power
Remainder
12 10 8 3 2 0
4096 1024 256 8 4 1
5389 1293 269 13 5 1 0
1010100001101
(c) Power
2^Power
Remainder
6 1 -2 -4
64 2 0.25 0.0625
66.3125 2.3125 0.3125 0.0625 0
1000010.0101
2.2 Convert the following binary numbers to decimal. (a) 01001101
(b) 1110001101
(c) 1000.111
================================================================== (a) 1×26 = 64 1×23 = 8 1×22 = 4 1×20 = 1 77
(b) 1×29 = 512 1×28 = 256 1×27 = 128 1×23 = 8 1×22 = 4 0 1×2 = 1 909
(c) 1×2 3 = 8 1×2-1 = 0.5 1×2-2 = 0.25 1×2-3 = 0.125 8.875
2.3 Express the following base-10 numbers in base-8, octal. (a) 87
(b) 29
(c) 53
================================================================== (a) 2 1 0
1 2 7
64 16 7
127 base-8
87 23 7 0
(b) 1 0
3 5
29 5 0
24 5
35 base-8
(c) 1 0
6 5
53 5 0
48 5
65 base-8
2.4 Conversions between binary, octal and hexadecimal systems. a) Convert the following binary numbers to octal. 011101000010
110101100001
b) Convert the following binary numbers to hexadecimal. 1001110100001001
1111010110000100
c) Convert the following hexadecimal numbers to binary. ACDC
F2D1
==================================================================== (a) 011 101 000 010 3
5
0
110 101 100 001 6 5 4 1
(b)
(c)
35028
2 65418
1001 1101 0000 1001 B D 0 B
BD0B16
1111 0101 1000 0100 F 5 A 4
F5A416
A C D C 1000 1010 1011 1010
10001010101110102
F 2 D 1 1111 0010 1011 0001
11110010101100012
2.5 Express your age (or another age of your choice over 18) in (a) decimal, (b) binary, (c) octal, and (d) hexadecimal. ==================================================================== Example: 19 yr (a)
1910
(b)
100112
(c)
238
(d)
1316
2.6 Using binary arithmetic, compute the following additions: (a) 101 + 011
(b) 0011 + 1001 + 0101
==================================================================== (a)
(b)
11 101 011 1000
(carry)
111 0011 1001 0101 10001
(carry)
(5 + 3 = 8 decimal)
(3 + 9 + 5 = 17 decimal)
2.7 Using binary subtraction, compute the following subtractions: (a) 1010 − 0101 (b) 11000 − 00111 ================================================================== (a)
1 1 1010 -0101 0101
(borrow) (10-5 = 5 decimal)
(b)
111 11000 -00111 10001
(borrow) (24 - 7 = 17)
2.8 Repeat Prob. 2.7 but perform the subtractions by adding the 2's complement of the subtrahend. Use 8-bit integer representation. ================================================================= (a) 0101
00000101
11111010 + 1
00000111
11111000 + 1
11111001
11111011
00001010 +11111011 00000101
(b) 00111
00011000 +11111001 00010001
2.9 Using binary arithmetic, compute the following product: 111011 x 1011 ================================================================== 111011 × 1011 111011 1110110 00000000 111011000 1010001001
check:
59 × 11 = 649
check:
1+8+128+512 = 649
2.10 Using binary arithmetic, carry out the following long divisions: (a) 101 101101
(b) 11 100100
==================================================================
(a)
1001 101 )101101 -101___ 101 -101 0
check: 9 × 5 = 45
(b) ___1100 11 )100100 - 11 001100 -11 0
check: 12 × 3 = 36
2.11 Employ 2's-complement 8-bit integers to compute the negative of (a) 01011010
(b) 11110000
==================================================================== (a) 01011010
10100101 + 1
10100110
11110000
00001111 + 1
00010000
(b)
2.12 Show the IEEE 745 binary layout for the following decimal quantities. (a) 6.626196 x 10−27 (Planck's constant) (b) 6.02252 x 1023 (Avogadro's number) ==================================================================== (a) Convert mantissa to binary 1
1 •
0
1
0
1
0
0
0
0
0
0
1
0
0
1
1
1
0
0
1
1
0
0
0
0
1
1
0
0
1
1
0
0
1
1
1
0
0
0
1
0
1
1
0
1
0
0
0
1
1
1
0
Normalize binary mantissa by shifting binary point to the left, adding 1 power of 2 to the exponent 1 •
1
0
1
0
1
0
0
0
0
0
0
1
0
0
1
1
1
0
0
1
1
0
0
0
0
1
0
0
0
1
1
0
0
1
1
1
0
0
0
1
0
1
1
0
1
0
0
0
1
1
1
11111100100 + 1
11111100101
1
Convert exponent to 2's complement binary -27
- 00000011011
Convert to biased exponent 11111100101 + 01111111111 = 00001100100
Add 1 from normalizing mantissa 01100100 + 1 = 01100101
Combine sign bit (0) with exponent and mantissa for complete binary layout Note: mantissa field discards the one to the left of the binary point. 0
0
1
0
1
0
1
1
0
0
0
1
0
0
0
1
1
0
0
0
0
0
0
0
0
1
0
0
1
1
1
0
0
1
1
0
0
0
0
1
1
0
0
1
1
1
0
0
0
1
0
1
1
0
1
0
0
0
1
1
1
1
0
1
(b) Convert mantissa to binary 1
1 •
0
0
0
0
0
0
1
0
1
1
1
0
0
0
0
1
1
1
1
0
1
1
1
1
0
1
1
0
0
1
1
1
1
0
0
0
0
0
0
1
1
0
0
0
0
0
1
1
1
1
1
Normalize binary mantissa by shifting binary point to the left, adding 1 power of 2 to the exponent 1 •
1
0
0
0
0
0
0
1
0
1
1
1
0
0
0
0
1
1
1
1
0
1
1
1
1
0
1
1
0
0
1
1
1
1
0
0
0
0
0
0
1
1
0
0
0
0
0
1
1
1
1
1
Convert exponent to binary 23
000000010111
Convert to biased exponent 000000010111 + 01111111111 = 10000010110
Add 1 from normalizing mantissa 10000010110 + 1 = 10000010111
Combine sign bit (0) with exponent and mantissa for complete binary layout Note: mantissa field discards the one to the left of the binary point. 0
1
1
0
0
0
0
1
1
1
0
0
0
0
0
0
0
1
0
0
0
1
0
1
1
1
0
0
0
0
1
1
1
1
0
1
1
1
1
0
1
1
1
1
0
0
0
0
0
0
1
1
0
0
0
0
0
1
1
1
1
1
1
1
2.13 Compute the number of bytes and bits for the hard disk storage of your laptop computer. ==================================================================== 952 Gb = 952 × 230 1.022×1012 bytes 8.177×1012 bits
2.14 Early in school, you undoubtedly learned about the primary colors: red, blue, and yellow and that there were three secondary colors, orange, green, and violet that are created by the addition of pairs of the primary colors (orange = red + yellow). Computers represent colors differently as they deal with the mixture of light. Thus, the three primary colors for computers, televisions, and electronics are red, green, and blue. This is called the RGB color model standing for red, green, and blue. As depicted in Figure P2.14, the secondary colors are yellow, magenta, and cyan. The RGB model is an additive model, meaning that colors are created through light waves that are added together in particular combinations in order to produce colors. Table P2.14 The RGB color model indicting the mixtures of the primary colors that yield the secondary colors.
R G B
R Red
G Yellow Green
B Magenta Cyan Blue
As with everything within the computer, colors are represented by a code. The color code for RGB uses a six-digit combination of hexadecimal numbers and letters preceded by a
hash mark. Each pair of hexadecimal digits represents the mix of red, green and blue (RGB) with the magnitude of each pair indicating the intensity of each color. For example, pure blue would be represented by #0000FF. Because we are “decimal” animals, the hexadecimal code is often expressed as a decimal RGB code for better human understanding and communication. Because FFh corresponds to 25510, the code for blue could be expressed in base-10 as #0000FF RGB(0, 0, 255)
(a) Convert the code for pink (#FFC0CB) to the RGB code. (b) To convince yourself of the usefulness of hexadecimal, convert the code for pink to the equivalent binary code. Convert the following Hex codes to RGB codes and guess what colors they represent: (c) #000000 (d) #FFFFFF (e) #00FFFF (f) #A020F0 (hint: Elvis and Prince loved this color) If you would like to learn more about the fascinating topic of color mixing, check out https://en.wikipedia.org/wiki/Color_mixing. =====================================================================
(a) Convert the code for pink (#FFC0CB) to the RGB code. FFh = 15×161 + 15×160 = 240 + 15 = 255 C0h = 12×161 + 0×160 = 192 + 0 = 192 CBh = 12×161 + 11×160 = 192 + 11 = 203 #FFC0CB = RGB(255, 192, 203)
(b) To convince yourself of the usefulness of hexadecimal, convert the code for pink to the equivalent binary code. 255 divide by 2, 127, remainder = 1 127 divide by 2, 63, remainder = 1 63 divide by 2, 31, remainder = 1 31 divide by 2, 15, remainder = 1 15 divide by 2, 7, remainder = 1 7 divide by 2, 3, remainder = 1 3 divide by 2, 1, remainder = 1 1 divide by 2, 0, remainder = 1
192 divide by 2, 96, remainder = 0 96 divide by 2, 48, remainder = 0 48 divide by 2, 24, remainder = 0 24 divide by 2, 12, remainder = 0 12 divide by 2, 6, remainder = 0 6 divide by 2, 3, remainder = 0 3 divide by 2, 1, remainder = 1 1 divide by 2, 0, remainder = 1
203 divide by 2, 101, remainder = 1 101 divide by 2, 50, remainder = 1 50 divide by 2, 25, remainder = 0 25 divide by 2, 12, remainder = 1 12 divide by 2, 6, remainder = 0 6 divide by 2, 3, remainder = 0 3 divide by 2, 1, remainder = 1 1 divide by 2, 0, remainder = 1
Reverse to yield: #FFC0CB = (11111111)2, (11000000)2, (11001011)2
Convert the following Hex codes to RGB codes and guess what colors they represent: (c) #000000 = RGB(0,0,0) Answer: Black (The absence of light) (d) #FFFFFF = RGB(255,255,255) Answer: White Reasoning: The sum of all wavelengths of light (e) #00FFFF = RGB(0,255,255) Answer: Cyan Reasoning: The sum of green and blue light (f) #A020F0 = RGB(160, 32, 240) Answer: Purple Reasoning: Mostly blue and significant red with a small amount of green. Together with the hint, it must be purple. Explanation of hints: Elvis had an awesome 1956 purple Cadillac as well as purple jumpsuits, purple rooms in Graceland, etc., etc., etc. Prince’s greatest CD (in our estimation) was Purple Rain (with Sign o’ the Times a close second). According to his oldest sibling (Sharon Nelson): “Prince … especially loved the color purple because it represented royalty.” She also said “the color purple always made him feel Princely.”
Chapter 3 3.1 Implement the following arithmetic expressions in the IPython Console and report the numerical results. 12 −
(a)
5 2
22 − 7
3 15 − 11 (b) 112 3 1 (c) 1 3 0.6 + − 20 11 0.2
=================================================================== (a) import math (12-5/2)/abs(22/7-math.pi) 7512.914696311852
(b) math.pi*11**2*((3*15-11)/3) 4308.170725622804
(c) 1/(1/20+3/11-0.6/0.2) -0.3735144312393889
3.2 Determine the Great Circle distance in kilometers between the following airport locations. (a) Reykjavik, Iceland – Keflavik Airport Los Angeles – LAX Airport (b) Dubai, UAE – International Airport Buenos Aires, Argentina – Ministro Pistarini International Airport (c) London, England – Gatwick Airport Belfast, N. Ireland - George Best City Airport ===================================================================
(a) Keflavik Airport Latitude: 63° 59' 06" N Longitude: 22° 36' 20" W LAX Airport Latitude: 33° 56' 33" N Longitude: 118° 24' 29" W import math r = 6371 # km # specify latitude and longitude of two locations # Reykjavik Keflavik Airport lt1d = 63 + 59/60 + 6/60/60 # degrees lt1 = math.radians(lt1d) ln1d = 22 + 36/60 + 20/60/60 # degrees ln1 = math.radians(ln1d) # Los Angeles LAX Airport lt2d = 33 + 56/60 + 33/60/60 # degrees lt2 = math.radians(lt2d) ln2d = 118 + 24/60 + 29/60/60 # degrees ln2 = math.radians(ln2d) # compute Great Circle distance d1 = math.sin((lt1-lt2)/2)**2 d2 = math.cos(lt1)*math.cos(lt2)*(math.sin((ln1-ln2)/2))**2 d = 2*math.asin(math.sqrt(d1+d2))*r # display result in the Console print('Distance = {0:6.1f} km'.format(d)) Distance = 6926.3 km
(b) Dubai, UAE – International Airport Latitude: 25° 15' 10" N Longitude: 55° 21' 52" E Buenos Aires, Argentina – Ministro Pistarini International Airport Latitude: 34° 49' 20" S Longitude: 58° 32' 09" W import math r = 6371 # km # specify latitude and longitude of two locations # Dubai International Airport lt1d = 25 + 15/60 + 10/60/60 # degrees lt1 = math.radians(lt1d) ln1d = -(55 + 21/60 + 52/60/60) # degrees
ln1 = math.radians(ln1d) # Ministro Pistarini International Airport lt2d = -(34 + 49/60 + 20/60/60) # degrees lt2 = math.radians(lt2d) ln2d = (58 + 32/60 + 9/60/60) # degrees ln2 = math.radians(ln2d) # compute Great Circle distance d1 = math.sin((lt1-lt2)/2)**2 d2 = math.cos(lt1)*math.cos(lt2)*(math.sin((ln1-ln2)/2))**2 d = 2*math.asin(math.sqrt(d1+d2))*r # display result in the Console print('Distance = {0:6.1f} km'.format(d)) Distance = 13675.3 km
(c) London, England – Gatwick Airport Latitude: 51° 08' 53" N Longitude: 0° 11' 25" W Belfast, N. Ireland - George Best City Airport Latitude: 54° 37' 05" N Longitude: 5° 52' 21" W import math r = 6371 # km # specify latitude and longitude of two locations # London Gatwick Airport lt1d = 51 + 8/60 + 53/60/60 # degrees lt1 = math.radians(lt1d) ln1d = 0 + 11/60 + 25/60/60 # degrees ln1 = math.radians(ln1d) # Belfast George Best City Airport lt2d = 54 + 37/60 + 5/60/60 # degrees lt2 = math.radians(lt2d) ln2d =5 + 52/60 + 21/60/60 # degrees ln2 = math.radians(ln2d) # compute Great Circle distance d1 = math.sin((lt1-lt2)/2)**2 d2 = math.cos(lt1)*math.cos(lt2)*(math.sin((ln1-ln2)/2))**2 d = 2*math.asin(math.sqrt(d1+d2))*r # display result in the Console print('Distance = {0:6.1f} km'.format(d)) Distance =
542.1 km
3.3 The sum-of-angles trigonometric identity for tangents is given as
tan ( + ) =
tan ( ) + tan
1 − tan ( ) tan ( )
Verify this identity with computations in the IPython Console for = 37o and = 107o. ================================================================== import math ad = 37 a = math.radians(ad) bd = 107 b = math.radians(bd) ans1 = math.tan(a+b) print('left side = ',ans1) ans2 = (math.tan(a)+math.tan(b))/(1-math.tan(a)*math.tan(b)) print('right side = ',ans2) left side = right side =
-0.726542528005361 -0.726542528005361
3.4 As shown in Figure P3.4, a lune is a three-dimension, pie-like slice of a sphere. The volume of a lune is given by 2 r 2 where r is the radius of the sphere, and is the included angle. Using Python, compute the volume of a lune of the Earth in cubic kilometers from the longitude of Istanbul, Turkey, to Madrid, Spain.
Figure P3.4 Depiction of a lune.
==================================================================== Longitude Istanbul 28° 57' 18" E Madrid 3° 43' W import math istangd = -(28 + 57/60 + 18/60/60) istang = math.radians(istangd) madangd = 3 + 43/60 madang = math.radians(madangd) ang = madang - istang r = 6137 # km lunevol = 2 * r**2 * ang print('volume of lune = {0:8.3e} km3'.format(lunevol)) volume of lune = 4.295e+07 km3
3.5 Find the worldwide annual production of methanol in million metric tons. Obtain the density of methanol at room temperature and compute the corresponding volume of methanol in cubic meters. Determine the volume of a typical tanker truck used for transporting liquid chemicals. Compute the number of tanker trucks required to contain this annual production. Estimate the length of the tanker truck including its tractor. If the computed number of tanker trucks were lined up one after another, how long would the line be? How does that compare to the circumference of the Earth at the Equator? =================================================================== Basic data: Worldwide annual production: 110×106 metric tons Density of methanol at room temperature: 793 kg/m3 Volume of tanker truck: 7,000 gallons Length of tanker truck including tractor: 17 m Radius of the earth: 6371 km import math prodmasst = 110e6 # metric tons prodmass = prodmasst * 1000 # kg den = 763 # kg/m3 prodvol = prodmass/den # m3 tankvolg = 7000
# gal
tankvol = tankvolg * 3.785e-3
# m3
numtankers = prodvol/tankvol tankerlength = 17 # m tankerslength = numtankers*tankerlength # m print('total length of tankers = {0:8.1e} m'.format(tankerslength)) earthrad = 6371e3 # m earthcirc = 2*math.pi*earthrad tankerearthratio = tankerslength/earthcirc print('ratio of tankers length to earth circumference = {0:5.1f}' \ .format(tankerearthratio)) total length of tankers = 9.3e+07 m ratio of tankers length to earth circumference =
2.3
Tankers stretch around the Earth at the Equation 2.3 times!
3.6 The Colorado River Aqueduct carries about 1.1 million acre-feet of water per year from the Colorado River at the Arizona-California border to the Los Angeles basin. The average daily consumption of water in Los Angeles County is 85 gallons. How many people could be suppled water by the California Aqueduct? =================================================================== Conversion factors: 1 acre = 4046.8m2 gallon = 3.785×10-3m3 foot = 0.3048m 365 days/year m2peracre = 4046.8 # square meters per acre mperft = 0.3048 # meters per foot m3pergal = 3.785e-3 # cubic meters per gallon volyracft = 1.1e6 # annual flow in acre-ft volyrm3 = volyracft*m2peracre*mperft # annual flow in m3 voldaym3 = volyrm3/365 # daily flow in m3 volpersdgal = 85 # per capita consumption in gallons volpersdm3 = volpersdgal*m3pergal # in m3 nopersons = int(voldaym3/volpersdm3) # number of persons served print('no of persons = {0:12d}'.format(nopersons)) no of persons =
11554248
11-1/2 million people served
3.7 Complete using Python the following expressions and explain the results. (a) round(np.pi,8) and round(np.pi,7) (b) 2**3**4 and (2**3)**4 (c) np.arctan(1.5) and np.arctan2(-3.,-2.) (d) 4+-3 ==================================================================== (a) import numpy as np print(np.pi) print(round(np.pi,8)) print(round(np.pi,7)) 3.141592653589793 3.14159265 3.1415927
First, round down to 5, since remaining 358... is less than 5. Second, round down to 7, since remaining 535... is greater than 5. (b) print(2**3**4) print((2**3)**4) 2417851639229258349412352 4096
First evaluation: 3**4 first = 81, then 2**81 = very large number Second evaluation: 2**3 first = 8 then 8**4 = 4096 (c) ang1r = np.arctan(1.5) ang1d = np.degrees(ang1r) ang2r = np.arctan2(-3.,-2.) ang2d = np.degrees(ang2r) print(ang1r,ang1d) print(ang2r,ang2d) 0.982793723247329 56.309932474020215 -2.158798930342464 -123.69006752597979
First is 56° in the first quadrant. Second is -124° or +236° and in the third quadrant.
(d) print(4+-3) 1
Unary − first negates 3, then −3 is added to 4 to yield 1. 3.8 The following data represent the density of water, (kg/m3), at atmospheric pressure for temperatures, T, from 0°C to 100°C. Use Python's pylab module to create a neatly labeled plot of the data and comment on any characteristics you observe. T (°C) 0 2 4 6 10 15
ρ 3
(kg/m ) 999.87 999.97 1000 999.97 999.73 999.13
T (°C) 20 25 30 35 40 45
ρ 3
(kg/m ) 998.23 997.08 995.68 994.06 992.25 990.25
T (°C) 50 55 60 65 70 75
ρ 3
(kg/m ) 988.07 985.73 983.24 980.59 977.81 974.89
T (°C) 80 85 90 95 100
ρ (kg/m3) 971.83 968.65 965.34 961.92 958.38
==================================================================== import numpy as np import pylab as py tmp,den = np.loadtxt('H2ODensityVsTemperature.csv',delimiter=','\ ,unpack=True) py.scatter(tmp,den,c='k') py.grid() py.xlabel('Temperature - degC') py.ylabel('Density - kg/m3') py.title('Density of Water vs. Temperature')
Density goes through a maximum at 4 °C. Shows downward curvature with temperature. 3.9 Download, as a .csv file, the history of global average temperature deviation from the 20th Century average from NOAA’s National Centers for Environmental information (NCEI)Climate at a Glance website. Write a Python script to import these data and create a neatly labeled plot. ================================================================== import numpy as np import pylab as py yr,deg = np.loadtxt('GlobalTemperatureAnomalyData.csv',delimiter=','\ ,unpack=True) py.plot(yr,deg,c='k') py.grid() py.xlabel('Year') py.ylabel('Temperature Anomaly - degC') py.title('Global Temperature Anomaly History')
3.10 Download, as a .txt file, the history of atmospheric global CO2 concentration from https://gml.noaa.gov/ccgg/trends/data.html. Write a Python script to import these data and create a neatly labeled plot. If you also solved Prob. 3.9, plot the two datasets on the same graph with a legend. To do this, you will have to "trim" the temperature dataset so the years match those of the CO2 history. Also, scale down the CO2 data by a factor of 1000. =================================================================== import numpy as np import pylab as py yr,ppm = np.loadtxt('GlobalCO2Data.txt',unpack=True) py.plot(yr,ppm,c='k') py.grid() py.xlabel('Year') py.ylabel('Global CO2 Concentration - ppm') py.title('Global Atmospheric CO2 History')
yr1,deg = np.loadtxt('GlobalTemperatureAnomalyDataTrimmed.csv',delimiter=','\ ,unpack=True) py.figure() py.plot(yr,ppm/1000,c='k',label='CO2 - ppm/1000') py.plot(yr1,deg,c='k',ls='--',label='Temp Anomaly - degC') py.grid() py.xlabel('Year') py.ylabel('Global CO2 Concentration and Temperature Anomaly') py.legend()
3.11 Write a Python script that computes the volume of liquid in a horizontal, cylindrical tank. See Figure P3.11. Specify a tank diameter of 2 meters and length of 4 meters. Allow the user to input the liquid depth at the centerline of the tank. Produce output in the Console displaying the liquid volume in liters. The formula to be used is
r −h 2 V = r 2 cos −1 − (r − h) 2 r h − h L r where V = volume of liquid, r = tank radius = diameter/2, L = length of the tank, h = liquid depth. All dimensions used in the formula must be consistent.
Figure P3.11 Horizontal, cylindrical tank containing a liquid. ==================================================================== import numpy as np d = 2 # m r = d/2 L = 4 # m h = float(input('Enter liquid depth in meters: ')) V = (r**2 * np.cos((r-h)/r) - (r-h)*np.sqrt(2*r*h-h**2)) * L print('Volume = {0:6.2f} m3'.format(V)) Enter liquid depth in meters: 1.2 Volume = 4.70 m3 Enter liquid depth in meters: 0.4 Volume = 1.38 m3
3.12 Newton's Universal Law of Gravitation is given by
F=
G m1 m2 d2
where F = gravitational force (N), m1 and m2 = the two involved masses (kg), d = distance between the centers of the two masses, and G = the universal gravitation constant (6.673×10-11N·m2/kg2). Determine the gravitational force for the following two cases: (a)
A person with mass 80kg standing on the surface of the Earth. The radius of the Earth is 6371km, and its mass is estimated as 5.98×1024kg.
(b)
The mass of the planet Jupiter is estimated as 1.898×1027kg. The mass of Jupiter's moon Europa is 4.8×1022kg. The orbital radius of Europa is 670,900km.
===================================================================== (a) G = 6.673e-11
# N*m2/kg2
m1 = 80 # kg m2 = 5.98e24 # kg d = 6371e3 # m F = G*m1*m2/d**2 print('Gravitational force = {0:8.4g} N'.format(F)) Gravitational force =
786.5 N
(b) G = 6.673e-11
# N*m2/kg2
m1 = 1.898e27 # kg m2 = 4.8e22 # kg d = 670900*1000 # m F = G*m1*m2/d**2 print('Gravitational force = {0:8.4g} N'.format(F)) Gravitational force = 1.351e+22 N
3.13 When counting hours on a twelve-hour clock, you count up to 12 and then wrap back to 1. For example, say you want to determine what time it would be 10 hours after 9:00 a.m. On a twelve-hour clock, you cannot simply add 10 to 9 because you would get 19. Complete the last two statements of the following code fragment to compute the time for a 24-hour clock (t24) and use the modulus operator to compute the 12-hour clock time (t12): tinit = 9 deltat=10 t24 = t12 =
==================================================================== t24 = tinit + deltat t12 = t24 % 12
Chapter 4 4.1 Write a Python script for the flowchart depicted in Figure P4.1.
Figure P4.1 Flowchart ==================================================================== import numpy as np if a > 0 : th = np.arctan(b/a) elif b > 0 : th = np.arctan(-b/a) + np.pi else : th = np.arctan(b/a) + np.pi
4.2 Consider a vertical, cylindrical bin with conical base partially filled with grain. Refer to Figure P4.2. Develop and test a Python function named binvol that has arguments of the bin cylindrical radius, r, the heights of the cylindrical, hcyl, and conical, hcon, sections, and the overall depth of the grain in the bin, h. The function should return the volume of grain, gvol. It should also check that the grain depth is feasible – not greater than the sum of the conical and cylindrical heights.
Figure P4.2 Grain bin ==================================================================== Case 1: Level in cylindrical part import numpy as np def binvol(r,hcyl,hcon,h): if h > hcyl + hcon: return 'level is greater than height of bin' if h > hcon: gvol = np.pi*r**2*hcon/3 + np.pi*r**2*h else: ratio = r/hcon rcon = h*ratio gvol = np.pi*rcon**2*h/3 return gvol r = 2 hcyl = 5 hcon = 3 h = 5 grain_volume = binvol(r,hcyl,hcon,h) print('grain volume = {0:7.2f}'.format(grain_volume)) grain volume =
75.40
Case 2: Level in conical part r = 2 hcyl = 5 hcon = 3 h = 2.5 grain_volume = binvol(r,hcyl,hcon,h) print('grain volume = {0:7.2f}'.format(grain_volume))
grain volume =
7.27
Case 3: Level above bin r = 2 hcyl = 5 hcon = 3 h = 9 grain_volume = binvol(r,hcyl,hcon,h) print(grain_volume) level is greater than height of bin
4.3 Approximate models for the variation of temperature and pressure versus altitude in the Earth's atmosphere are available.1 They provide formulas for three atmospheric layers: the troposphere, lower stratosphere, and upper stratosphere. troposphere (h < 11000): 5.256
T + 273.15 P = 101.29 288.08
T = 15.04 − 0.00649h
lower stratosphere (11000 h < 25000): P = 22.65 e1.73−0.000157 h
T = −56.46
upper stratosphere (25000 h):
T + 273.15 T = −131.21 + 0.00299 h P = 2.488 216.6 In the formulas above, the specific units required are h : meters (m)
T: °C
−11.388
P: kilopascals (kPa)
and the density of air is related to temperature and pressure by
=
P 0.2869 (T + 273.15)
: kg/m3
(a) Develop a Python function, atm, that has a single argument, h, and returns the temperature, T, pressure, P, and density, rho. Test this function for different h values.
1
https://www.grc.nasa.gov/www/k-12/airplane/atmosmet.html
(b) Use your function from to produce a plot of density versus altitude from 0 to 25000 meters. Altitude should be the vertical axis. =================================================================== (a) import numpy as np def atm(h): if h < 11000: T = 15.04 - 0.00649 * h P = 101.29 * ((T+273.15)/288.08)**5.256 elif h < 25000: T = -56.46 P = 22.65 * np.exp(1.73-0.000157*h) else: T = -131.21 + 0.00299*h P = 2.488 * ((T+273.15)/216.6)**(-11.388) rho = P / 0.2869 / (T+273.15) return T,P,rho h1 = 5000 h2 = 15000 h3 = 30000 T,P,rho = atm(h1) print('Altitude = {0:7.1f} m'.format(h1)) print('Temperature = {0:5.1f} degC'.format(T)) print('Pressure = {0:5.2f} kPa'.format(P)) print('Density = {0:6.4f} kg/m3'.format(rho)) T,P,rho = atm(h2) print('\nAltitude = {0:7.1f} m'.format(h2)) print('Temperature = {0:5.1f} degC'.format(T)) print('Pressure = {0:5.2f} kPa'.format(P)) print('Density = {0:6.4f} kg/m3'.format(rho)) T,P,rho = atm(h3) print('\nAltitude = {0:7.1f} m'.format(h3)) print('Temperature = {0:5.1f} degC'.format(T)) print('Pressure = {0:5.2f} kPa'.format(P)) print('Density = {0:6.4f} kg/m3'.format(rho)) Altitude = 5000.0 m Temperature = -17.4 degC Pressure = 54.17 kPa Density = 0.7383 kg/m3 Altitude = 15000.0 m Temperature = -56.5 degC
Pressure = 12.12 kPa Density = 0.1950 kg/m3 Altitude = 30000.0 m Temperature = -41.5 degC Pressure = 1.16 kPa Density = 0.0174 kg/m3
(b) import pylab as py h = np.linspace(0,40000,200) n = len(h) den = np.zeros(n) for i in range(n): T,P,rho = atm(h[i]) den[i] = rho py.plot(den,h,c='k') py.grid() py.ylabel('Altitude - m') py.xlabel('Density - kg/m3') py.title('Altitude vs. Atmospheric Density')
4.4 Scientists and engineers frequently have to extract numerical information from tables of data. For example, it is well known that the density of water, (kg/m3), changes with temperature, T (oC). This relationship can be represented by the data in Table P4.4. Develop and test a Python function, H2Oden, with a single argument of temperature, T, that returns an estimate of the density, rho, using the method of linear interpolation. For this method, the function finds the pair of table entries that bracket the input temperature, denoted 1 and 2, and then uses a straight line to estimate the corresponding density, as in
=
T − T1 ( 2 − 1 ) + 1 T2 − T1
Table P4.4 Density of water versus temperature T T T T (°C) (kg/m3) (°C) (kg/m3) (°C) (kg/m3) (°C) (kg/m3) 0 999.87 20 998.23 50 988.07 80 971.83 2 999.97 25 997.08 55 985.73 85 968.65 4 1000 30 995.68 60 983.24 90 965.34 6 999.97 35 994.06 65 980.59 95 961.92 10 999.73 40 992.25 70 977.81 100 958.38 15 999.13 45 990.25 75 974.89 =================================================================== import numpy as np Ttbl,rhotbl = np.loadtxt('H2ODensityVsTemperature.csv',delimiter=',' \ ,unpack=True) n = len(Ttbl) def H2Oden(T): if T < Ttbl[0] or T > Ttbl[n-1]: return 'temperature out of range' if T == Ttbl[n-1]: rho = rhotbl[n-1] return rho for i in range(n): if T <= Ttbl[i]: ilow = i break T1 = Ttbl[ilow] T2 = Ttbl[ilow+1] rho1 = rhotbl[ilow] rho2 = rhotbl[ilow+1] rho = (T-T1)/(T2-T1)*(rho2-rho1)+rho1 return rho
T = 58 print(T,H2Oden(T)) T = 100 print(T,H2Oden(T)) T = -5 print(T,H2Oden(T)) T = 105 print(T,H2Oden(T)) 58 984.3 100 958.38 -5 temperature out of range 105 temperature out of range
4.5 What is wrong with the following Python code segment? Propose a corrected version. if x <= 0.: fval = -x else: if x < 1: fval = x**2 else: fval = 1
==================================================================== The final statement after the else: will not execute if x >= 1. if x <= 0.: fval = -x elif x < 1: fval = x**2 else: fval = 1
4.6 The graph shown in Figure P4.6 describes a functional relationship between an input variable, x, and an output variable, y. Electrical engineers might call this a linear function with saturation. Develop and test a Python function, sat, which accepts an input argument, x, and returns the y value.
Figure P4.6 Saturation function =================================================================== def sat(x): if x <= -1: y = -1 elif x < 1: y = x else: y = 1 return y x = -2 print(x,sat(x)) x = -0.3 print(x,sat(x)) x = 0.7 print(x,sat(x)) x = 1 print(x,sat(x)) x = 2 print(x,sat(x)) -2 -1 -0.3 -0.3 0.7 0.7 1 1 2 1
4.7 An environmental scientist is developing a computer program to determine whether or not it is safe to introduce a weed herbicide into a lake. According to scientific studies, the chemical should only be applied when the water temperature is less than 30°C, the dissolved oxygen concentration is above 6mg/L, and the pH is between 6.8 and 8. Develop a Python
function, weedtest, with arguments temperature, T, dissolved oxygen concentration, Do, and pH, and returns a string, either 'safe' or 'unsafe'. Test your function for various input values. ==================================================================== def weedtest(T,Do,pH): if T >= 30: apply = 'unsafe' elif Do <= 6: apply = 'unsafe' elif pH <= 6.8 or pH >= 8: apply = 'unsafe' else: apply = 'safe' return apply T = 31 ; Do = 7 ; pH = 7 print(T,Do,pH,weedtest(T,Do,pH)) T = 25 ; Do = 7 ; pH = 7 print(T,Do,pH,weedtest(T,Do,pH)) T = 25 ; Do = 5 ; pH = 7 print(T,Do,pH,weedtest(T,Do,pH)) T = 25 ; Do = 7 ; pH = 6.8 print(T,Do,pH,weedtest(T,Do,pH)) T = 25 ; Do = 7 ; pH = 8.5 print(T,Do,pH,weedtest(T,Do,pH)) 31 7 7 unsafe 25 7 7 safe 25 5 7 unsafe 25 7 6.8 unsafe 25 7 8.5 unsafe
4.8 Write and test a Python function, findrx, that has one argument, x, and returns rx. The value of x is required to be positive or zero, so the function should check for this and return an error message is x is negative. If the value of x is zero, the function simply returns a value of zero. For positive x values, the function implements the flowchart shown in Figure P4.8. Test your function with various values of x. What calculation does your function perform?
Figure P4.8 =================================================================== def findrx(x): xold = x while True: xnew = (xold + x/xold) / 2 if abs((xnew-xold)/xnew) < 1.e-7: break xold = xnew return xnew x = 2 print(x,findrx(x)) x = 0.5 print(x,findrx(x)) 2 1.414213562373095 0.5 0.7071067811865475
Compute the square root. Will fail for x = 0 or x < 0. Could modify code to trap those conditions.
4.9 The famous Fibonacci series starts with the sequence 0 and 1. Each subsequent number in the series is the sum of the two previous numbers. Applying that to the first two numbers, the series evolves with the numbers 0, 1, 1, 2, 3, 5, … Develop a Python function, fibo, that has an integer input argument, n. The function computes n terms of the Fibonacci series (n must be greater than 2) and returns the ratio of the second last term to the last term.
Experiment with the function to see whether the ratio converges for larger values of n. Compare the ratio to the Golden Ratio, given by 5 −1 2
As a supplement, do some reading on the Fibonacci series, its evidence in nature, and its relationship to the Golden Ratio. =================================================================== def fibo(n): if n == 0: return 0 elif n == 1: return 1 else: fval = [0,1] for i in range(2,n+1): fval.append(fval[i-1]+fval[i-2]) return fval[n-1]/fval[n] import numpy as np print(fibo(10)) print((np.sqrt(5)-1)/2) 0.6181818181818182 0.6180339887498949
Fibonacci Ratio close to Golden Ratio print(fibo(50)) print((np.sqrt(5)-1)/2) 0.6180339887498949 0.6180339887498949
Fibonacci Ratio identical to Golden Ratio 4.10 Develop a Python function, sumn, that has an integer input argument, n, and returns the sum of the integers from 1 to n. Test your function and compare the result to the shortcut formula n ( n + 1) 2
================================================================= def sumn(n): sumval = 0 for i in range(1,n+1): sumval = sumval + i return sumval n = 10 print(sumn(n)) print(n*(n+1)/2) 55 55.0
4.11 The American Association of State Highway and Transportation Officials provides the criteria in Table P4.11 for classifying soils in accordance with their suitability for use in highway subgrades and embankment construction. Develop a Python function, soilclass, that has average grain size, gsize, as an input argument and returns the classification. Table P4.11 Soil classification Grain size mm size > 75 2< size 75 0.05 <size 0.002 < size size
Classification boulders gravel sand silt clay
=================================================================== def soilclass(gsize): if gsize <= 0.002: return 'clay' elif gsize <= 0.05: return 'silt' elif gsize <= 2: return 'sand' elif gsize <= 75: return 'gravel' else: return 'boulders' gsz = float(input('Enter grain size in mm: ')) print(soilclass(gsz))
Enter grain size in mm: 0.001 clay Enter grain size in mm: 0.033 silt Enter grain size in mm: 1.5 sand Enter grain size in mm: 25 gravel Enter grain size in mm: 145 Boulders
4.12 Write a Python code segment to implement the flowchart shown in Figure P4.12.
Figure P4.12 =================================================================== if i < 10: if i > 5: A = A - 4 else: A = A/4 else: A = 16 if i < 20: A = A - 4
4.13 The ideal gas law is widely used to predict the pressure, temperature, or volume of a gas at conditions that are not extreme, especially not inordinately high pressure. It can be expressed as PVˆ = RT
where P : pressure, Vˆ : specific volume, R : the gas law constant, and T : the absolute temperature. For typical SI units, P [=] kPa, Vˆ [=] m3/kmol, T [=] K, the value of R is R 8.3145 kPa·m3/[kmol·K] Develop a user-defined Python function called idealgas that has input arguments of P, Vˆ , and T. Use keyword arguments. When invoked, only two of the three arguments are provided, and the function returns the third. Test your function for various scenarios. =================================================================== R = 8.3145
# kPa*m3/[kmol*K]
def idealgas(P=0.,V=0.,T=0.): if P == 0 and V != 0 and T != 0: P = R*T/V return P elif V == 0 and P != 0 and T != 0: V = R*T/P return V elif T == 0 and P != 0 and V != 0: T = P*V/R return T P = idealgas(V=22.4,T=293.) print('Pressure = {0:6.1f} kPa'.format(P)) V = idealgas(P=100.,T=293.) print('Molar volume = {0:5.2f} m3/kmol'.format(V)) T = idealgas(P=100., V=22.4) print('Temperature = {0:6.1f} K'.format(T)) Pressure = 108.8 kPa Molar volume = 24.36 m3/kmol Temperature = 269.4 K
4.14 As temperature increases, solid materials expand. This expansion can be quantified by a coefficient with the formula L = T L
where L is the change in length, is the linear expansion coefficient, T is the change in temperature, and L is the length of the material at the base temperature, T. The table below lists the expansion coefficients for several common materials.
Material
Expansion Coefficient 106 m/[m·°C]
Copper Aluminum Glass Steel Polyethylene
16 22.5 5.9 11.5 125
Create a Python function called linexp that has input arguments of material type (text string), temperature change, and base length. The function should return the expanded length. Test your function with various examples. ==================================================================== def linexp(matl,dT,L): if matl == 'Copper': alpha = 16.e-6 elif matl == 'Aluminum': alpha = 22.5e-6 elif matl == 'Glass': alpha = 5.9e-6 elif matl == 'Steel': alpha = 11.5e-6 elif matl == 'Polyethylene': alpha = 125.e-6 else: return 'incorrect material' dL = alpha*dT*L return dL dL = linexp('Copper',100.,1.0) if type(dL) == str: print(dL) else:
print('Expansion = {0:7.4g} m'.format(dL)) dL = linexp('Cpper',100.,1.0) if type(dL) == str: print(dL) else: print('Expansion = {0:7.4g} m'.format(dL)) dL = linexp('Polyethylene',25.,1.0) if type(dL) == str: print(dL) else: print('Expansion = {0:7.4g} m'.format(dL)) Expansion = 0.0016 m incorrect material Expansion = 0.003125 m
4.15 In the U.S., vehicle fuel economy is typically reported in units of miles per gallon (mpg); whereas, in Europe, the quantity used is liters/100km (L/100km) traveled. Create a lambda function that converts mpg to L/100km. If a good value for fuel economy in the U.S. is 40mpg, use your function to compute the value in L/100km. ==================================================================== Lp100km = lambda mpg: 1/mpg * 3.785 / 1.609 * 100 print('Fuel economy = {0:5.2f} L/100km'.format(Lp100km(40))) Fuel economy =
5.88 L/100 km
4.16 At some point, you may have the adventure of purchasing a new or used vehicle (perhaps you have experienced this treat already). It is usually necessary to finance part of the purchase with a loan. Key parameters associated with the loan are its amount or principal, P, the interest rate, i, the term of length of the loan (n = number of monthly payments), and the constant monthly payment, A. These are related by the formula A= P
i (1 + i )
n
(1 + i ) − 1 n
If the interest rate is given in annual terms, and the payments are monthly, the interest rate divided by 12 must be used in the formula. Develop a Python function called loanpmt with the arguments of loan amount, annual interest rate, and number of years of the loan period. The function should return the monthly payment.
Imagine that you can afford a monthly payment of $400, and you can obtain a loan term of six years at 4.8% annual interest rate. If a 20% down payment is required to qualify for the loan, experiment with your function to determine the maximum purchase price you can afford. ==================================================================== def loanpmt(P,iyp,yr): im = iyp/12/100 mo = yr*12 pmt = P * im * (1+im)**mo / ( (1+im)**mo - 1) return pmt Total = 31226 IntRate = 4.8 # % yr = 6 Loan = 0.8*Total Payment = loanpmt(Loan,IntRate,yr) print('Monthly payment = ${0:7.2f}'.format(Payment)) Monthly payment = $ 400.00
4.17 Ohm's Law describes the relationship of electrical current, I, and voltage, V, for a resistance, R. Standard units are amperes (A), volts (V), and ohms (), and the Ohm's Law formula is V = I R
Develop a Python function called ohmslaw with three keyword arguments, I, V, and R. When the function is invoked, two of these three are supplied and the function computes and returns the third. Test your function with several examples. =================================================================== def ohmslaw(I=0.,V=0.,R=0.): if I == 0. and V != 0. and R != 0.: I = V/R return I elif V == 0. and I != 0. and R != 0.: V = I*R return V elif R == 0. and V != 0. and I != 0.: R = V/I return R else: return 'input incorrect'
Vlt = 100. Res = 1000. I = ohmslaw(V=Vlt,R=Res) print('Current = {0:6.2f} A'.format(I)) Cur = 12. Res = 100. V = ohmslaw(I=Cur,R=Res) print('Voltage = {0:6.2f} V'.format(V)) Cur = 0.2 Vlt = 1.5 R = ohmslaw(I=Cur,V=Vlt) print('Resistance = {0:6.2f} ohms'.format(R)) Current = 0.10 A Voltage = 1200.00 V Resistance = 7.50 ohms
Chapter 5 5.1 Develop a Python script using the Matplotlib pyplot module to produce a well-labeled line plot of the function
f ( x ) = x 2 sin ( x ) − 4 over the domain 0 x 4. =================================================================== import numpy as np import matplotlib.pyplot as plt f = lambda x: x**2 * abs(np.sin(x)) - 4 x = np.linspace(0,4,200) plt.plot(x,f(x),c='k') plt.grid() plt.xlabel('x') plt.ylabel('f(x)')
5.2 Table P5.2 presents greenhouse gas emission by the U.S. over the years 1990 through 2019. Write Python scripts using Matplotlib pyplot to generate the following well-labeled plots: a) a scatter plot of total emissions versus year, and b) a line plot of carbon dioxide, methane and nitrous oxide emissions by year using twin axes. Employ different linestyles and a legend. Table P5.2 Greenhouse gas emissions by the U.S. from 1990 to 2019 (MMT CO2 equivalent). Carbon dioxide Methane Nitrous oxide Fluorinated gases Total
1990 5113.5 776.9 452.7 99.7 6442.7
1991 5057.9 781.8 443.2 90.7 6373.6
1992 5167.5 780.5 443.1 95.3 6486.3
1993 5267.3 770.7 471.3 95.0 6604.3
1994 5358.8 777.0 457.0 99.0 6691.8
1995 5421.5 767.7 468.8 117.9 6775.9
1996 5610.6 760.8 480.8 129.0 6981.2
1997 5686.5 746.8 466.8 136.4 7036.5
1998 5731.0 731.7 467.7 152.9 7083.3
1999 5804.7 713.4 457.5 149.9 7125.4
Carbon dioxide Methane Nitrous oxide Fluorinated gases Total
2000 6010.5 707.6 444.7 150.9 7313.6
2001 5904.9 699.8 460.4 137.9 7203.0
2002 5946.8 692.8 458.8 145.9 7244.3
2003 6011.8 692.7 459.8 137.0 7301.4
2004 6114.0 687.0 469.9 144.6 7415.5
2005 6134.5 686.1 455.8 146.6 7423.0
2006 6051.7 690.8 452.5 149.5 7344.5
2007 6131.0 693.9 463.5 161.2 7449.6
2008 5914.1 700.8 446.9 162.8 7224.6
2009 5478.2 689.7 445.6 158.5 6772.0
Carbon dioxide Methane Nitrous oxide Fluorinated gases Total
2010 5675.8 692.1 455.0 168.2 6991.1
2011 5540.2 666.2 445.6 175.5 6827.4
2012 5338.7 658.2 416.8 172.3 6585.9
2013 5474.3 654.4 463.9 172.1 6764.7
2014 5522.8 651.0 474.0 177.1 6825.0
2015 5371.8 651.5 468.2 179.6 6671.1
2016 5248.0 642.4 450.8 179.1 6520.3
2017 5207.8 648.4 446.3 180.9 6483.3
2018 5375.5 655.9 459.2 180.8 6671.4
2019 5255.8 659.7 457.1 185.7 6558.3
https://cfpub.epa.gov/ghgdata/inventoryexplorer/#allsectors/allsectors/allgas/gas/all ==================================================================== (a) import numpy as np import matplotlib.pyplot as plt yr,total,co2,ch4,n2o = np.loadtxt('Problem5-1_EmissionsData.csv' \ ,delimiter=',',unpack=True) plt.figure() plt.scatter(yr,total,c='k') plt.grid() plt.xlabel('Year') plt.ylabel('Total Emissions - MMT CO2 Equiv') plt.title('U.S. Total Greenhouse Gas Emissions')
(b)
Code added:
plt.figure() curve1 = plt.plot(yr,co2,c='k',label='CO2') plt.grid() plt.xlabel('Year') plt.ylabel('CO2 Emissions - MMT') plt.title('U.S. Greenhouse Gas Emissions') plt.twinx() curve2 = plt.plot(yr,ch4,c='k',ls='--',label='CH4') curve3 = plt.plot(yr,n2o,c='k',ls=':',label='N2O') plt.ylabel('CH4 and N2O Emissions -- MMT CO2 Equiv') curves = curve1 + curve2 + curve3 labels = [] for curve in curves: labels.append(curve.get_label()) plt.legend(curves,labels)
5.3 In the production of sugar (sucrose, C12H22O11), the concentration of sucrose solutions is typically measured by refractive index (using an instrument called a refractometer). The data in Table P5.3 present relevant measurements. A relationship between refractive index, n, and sucrose concentration, pctS, has been proposed as
n = 1.32495 + 1.99515 10−3 pctS Create a Python script using the Matplotlib pyplot module that produces a plot of the sucrose concentration data using only markers and adds the model to the plot using lines. Label the plot and add a legend. Table P5.3 Refractive Index of Sucrose Solutions. Percent sucrose 0 5 10 15 20 25
Refractive index 1.3330 1.3403 1.3479 1.3557 1.3639 1.3723
Percent sucrose 30 35 40 45 50 55
Refractive index 1.3811 1.3902 1.3997 1.4096 1.4200 1.4307
Percent sucrose 60 65 70 75 80 85
Refractive index 1.4418 1.4532 1.4651 1.4774 1.4901 1.5033
================================================================== import numpy as np import matplotlib.pyplot as plt pct,ref = np.loadtxt('Sucrose.csv' \ ,delimiter=',',unpack=True) n = lambda p: 1.32495 + 1.99515e-3*p plt.scatter(pct,ref,c='k',label='data') plt.plot(pct,n(pct),c='k',label='equation') plt.grid() plt.xlabel('Percent Sucrose') plt.ylabel('Refractive Index') plt.legend() plt.title('Refractive Index of Sucrose Solutions')
5.4 Create a two-by-two array of line plots in a pyplot figure window for the following hyperbolic functions over the domain −1 x 1. sinh ( x ) vs. x
cosh ( x ) vs. x
tanh ( x ) vs. x
sech ( x ) vs. x
================================================================== import numpy as np import matplotlib.pyplot as plt x = np.linspace(-1,1,100) fig = plt.figure() ax1 = fig.add_subplot(221) ax1.plot(x,np.sinh(x),c='k') ax1.grid() ax1.set_xlabel('x') ax1.set_ylabel('sinh(x)') ax2 = fig.add_subplot(222) ax2.plot(x,np.cosh(x),c='k') ax2.grid() ax2.set_xlabel('x') ax2.set_ylabel('cosh(x)') ax3 = fig.add_subplot(223) ax3.plot(x,np.tanh(x),c='k') ax3.grid()
ax3.set_xlabel('x') ax3.set_ylabel('tanh(x)') ax4 = fig.add_subplot(224) ax4.plot(x,1/np.cosh(x),c='k') ax4.grid() ax4.set_xlabel('x') ax4.set_ylabel('sech(x)') fig.subplots_adjust(wspace=0.4)
5.5 Table P5.5 below presents the escape velocities from planets in the solar system. Employ the Matplotlib pyplot module in a Python script to create an ordered (Pareto style) horizontal bar chart to display these data.
Table P5.5 Escape Velocity (km/h) for planets in the solar system.
Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune
Escape Velocity (km/h) 1.302E+04 3.747E+04 4.027E+04 1.831E+04 2.145E+05 1.278E+05 7.666E+04 8.525E+04
==================================================================== import matplotlib.pyplot as plt
planet = ['Jupiter','Saturn','Neptune','Uranus','Earth' \ ,'Venus','Mars','Mercury'] escvel = [2.145e5,1.278e5,8.525e4,7.666e4,4.027e4 \ ,3.747e4,1.831e4,1.302e4] plt.bar(planet,escvel,edgecolor='k',facecolor='y') plt.xticks(rotation=-90) plt.grid(axis='y') plt.title('Escape Velocities of the Planets')
5.6
Example 5.5 presents data and illustrates a bar chart for wind power capacity by country. Create a similar plot based on data of solar capacity by country.
================================================================== import matplotlib.pyplot as plt country = ['China','United States','Japan','Germany','India' \ ,'Italy','United Kingdom','Australia','France','South Korea'] GW = [175.018,62.200,55.500,45.930,26.869,20.120,13.108,11.300,9.483,7.862] plt.bar(country,GW,edgecolor='k',facecolor='c') plt.xticks(rotation=-90) plt.grid(axis='y') plt.ylabel('GW') plt.title('Solar Power Produced - Top 10 Countries - 2021')
https://worldpopulationreview.com/country-rankings/solar-power-by-country
5.7
An analytical function that we used in the chapter for examples of line and stem plots is f ( x ) = sin ( x ) cosh ( x ) − 1
0 x4
Create a polar plot of this function over the given domain (radians) using a Python script and the Matplotlib pyplot module. Label the plot. =================================================================== import numpy as np import matplotlib.pyplot as plt f = lambda x: np.sin(x)*np.cosh(x)-1 x = np.linspace(0,4,100) plt.polar(x,f(x),c='k') plt.title('Polar Plot of sin(x)*cosh(x)-1',pad=15) plt.figure() plt.plot(x,f(x))
5.8
The data in Table P5.8 were collected from a set of experiments involving a chemical reaction at different temperatures and catalyst concentrations. Using the stem function from Matplotlib pyplot, create a three-dimensional stem plot of these data. Access the Matplotlib Users Guide, if necessary. Table P5.8 Product yield at different temperatures and catalyst concentrations. Catalyst Concentration (g/L) 0.54 0.63 0.71 0.48 0.57 0.63 0.59 0.60
Temperature (°C)
Product Yield (%)
110 117 113 107 118 117 102 118
83 87 84 82 92 89 78 91
=================================================================== import matplotlib.pyplot as plt cat = [0.54,0.63,0.71,0.48,0.57,0.63,0.59,0.60] tmp = [110.,117.,113.,107.,118.,117.,102.,118.] yld = [83.,87.,84.,82.,92.,89.,78.,91.] fig, ax = plt.subplots(subplot_kw=dict(projection='3d')) ax.stem(cat, tmp, yld,basefmt='None') ax.set_xlabel('Catalyst Concentration - mg/L') ax.set_ylabel('Temperature - degC') ax.set_zlabel('Yield - %')
5.9
A rectangular wave is depicted in Figure P5.9. This waveform can be broken down into an infinite series of sinusoidal components called a Fourier series.
f ( x) =
4
cos ( x ) −
4 4 4 cos ( 3x ) + cos ( 5 x ) − cos ( 7 x ) + 3 5 7
Write a Python script that produces a line plot for − x including curves of the first term above, the first two terms, the first three terms, and so on to the first five terms. Use colored curves and provide a legend on the plot.
Figure P5.9 Rectangular wave. ==================================================================== import numpy as np import matplotlib.pyplot as plt f1 = lambda x: 4/np.pi*np.cos(x) f2 = lambda x: 4/np.pi*np.cos(x) -4/3/np.pi*np.cos(3*x) f3 = lambda x: 4/np.pi*np.cos(x) -4/3/np.pi*np.cos(3*x) \ + 4/5/np.pi*np.cos(5*x) f4 = lambda x: 4/np.pi*np.cos(x) -4/3/np.pi*np.cos(3*x) \ + 4/5/np.pi*np.cos(5*x) - 4/7/np.pi*np.cos(7*x) f5 = lambda x: 4/np.pi*np.cos(x) -4/3/np.pi*np.cos(3*x) \ + 4/5/np.pi*np.cos(5*x) - 4/7/np.pi*np.cos(7*x) \ + 4/9/np.pi*np.cos(9*x) x = np.linspace(-np.pi,np.pi,100) plt.plot(x,f1(x),c='k',label='one term') plt.plot(x,f2(x),c='g',label='two terms') plt.plot(x,f3(x),c='r',label='three terms') plt.plot(x,f4(x),c='m',label='four terms') plt.plot(x,f5(x),c='c',label='five terms') plt.grid() plt.xlabel('x') plt.ylabel('f(x)') plt.title('Fourier series approximation of rectangular waveform') plt.legend(loc='lower center')
5.10 A study has been conducted on the effect of water temperature and time in solution on the amount of dye absorbed by a type of fabric yarn. A standard amount of dye (200mg) was added to a fixed amount of water. The three temperatures used in the experiments were 105, 120 and 135C. The fabric was left in the water 15, 30 or 60 minutes. For each of these temperature-time combinations, the amount of dye absorbed by the yarn was measured. The experiments yielded the results shown in Table P5.10. Table P5.10 Dye absorption for different solution times and temperatures. Dye in yarn (mg) 120.4 143.0 190.6 111.2 134.8 182.6 98.4 123.9 173.2
Time in solution (min) 15 30 60 15 30 60 15 30 60
Temperature (C) 105 105 105 120 120 120 135 135 135
Create a meshplot of the dye absorbed versus time in solution and temperature. =================================================================== import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D
tim = np.array([15.,30.,60.]) tmp = np.array([105.,120.,135.]) dye = np.array([[120.4,111.2,98.4],[143.,134.8,123.9],[190.6,182.6,173.2]]) TM,TP = np.meshgrid(tim,tmp) fig = plt.figure() ax1 = fig.add_subplot(111,projection='3d') ax1.plot_wireframe(TM,TP,dye,color='k',rstride=5,cstride=5) ax1.grid() ax1.set_yticks([105.,115.,125.,135.]) #ax1.set_yticks([-2.,-1.,0.,1.,2.]) #ax1.set_zlim(0.,100.) #ax1.set_zticks(np.arange(0.,120.,20.)) ax1.set_xlabel('Time in solution - m') ax1.set_ylabel('Temperature - degC') ax1.set_zlabel('Dye in yarn (mg)') ax1.view_init(15)
5.11 Develop mesh and surface plots for the following model developed from experimental data of a response, y, for different values of two independent variables, x1 and x2.
y = 60.64 − 3.672 x1 + 11.661x2 − 3.514 x12 − 0.924 x22 + 2.220 x1 x2 Determine a meaningful range for each of the independent variables to illustrate the function near its minimum and display this case as your final plot. ===================================================================
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D def f(x1,x2): return 60.64 - 3.672*x1 + 11.66*x2 - 3.514*x1**2 \ - 0.924*x2**2 + 2.220*x1*x2 x1 = np.linspace(-2.,2.) x2 = np.linspace(0.,10.) X1,X2 = np.meshgrid(x1,x2) Y = f(X1,X2) fig1 = plt.figure() ax1 = fig1.add_subplot(111,projection='3d') ax1.plot_wireframe(X1,X2,Y,color='k',rstride=5,cstride=5) ax1.grid() ax1.set_xlabel('x1') ax1.set_ylabel('x2') ax1.set_zlabel('y') ax1.view_init(15)
from matplotlib import cm fig2 = plt.figure() ax2 = fig2.add_subplot(111,projection='3d') ax2.plot_surface(X1,X2,Y,cmap=cm.gray) ax2.grid() ax2.set_xlabel('x1') ax2.set_ylabel('x2') ax2.set_zlabel('y') ax2.view_init(45)
5.12 For the salt density data presented in Table 5.9, use the Matplotlib pyplot module to produce two line plots: (a) (b)
Salt density versus concentration with separate lines for temperatures 0, 25, 60 and 100°C. Salt density versus temperature with separate lines for concentrations 1, 8, 16, and 26 wt%.
For each plot, label the axes and include a title. Also, use different linestyles, black color, and provide a legend. ==================================================================== (a) import numpy as np import matplotlib.pyplot as plt NaOHDen = np.loadtxt('SaltDensity.csv',delimiter=',') T = np.array([0.,10.,25.,40.,60.,80.,100.]) C = np.array([1.,2.,4.,8.,12.,16.,20.,24.,26.]) plt.figure() plt.plot(C,NaOHDen[:,0],c='g',label='0 degC') plt.plot(C,NaOHDen[:,2],c='c',label='25 degC') plt.plot(C,NaOHDen[:,4],c='m',label='60 degC') plt.plot(C,NaOHDen[:,6],c='r',label='100 degC') plt.grid()
plt.xlabel('Salt Concentration - wt%') plt.ylabel('Density - g/cm3') plt.legend()
(b) NaOHDenT = np.transpose(NaOHDen) plt.figure() plt.plot(T,NaOHDenT[:,0],c='g',label='1 wt%') plt.plot(T,NaOHDenT[:,3],c='c',label='8 wt%') plt.plot(T,NaOHDenT[:,5],c='m',label='16 wt%') plt.plot(T,NaOHDenT[:,8],c='r',label='26 wt%') plt.ylim(0.95,1.3) plt.grid() plt.xlabel('Temperature - degC') plt.ylabel('Density - g/cm3') plt.legend()
5.13 The Pareto chart from Example 5.5 is often accompanied by a plot of the cumulative percentages of the total number of occurrences. Expand the Python code from Example 5.5 to compute the cumulative % of total wind power capacity and develop a line plot of cumulative capacity versus country. ===================================================================== import numpy as np import matplotlib.pyplot as plt country = ['China','USA','Germany','India','Spain' \ , 'UK', 'France', 'Brazil', 'Canada', 'Italy'] GW = [221.0, 96.4, 59.3, 35.0, 23.0, 21.7, 15.3, 14.5, 12.8, 10.0] plt.bar(country,GW,edgecolor='k',facecolor='c') plt.xticks(rotation=-90) plt.grid(axis='y') plt.title('Wind Power Capacity in GW by Country in 2021') GWTotal = np.sum(GW) GWPct = GW/GWTotal*100 n = len(GWPct) GWCum = np.zeros(n) GWCum[0] = GWPct[0] for i in range(n): GWCum[i] = GWCum[i-1] + GWPct[i] plt.figure() plt.plot(country,GWCum,c='k',marker='o') plt.grid() plt.xticks(rotation=-90)
plt.xlabel('Country') plt.ylabel('Cumulative Percentage of Total Wind Power')
Chapter 6 6.1 Write a Python script to create the two-dimensional array shown below, and then add statements to extract the subset array indicated. Hint: Use the colon in array indexing. Use print statements to document your results. 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8
=================================================================== import numpy as np A = np.array([[1,2,3,4,5],[2,3,4,5,6],[3,4,5,6,7],[4,5,6,7,8]]) print(A) B = A[1:4,2:4] print('\n',B) [[1 2 3 4 5] [2 3 4 5 6] [3 4 5 6 7] [4 5 6 7 8]] [[4 5] [5 6] [6 7]]
6.2 Write a Python script using a for loop to create a one-dimensional array with odd numbers from 1 to 9. Use print statements to document your results. ==================================================================== import numpy as np x = np.zeros(5) for i in range(5): x[i] = (i+1)*2-1 print(x) [1. 3. 5. 7. 9.]
6.3 Employ the stack commands to add the following to the array in Problem 6.1. (a) Add this row to the bottom of the array: −1 −2 −3 −4 −5
6 (b) Add this column to the right side of the array:
7 8 9
Provide your Python code and document your results. ================================================================== (a) import numpy as np A = np.array([[1,2,3,4,5],[2,3,4,5,6],[3,4,5,6,7],[4,5,6,7,8]]) print(A) x = np.array([-1,-2,-3,-4,-5]) A1 = np.vstack((A,x)) print('\n',A1) [[1 2 3 4 5] [2 3 4 5 6] [3 4 5 6 7] [4 5 6 7 8]] [[ 1 2 3 4 5] [ 2 3 4 5 6] [ 3 4 5 6 7] [ 4 5 6 7 8] [-1 -2 -3 -4 -5]]
(b) y = np.array([[6],[7],[8],[9]]) A2 = np.hstack((A,y)) print('\n',A2) [[1 2 3 4 5 6] [2 3 4 5 6 7] [3 4 5 6 7 8] [4 5 6 7 8 9]]
6.4 Write Python code to create an array of one hundred x values from 0 to 5, and then use this array to create a well-formatted plot of the following function:
e− x 4 ( 2 − x ) − 1 ==================================================================== import numpy as np import matplotlib.pyplot as plt f = lambda x: np.exp(-x/4)*(2-x)-1 x = np.linspace(0,5,100) y = f(x) plt.plot(x,y,c='k') plt.grid() plt.xlabel('x') plt.ylabel('f(x)') plt.title('Plot of f(x)=exp(-x/4)*(2-x)-1')
6.5 Write a compact set of Python statements to create a 7×7 array filled with sevens. Use a print statement to display the array. ================================================================== import numpy as np A = np.ones((7,7))
A = A*7 print(A) [[7. 7. 7. 7. 7. 7. 7.] [7. 7. 7. 7. 7. 7. 7.] [7. 7. 7. 7. 7. 7. 7.] [7. 7. 7. 7. 7. 7. 7.] [7. 7. 7. 7. 7. 7. 7.] [7. 7. 7. 7. 7. 7. 7.] [7. 7. 7. 7. 7. 7. 7.]]
6.6 Develop Python code that changes this row array −1 −2 −3 −4 −5
into a column array. Do so with both the reshape function and the transpose method and compare the results. =================================================================== import numpy as np x = np.array([[-1,-2,-3,-4,-5]]) x1 = np.transpose(x) print(x) print(x1) [[-1 -2 -3 -4 -5]] [[-1] [-2] [-3] [-4] [-5]]
Note: For the use of transpose, we must use an extra pair of [ ] in creating x as a twodimensional array with one row and five columns. This is not necessary with reshape below. Reshape creates the two-dimensional array from a one-dimensional array. y = np.array([-1,-2,-3,-4,-5]) y1 = y.reshape(5,1) print(y) print(y1) [-1 -2 -3 -4 -5] [[-1]
[-2] [-3] [-4] [-5]]
6.7 Create a Python function called fnorm, that computes the square root of the sum of squares of an array. The function should perform with one- or two-dimensional arrays. Test it for both. Compare your result to that from using the norm function from the NumPy linalg module. This is called the Frobenius norm. ==================================================================== import numpy as np def fnorm(x): x2 = x*x sumx2 = np.sum(x2) return np.sqrt(sumx2) a = np.array([[1.,2.],[3.,4.]]) print(fnorm(a)) print(np.linalg.norm(a)) b = np.array([-1,-2,-3,-4,-5]) print(fnorm(b)) print(np.linalg.norm(b)) 5.477225575051661 5.477225575051661 7.416198487095663 7.416198487095663
6.8 The vector product, xTx, is equivalent to the dot product of x with itself. A more general form is the quadratic form, xT Q x
where Q is a symmetric, square matrix. The form of the Q matrix is such that its transpose is the same matrix. The elements on opposite sides of the diagonal are equal. q11 q 21 qn1
q21 q22 qn 2
qn1 qn 2 qnn
Develop a Python function, called quadform, that takes arguments of x and Q and computes the quadratic product as shown above. The function should check that the dimension of x matches the number of rows and columns of Q. It should also check that Q is a square matrix and a symmetric matrix. Test and document your results. ===================================================================== import numpy as np def quadform(x,Q): n,m = np.shape(Q) if n != m: return 'Q matrix not square' Qt = Q.transpose() for i in range(n): for j in range(n): if Qt[i,j] != Q[i,j]: return 'Q matrix not symmetric' n1 = len(x) if n1 != n: return 'n and Q dimensions do not match' xQ = x.dot(Q) xQx = xQ.dot(xQ) return xQx x = np.array([1,2,3,4]) Q = np.array([[1.0,0.5,0.2,0.1], [0.5,1.0,0.4,0.3] \ ,[0.2,0.4,1.0,0.05],[0.1,0.3,0.05,1.0]]) quadxQ = quadform(x,Q) print(quadxQ) 74.1725
Test error traps Asymmetric Q Q = np.array([[1.0,0.6,0.2,0.1], [0.5,1.0,0.4,0.3] \ ,[0.2,0.4,1.0,0.05],[0.1,0.3,0.05,1.0]]) Q matrix not symmetric
x not correct dimension x = np.array([1,2,3]) n and Q dimensions do not match
Q matrix not square Q = np.array([[1.0,0.5,0.2,0.1], [0.5,1.0,0.4,0.3]]) Q matrix not square
6.9 Write a Python script to determine the inverse of the following matrix.
1.13 0.53 3.41 1.24
3.40 1.54 4.93 8.76 1.31 4.99 10.67 0.02
3.83 1.79
1.16 2.53
Include in your script a test of the inverse by multiplying it by the original matrix with the result of an approximate identity matrix. ==================================================================== import numpy as np A = np.array([[1.13,3.83,1.16,3.40],[0.53,1.79,2.53,1.54] \ ,[3.41,4.93,8.76,1.31],[1.24,4.99,10.67,0.02]]) Ainv = np.linalg.inv(A) Itest = A.dot(Ainv) print('A =\n',A) print('\nA-inverse =\n',Ainv) print('\nA x A-inverse =\n',Itest) A = [[ 1.13 3.83 1.16 3.4 ] [ 0.53 1.79 2.53 1.54] [ 3.41 4.93 8.76 1.31] [ 1.24 4.99 10.67 0.02]] A-inverse = [[-0.07829101 -0.22514192 0.47285055 -0.32631154] [ 0.5333038 -1.08340824 -0.11500059 0.29332667] [-0.23993721 0.53074973 -0.00111876 -0.00512364] [-0.19875222 1.11417477 -0.0272268 -0.2202252 ]] A x A-inverse = [[ 1.00000000e+00 6.66133815e-16 0.00000000e+00 0.00000000e+00] [-1.11022302e-16 1.00000000e+00 0.00000000e+00 -2.77555756e-17] [ 0.00000000e+00 -4.44089210e-16 1.00000000e+00 0.00000000e+00] [ 0.00000000e+00 -8.88178420e-16 1.11022302e-16 1.00000000e+00]]
6.10 Solve the following set of linear equations via a Python script using the inv function from the NumPy linalg module and the dot method. Test your solution using the equations. 8 x1 + 2 x2 + 3 x3 = 30 x1 − 9 x2 + 2 x3 = 1 2 x1 + 3x2 + 6 x3 = 31
==================================================================== import numpy as np A = np.array([[8,2,3],[1,-9,2],[2,3,6]]) b = np.array([30,1,31]) Ainv = np.linalg.inv(A) x = Ainv.dot(b) print(x) btest = A.dot(x) print(btest) [2. 1. 4.] [30. 1. 31.]
x1 = 2, x2 = 1, x3 = 4 Solution tests successfully. 6.11 Develop and test a Python function, called trace, that has a square array as an argument and returns the sum of the diagonal elements of the array. The function should check that the input argument is a square matrix. ================================================================== import numpy as np A = np.array([[1.13,3.83,1.16,3.40],[0.53,1.79,2.53,1.54] \ ,[3.41,4.93,8.76,1.31],[1.24,4.99,10.67,0.02]]) def trace(A): n,m = np.shape(A) if n != m: return 'matrix not square' sumdiag = 0 for i in range(n): sumdiag = sumdiag + A[i,i] return sumdiag
print(trace(A)) 11.7
Error check: A = np.array([[1.13,3.83,1.16,3.40],[0.53,1.79,2.53,1.54]]) matrix not square
6.12 Develop and test a Python function, called absnorm, which is an alternate to the Frobenius norm. This norm sums the absolute values of the array elements. ==================================================================== import numpy as np A = np.array([[1.,-2.,3.],[-5.,4.,-3.],[2.,3.,-6.]]) def absnorm(A): n,m = np.shape(A) sumabs = 0 for i in range(n): for j in range(m): sumabs = sumabs + abs(A[i,j]) return sumabs print(absnorm(A)) 29.0
Chapter 7 7.1 Use the bisection method to find a root of the following equation between x = 0 and x = 2.
f ( x)
−x =e 4
( 2 − x) −1 = 0
=================================================================== def bisect(func,x1,x2,maxit=20): if func(x1)*func(x2)>0: return 'initial estimates do not bracket solution' for i in range(maxit): # carry out maxit iterations xmid = (x1+x2)/2 # calculate midpoint if func(xmid)*func(x1)>0: # check if f(xmid) same sign as f(x1) x1 = xmid # if so, replace x1 with xmid else: x2 = xmid # if not, replace x2 with xmid return xmid # return the latest value of xmid as the solution import numpy as np f = lambda x: np.exp(-x/4)*(2-x)-1 x1 = 0. ; x2 = 2. xsoln = bisect(f,x1,x2) print('solution is {0:6.4f}'.format(xsoln)) solution is 0.7836
7.2 For the equation from Problem 1, devise and carry out a fixed-point iteration scheme to solve for x. =================================================================== Try
x = −e x 4 + 2 Make a plot first import numpy as np import matplotlib.pyplot as plt g = lambda x: -np.exp(x/4)+2 x = np.linspace(0,2)
plt.plot(x,g(x),c='k') plt.grid() plt.xlabel('x') plt.ylabel('g(x)')
|g'(x)| appears to be < 1, so should work def fixpt(g,x1,Ea=1.e-7,maxit=30): for i in range(maxit): # carry out maxit iterations x2 = g(x1) # calculate new x if abs((x2-x1)/x2) < Ea: # check if converged iter = i+1 return x2,iter # if converged, return xnew and iterations x1 = x2 # if not converged, replace x1 with x2 iter = i+1 return x2,iter # here if maxit reached x0 = 1 xsoln,iter = fixpt(g,x0) print(xsoln) print(iter) 0.7835959803263917 14
Agrees with the solution to Problem 7.1.
7.3 Determine 3
5
using a root-solving method. Hint: If x = 3 5 , cube both sides and proceed. ==================================================================== f ( x ) = x3 − 5 = 0 def bisect(func,x1,x2,maxit=20): if func(x1)*func(x2)>0: return 'initial estimates do not bracket solution' for i in range(maxit): # carry out maxit iterations xmid = (x1+x2)/2 # calculate midpoint if func(xmid)*func(x1)>0: # check if f(xmid) same sign as f(x1) x1 = xmid # if so, replace x1 with xmid else: x2 = xmid # if not, replace x2 with xmid return xmid # return the latest value of xmid as the solution f = lambda x: x**3 - 5 x1 = 0. ; x2 = 2. xsoln = bisect(f,x1,x2) print('solution is {0:8.5f}'.format(xsoln)) solution is
1.70997
7.4 Create a plot of
f ( x ) = x 2 sin ( x ) − 4 over the range 0 x 4 , and then use the false position method to determine a root that you observe in the plot. =================================================================== import numpy as np import matplotlib.pyplot as plt f = lambda x: x**2*abs(np.sin(x))-4 x = np.linspace(0,4,100) plt.plot(x,f(x),c='k')
plt.grid() plt.xlabel('x') plt.ylabel('f(x)')
There might be two (or three) roots. Let's take a closer look at 2.2 to 2.4.
No, not quite. We will find the root around 3.5. import numpy as np f = lambda x: x**2*abs(np.sin(x))-4 def falpos2(func,x1,x2,Ea=1.e-7,maxit=30): if func(x1)*func(x2)>0: return 'initial estimates do not bracket solution',0
xnewprev = 1. iter = 0 while True: # carry out maxit iterations iter = iter + 1 xnew = (x1*func(x2)-x2*func(x1))/(func(x2)-func(x1)) # calculate new x if abs((xnew-xnewprev)/xnew) < Ea: # check if converged return xnew,iter # if converged, return xnew and iterations if iter > maxit: # check if maximum iterations exceeded return 'maximum iterations exceeded',iter if func(xnew)*func(x1)>0: # check if f(xnew) same sign as f(x1) x1 = xnew # if so, replace x1 with xnew else: x2 = xnew # if not, replace x2 with xnew xnewprev = xnew xsoln,iter = falpos2(f,3.,4.) print(xsoln) print(iter) 3.478508504956564 9
7.5 Create a plot of f ( x ) = x3 − 12.42 x 2 + 50.444 x − 66.552
over the range 2 x 6 . (a) Apply the Newton-Raphson method three times with well-chosen initial estimates for x to determine all three roots of the polynomial. (b) Use the NumPy roots function to determine all the roots of the polynomial and compare these with your results from part a). ==================================================================== f ( x ) = 3x 2 − 24.84 x + 50.444 import numpy as np import matplotlib.pyplot as plt x = np.linspace(2.,6.,100) f = lambda x: x**3 - 12.42*x**2 + 50.444*x - 66.552
plt.plot(x,f(x),c='k') plt.grid() plt.xlabel('x') plt.ylabel('f(x)')
Let's check more closely between 4.5 and 5.0.
Looks like there may be a double, tangential root near x = 4.7. (a) import numpy as np f = lambda x: x**3 - 12.42*x**2 + 50.444*x - 66.552 fp = lambda x: 3*x**2 - 24.84*x + 50.444
def newtraph(f,fp,x1,Ea=1.e-7,maxit=30): for i in range(maxit): # carry out maxit iterations x2 = x1 - f(x1)/fp(x1) # calculate new x if abs((x2-x1)/x2) < Ea: # check if converged iter = i+1 return x2,iter # if converged, return xnew and iterations x1 = x2 # if not converged, replace x1 with x2 iter = i+1 return x2,iter # here if maxit reached x0 = 3.5 xsoln,iter = newtraph(f,fp,x0) print(xsoln) print(iter) 2.9999999999999956 9
Root is evidently x = 3. x0 = 4.5 4.700000000000515 8
One root at x = 4.7 x0 = 4.9 4.720000000001063
Another close, but not equal, x = 4.72. (b) coeff = np.array([1.,-12.42,50.444,-66.552]) r = np.roots(coeff) print(r) [4.72 4.7
3.
Confirms part (a).
]
7.6 Determine the root of f ( x ) = tan ( x ) − 2 x = 0
between x = 0 and x = /2 using the Newton-Raphson method. =================================================================== f ( x ) = tan 2 ( x ) − 1
Let's plot first. import numpy as np import matplotlib.pyplot as plt x = np.linspace(0,np.pi/2-0.1,100) f = lambda x: np.tan(x)-2*x plt.plot(x,f(x),c='k') plt.grid() plt.xlabel('x') plt.ylabel('f(x)')
Looks like a root around x = 1.2. Have to be careful with initial guesses < root because of initial slope. Try initial guess of 1.3. import numpy as np x = np.linspace(0,np.pi/2-0.1,100) f = lambda x: np.tan(x)-2*x fp = lambda x: np.tan(x)**2 - 1
def newtraph(f,fp,x1,Ea=1.e-7,maxit=30): for i in range(maxit): # carry out maxit iterations x2 = x1 - f(x1)/fp(x1) # calculate new x if abs((x2-x1)/x2) < Ea: # check if converged iter = i+1 return x2,iter # if converged, return xnew and iterations x1 = x2 # if not converged, replace x1 with x2 iter = i+1 return x2,iter # here if maxit reached x0 = 1.3 xsoln,iter = newtraph(f,fp,x0) print(xsoln) print(iter) 1.1655611852072112 6
7.7 A common shape for an underground tank to store liquids in a horizontal cylinder with hemispherical ends. This is illustrated in Figure P7.7. The volume of liquid, V, is related to the liquid depth, h, by considering two parts. Conceptually, the hemispherical ends can be put together to form a sphere of radius R. The volume in that part is then 3R − h Vsph = h 2 3
The volume in the cylindrical part is given by a more complicated formula:
R−h 2 Vcyl = R 2 cos −1 − ( R − h) 2 R h − h L R Then, the total volume of liquid is
V = Vsph + Vcyl For a tank with the dimensions, L = 5 m and R = 0.75 m, write a Python script that finds the depth, h, for a given volume, V. Then, extend the script to provide a calibration table in even increments of volume in liters giving the corresponding depth for each volume value. Finally, produce a plot of depth in meters versus volume in liters.
Figure P7.7 Horizontal tank with hemispherical ends.
=================================================================== import numpy as np import matplotlib.pyplot as plt def Vsph(R,h): return np.pi*h**2*(3*R-h)/3 def Vcyl(R,h,L): vol = (R**2*np.arccos((R-h)/R)-(R-h)*np.sqrt(2*R*h-h**2))*L return vol def Vtot(R,h,L): return Vsph(R,h)+Vcyl(R,h,L) R = 0.75 L = 5 h = 2*R Vtotal = Vtot(R,h,L) print('Total tank volume: {0:6.3f} m3'.format(Vtotal)) V = 2
#
test value
def f(h): return Vtot(R,h,L)-V def bisect(func,x1,x2,maxit=20): if func(x1)*func(x2)>0: return 'initial estimates do not bracket solution' for i in range(maxit): # carry out maxit iterations xmid = (x1+x2)/2 # calculate midpoint if func(xmid)*func(x1)>0: # check if f(xmid) same sign as f(x1) x1 = xmid # if so, replace x1 with xmid else: x2 = xmid # if not, replace x2 with xmid return xmid # return the latest value of xmid as the solution result = bisect(f,0,2*R) if type(result) == str: print(result)
# test result
else: print('Liquid level: {0:6.3f} m'.format(result)) Vtest = np.linspace(0.001*Vtotal,0.999*Vtotal) n = len(Vtest) h = np.zeros(n) for i in range(n): V = Vtest[i] h[i] = bisect(f,0,2*R) VtestL = Vtest*1000 plt.plot(VtestL,h,c='k') plt.grid() plt.xlabel('Liquid Volume - liters') plt.ylabel('Liquid Level - meters') plt.title('Tank Calibration Curve, R = 0.75 m, L = 5 m')
Test result displayed: Total tank volume: 10.603 m3 Liquid level: 0.375 m
7.8 From Example 3.1, we have the formula for computing the Great Circle distance between two points on the Earth as 2 2 lt − lt ln − ln 1 2 1 2 d = 2sin sin + cos ( lt1 ) cos ( lt2 ) sin r 2 2 -1
where lt1 ,ln1 is the location of the first point by latitude and longitude. If we pick the Meridian at the Equator the location is {0,0}. For a longitude of 105° W, determine the
latitude S that is 11,500 km from the Meridian/Equator. Describe approximately where on the globe this is located. ==================================================================== import numpy as np def bisect(func,x1,x2,maxit=20): if func(x1)*func(x2)>0: return 'initial estimates do not bracket solution' for i in range(maxit): # carry out maxit iterations xmid = (x1+x2)/2 # calculate midpoint if func(xmid)*func(x1)>0: # check if f(xmid) same sign as f(x1) x1 = xmid # if so, replace x1 with xmid else: x2 = xmid # if not, replace x2 with xmid return xmid # return the latest value of xmid as the solution def dist(lt1,ln1,lt2,ln2,r): term1 = np.sin((lt1-lt2)/2)**2 term2 = np.cos(lt1)*np.cos(lt2)*(np.sin((ln1-ln2)/2))**2 d = 2*np.arcsin(np.sqrt(term1+term2))*r return d r = 6371 lt1 = 0 ln1 = 0 ln2 = np.radians(105) lt2d = 30 lt2 = np.radians(lt2d) dtest = dist(lt1,ln1,lt2,ln2,r) print('Test distance for 40 degN = {0:8.1f} km'.format(dtest)) dgoal = 11500 def f(lt2d): lt2 = np.radians(lt2d) return dgoal - dist(lt1,ln1,lt2,ln2,r) lat2d = bisect(f,0,75) print('Latitude = {0:6.1f} degrees'.format(lat2d)) Test distance for 40 degN = Latitude = 26.3 degrees
11447.8 km
For a distance of 11,500, the latitude is 26.3 °N.
Location: in Mexico, state of Durango, northwest of Torreón and northeast of Culiacán.
7.9 An alternate, and more recent equation of state than the van der Waals equation, which accounts for behavior of gases at nonideal conditions, is that due to Soave, Redlich and Kwong (SRK). The SRK equation is P=
RT a − Vˆ − b Vˆ Vˆ + b
(
)
where, including example units, P = pressure in kPa, R = gas law constant, 8.3145 L·kPa/(mol·K), T = temperature in K, V̂ = specific volume in L/mol. The additional parameters, b in L/mol, a in (L/mol)2, and α (dimensionless), are dependent on the gas under consideration. For propane, C3H8, their values are a = 9.3775 (L/mol)2, b = 0.06262 L/mol, and α = 0.903 For a temperature of 423 K and a pressure of 7,000 kPa, determine the specific volume using the SRK equation of state. Set this up as a circular calculation and use the Wegstein method. Compare the result to the specific volume determined by the ideal gas law,
P=
RT V̂
==================================================================
Solve for Vˆ in first term on the right:
( )
RT
Vˆ = b + P+
(
a
Vˆ Vˆ + b
= g Vˆ
)
def wegstein(g,x1,x2,Ea=1.e-7,maxit=30): for i in range(maxit): # carry out maxit iterations x3 = (x1*g(x2)-x2*g(x1))/(x1-x2+g(x2)-g(x1)) # calculate new x if abs((x3-x2)/x3) < Ea: # check if converged iter = i+1 return x3,iter # if converged, return xnew and iterations x1 = x2 # if not converged, replace x1 with x2 x2 = x3 # and replace x2 with x3 iter = i+1 return x3,iter # here if maxit reached R = 8.3145 a = 9.3775 b = 0.06262 alpha = 0.903 P = 7000 T = 423 def g(V): V2 = b + R*T/(P+alpha*a/V/(V+b)) return V2 V1 = 0.1 V2 = 0.2 Vsoln,iter = wegstein(g,V1,V2) print('Specific volume = {0:5.3f} L/mol'.format(Vsoln)) print(iter) Specific volume = 0.563 L/mol 5
7.10 An important property in working with thermal calculations with gases is the heat capacity. In general, it provides the energy required to raise the temperature of the gas by one degree. The heat capacity varies by temperature and can be modeled accurately by a polynomial. An example is the heat capacity of water vapor:
CP = 33.46 + 0.006880 T + 0.7604 10−5 T 2 − 3.59310−9 T 3
where CP is the heat capacity in J/(mol·°C) and T is the temperature in °C. This formula is valid for a wide range of temperatures, 0 to 1500 °C. a) Create a plot of heat capacity versus temperature for the entire valid range of temperatures. b) Create a Python script using the Newton-Raphson method to compute and display the temperature at which the heat capacity is 40 J/(mol·°C). =================================================================== (a) import numpy as np import matplotlib.pyplot as plt def CP(T): return 33.46 + 0.006880*T + 0.7604e-5*T**2 - 3.593e-9*T**3 def CPd(T): return 0.006880 + 2*0.7604e-5*T - 3.593e-9*3*T**2 Tplot = np.linspace(0,1500,100) plt.plot(Tplot,CP(Tplot),c='k') plt.grid() plt.xlabel('Temperature -- degC') plt.ylabel('Heat Capacity -- J/mol/degC') plt.title('Heat Capacity of Water Vapor vs. Temperature')
(b) def newtraph(f,fp,x1,Ea=1.e-7,maxit=30): for i in range(maxit): # carry out maxit iterations x2 = x1 - f(x1)/fp(x1) # calculate new x if abs((x2-x1)/x2) < Ea: # check if converged iter = i+1 return x2,iter # if converged, return xnew and iterations x1 = x2 # if not converged, replace x1 with x2 iter = i+1 return x2,iter # here if maxit reached def CP(T): return 33.46 + 0.006880*T + 0.7604e-5*T**2 - 3.593e-9*T**3 def CPd(T): return 0.006880 + 2*0.7604e-5*T - 3.593e-9*3*T**2 CPgoal = 40 def f(T): return CP(T)-CPgoal Tgoal,iter = newtraph(f,CPd,600) print('Temperature for CP = 40: {0:6.1f} degC'.format(Tgoal)) print(iter) Temperature for CP = 40: 3
637.1 degC
7.11 The upward velocity of a rocket can be computed with the following formula: m0 v = u ln − gt m0 − qt
where v is the upward velocity, u = the downward exit velocity of fuel relative to the rocket, m0 = mass of the rocket at t = 0 , q = the fuel consumption rate, g = gravitational acceleration, 9.81 m/s2. If u = 1800 m/s, m0 = 160,000 kg , q = 2600 kg/s, compute the time at which v = 750 m/s. Use the brentq function to solve this problem. =================================================================== import numpy as np from scipy.optimize import brentq g = 9.81 u = 1800 m0 = 160000 q = 2600
v = 750 def f(t): return v - u*np.log(m0/(m0-q*t))-g*t t1 = 0 t2 = 60 tsoln = brentq(f,t1,t2) print('Time = {0:6.2f} sec'.format(tsoln)) Time =
17.03 sec
7.12 You plan to buy a Tesla Model S vehicle for $79,990 by paying 10% down and financing the balance over a seven-year term. You have budgeted a monthly payment of $1,000. So, now you need to shop for a loan at the required interest rate (or lower, if you can find it!). The formula that governs this is A=P
i (1 + i )
n
(1 + i ) − 1 n
where A = the monthly payment, P = the loan amount, i = the monthly interest rate expressed as a fraction, not a percentage. Note: Loan interest rates are typically quoted on an annual basis as “APR.” To obtain the monthly interest rate, i , you divide the APR by twelve. The actual annual interest rate you 12 pay is greater than the quoted APR. It is (1 + i ) − 1 ( 100 for %). As an example, for an APR of 6%, with monthly payments, the actual annual interest rate is 6.17%. Determine the APR you will need to obtain to meet your goal. =================================================================== from scipy.optimize import brentq A = 1000 Purch = 79900 P = 0.9*Purch n = 12*7 def f(APR): i = APR/12/100 return A - P*i*(1+i)**n/((1+i)**n-1) APR1 = 3 APR2 = 7 APRsoln = brentq(f,APR1,APR2)
print('Required APR: {0:5.2f} %'.format(APRsoln)) Required APR:
4.51 %
7.13 As depicted schematically in Figure P7.13, for any two massive bodies there are five points where the net force on a third smaller-mass body is zero. That is, the gravitational forces of the massive bodies and the centrifugal force are in balance. This feature makes them excellent locations for satellites, as minimal orbit corrections are needed to maintain a desired orbit. For our solar system, this is the case for the Sun and any planet, or for a planet and one of its moons. These points are commonly referred to as the Lagrange points.2 Noteworthy spacecraft designed to operate near the Earth–Sun L2 are the Gaia Space Observatory and the James Webb Space Telescope. The force balance equation for L2 can be written as M1 R M +M M1 M + 22 = +r 1 3 2 2 (R + r) r M1 + M 2 R
where r is the distance of the L2 point from the smaller of the two massive objects, M1 and M2 are the masses of the larger and smaller massive objects, respectively, and R is the distance between them. A summary of the parameters for the Sun, Earth, and our Moon are listed in the table below. Determine r for an L2 satellite orbiting around (a) the Earth and (b) the moon. System
Sun-Earth
M1 M2 R
1.991x1030 5.98x1024 149,600,000
EarthMoon 5.98x1024 7.35x1022 384,401
units kg kg km
If the mass of the smaller object is much smaller than the mass of the larger object, a good initial estimate of r can be computed with rR 3
M2 3M1
Joseph-Louis Lagrange (born Giuseppe Luigi Lagrangia) (1736 –1813) was an Italian mathematician and astronomer, later naturalized French. He made significant contributions to the fields of analysis, number theory, and both classical and celestial mechanics. Together with Euclid, Euler, Gauss, Newton, and a few others, he is considered to be one of the giants of mathematics. 2
Figure P7.13 The Lagrange points for two massive bodies.
=================================================================== Equation to be solved for r is f (r ) =
M +M M1 M M1 R + 22 − +r 1 3 2 =0 2 (R + r) r M1 + M 2 R
M1 = 1.991e30 # kg M2 = 5.98e24 # kg R = 1.496e8 # km def f(r): term1 = M1/(R+r)**2 term2 = M2/r**2 term3 = (M1*R/(M1+M2)+r)*(M1+M2)/R**3 return term1 + term2 - term3 r0 = R*(M2/3/M1)**(1./3) def modsec(f,x1,delta=1.e-6,Ea=1.e-7,maxit=30): for i in range(maxit): # carry out maxit iterations x2 = x1*(1-f(x1)*delta/(f(x1*(1+delta))-f(x1))) # calculate new x if abs((x2-x1)/x2) < Ea: # check if converged iter = i+1 return x2,iter # if converged, return xnew and iterations x1 = x2 # if not converged, replace x1 with x2 iter = i+1 return x2,iter # here if maxit reached r,iter = modsec(f,r0) print('Sun-Earth Case') print('r = {0:7.3e} km'.format(r))
M1 = 5.98e24 # kg M2 = 7.35e22 # kg R = 384401. # km r0 = R*(M2/3/M1)**(1./3) r,iter = modsec(f,r0) print('\nEarth-Moon Case') print('r = {0:8.0f} km'.format(r)) Sun-Earth Case r = 1.502e+06 km Earth-Moon Case r = 64499 km
7.14 A four-bar linkage is illustrated in Figure P7.14. Such linkages are designed by mechanical engineers to transmit mechanical movement in various ways.
Figure P7.14 Four-bar linkage.
Member AD is anchored to a supporting base and does not move. Member AB rotates about point A, resulting in a reciprocating movement of members BC and CD. All motion is in the plane of the page. Therefore, angle 2 changes in response to a change in angle 1. This motion is therefore constrained by the equation 2
(
AD = AB cos 1 − CD cos 2 + BC − CD sin 2 − AB sin1
)
2
Suppose AB = 1.5 m , BC = 6 m , CD = 3.5 m , and AD = 5 m . (a)
Determine angle 2, given a value of angle 1, for example, 45°. Create a Python script that uses the false position technique.
(b)
Create a data table of 2 values for a range of 1 values. Present a graph of this relationship.
=================================================================== (a) import numpy as np import matplotlib.pyplot as plt AB = 1.5 BC = 6.0 CD = 3.5 AD = 5.0 th1d = 45 th1 = np.radians(th1d) def f(th2): eq = AD - AB*np.cos(th1) + CD*np.cos(th2) - \ np.sqrt(BC**2 - (CD*np.sin(th2) - AB*np.sin(th1))**2) return eq def falpos2(func,x1,x2,Ea=1.e-7,maxit=30): if func(x1)*func(x2)>0: return 'initial estimates do not bracket solution',0 xnewprev = 1. iter = 0 while True: # carry out maxit iterations iter = iter + 1 xnew = (x1*func(x2)-x2*func(x1))/(func(x2)-func(x1)) # calculate new x if abs((xnew-xnewprev)/xnew) < Ea: # check if converged return xnew,iter # if converged, return xnew and iterations if iter > maxit: # check if maximum iterations exceeded return 'maximum iterations exceeded',iter if func(xnew)*func(x1)>0: # check if f(xnew) same sign as f(x1) x1 = xnew # if so, replace x1 with xnew else: x2 = xnew # if not, replace x2 with xnew xnewprev = xnew th2d1 = -20 th21 = np.radians(th2d1) th2d2 = 80 th22 = np.radians(th2d2) th2,iter = falpos2(f,th21,th22)
th2d = np.degrees(th2) print('theta2 = {0:6.2f} degrees'.format(th2d)) print('Iterations = {0:4.0f}'.format(iter)) theta2 = 60.52 degrees Iterations = 10
(b) # Problem 7.14b import numpy as np import matplotlib.pyplot as plt AB = 1.5 BC = 6.0 CD = 3.5 AD = 5.0 def f(th2): eq = AD - AB*np.cos(th1) + CD*np.cos(th2) - \ np.sqrt(BC**2 - (CD*np.sin(th2) - AB*np.sin(th1))**2) return eq def falpos2(func,x1,x2,Ea=1.e-7,maxit=30): if func(x1)*func(x2)>0: return 'initial estimates do not bracket solution',0 xnewprev = 1. iter = 0 while True: # carry out maxit iterations iter = iter + 1 xnew = (x1*func(x2)-x2*func(x1))/(func(x2)-func(x1)) # calculate new x if abs((xnew-xnewprev)/xnew) < Ea: # check if converged return xnew,iter # if converged, return xnew and iterations if iter > maxit: # check if maximum iterations exceeded return 'maximum iterations exceeded',iter if func(xnew)*func(x1)>0: # check if f(xnew) same sign as f(x1) x1 = xnew # if so, replace x1 with xnew else: x2 = xnew # if not, replace x2 with xnew xnewprev = xnew th1ad = np.linspace(0,180,100) th1a = np.radians(th1ad) n = len(th1a) th2 = np.zeros(n) for i in range(n): th1 = th1a[i]
th2d1 = -20 th21 = np.radians(th2d1) th2d2 = 170 th22 = np.radians(th2d2) th2[i],iter = falpos2(f,th21,th22) plt.plot(th1ad,np.degrees(th2),c='k') plt.grid() plt.xlabel('theta1 - degrees') plt.ylabel('theta2 - degrees')
7.15 When water is flowing down a sloped, open channel, a phenomenon called a hydraulic jump can occur where the water with a depth, h1, and a flowing velocity, v1, suddenly jumps up to a depth, h2, with a slower velocity, v2. This is depicted in Figure P7.15.
Figure P7.15 Hydraulic jump.
An equation that describes a hydraulic jump is h1 8v12 h2 = 1 + − 1 2 gh1
and such a jump is possible only if
v1 gh1
If values of v1 and h2 are known, create a Python script to determine h1 and whether the jump is possible. Typical velocities (v1) would be 1-to-2 m/s and after-jump depths would be 2-to-3 cm. ===================================================================== import numpy as np from scipy.optimize import brentq g = 9.807 # m/s2 h2cm = 2.5 # cm h2 = h2cm/100 # m v1 = 1.5 # m/s f = lambda h1: h2 - h1/2*(np.sqrt(1+8*v1**2/g/h1)-1) h1a = 0.001 # m h1b = 0.2 # m h1 = brentq(f,h1a,h1b) print('upstream depth = {0:5.3f} cm'.format(100*h1)) print('feasibily test: ',v1>np.sqrt(g*h1)) upstream depth = 0.144 cm feasibily test: True
Chapter 8 8.1 Given the system of equations −3 x2 + 7 x3 = 4 x1 + 2 x2 − x3 = 0 5 x1 − 2 x2 = 3
Carry out the following manually. For parts (b) and (c), check your solutions in the equations. Carry fractions along in your calculations. (a) Compute the determinant of the coefficient matrix. (b) Solve the system of equations using Cramer's rule. (c) Solve the system of equations using Gaussian elimination with pivoting. =================================================================== (a) Coefficient matrix is 0 −3 7 1 2 −1 5 −2 0
Determinant calculation using the shortcut method: 0 2 0 + ( −3) ( −1) ( 5 ) + 1 ( −2 ) 7 −7 2 5 − ( −3) 1 0 − ( −1) ( −2 ) 0 = 15 − 14 − 70 = −69
(b) Solve for x1: 4 −3
7
0
2
−1
3 −2
0
−69
Solve for x2:
=
9 − 42 − 8 41 = 0.5942 −69 69
0 4
7
1 0 −1 5 3
0
−69
=
21 − 20 1 = − −0.0145 −69 69
Solve for x3: 0 −3 4 1
2
0
5 −2 3 −69
=
−8 − 40 + 9 39 13 = = = 0.5652 −69 69 23
(c) Original coefficient matrix and constant vector 0 −3 7 4 1 2 −1 0 5 −2 0 3
Swap rows 1 and 3 5 −2 0 3 1 2 −1 0 0 −3 7 4
reduce 2,1 element factor = 1/5 row 2 - 1/5 * row 1
5 −2 0 3 12 3 0 −1 − 5 5 0 −3 7 4 3,1 element already reduced Swap rows 2 and 3
5 −2 0 3 0 −3 7 4 12 3 −1 − 0 5 5
reduce 3,2 element factor = 12/5/(-3)=-4/5 5 −2 0 −3 0 0
row 3 - (-4/5)*row 2
0 3 7 4 23 13 5 5
Solve for x3:
x3 =
13 5 13 = 0.5652 5 23 23
Back-substitution Solve for x2: 1 13 1 x2 = − 4 − 7 = − −0.0145 3 23 69
Solve for x1:
1 1 41 x1 = 3 + 2 − = 0.5942 5 69 69
8.2 Solve the following system of equations by hand using naive Gaussian elimination. Carry fractions along in your calculations. Check that your answers satisfy the equations. 8 x1 + 2 x2 + 3 x3 = 30 x1 − 9 x2 + 2 x3 = 1 2 x1 + 3x2 + 6 x3 = 31 ===================================================================
8 2 3 30 1 −9 2 1 2 3 6 31
We will use the alternate scheme with separate normalization and reduction and the creation of the identity matrix with the back-substitution pass. Normalize the 1,1 pivot 1 1 4 1 −9 2 3
3 15 8 4 2 1 6 31
Reduce the 2,1 element by subtracting 1 * row 1 from row 2 1 3 15 1 4 8 4 0 − 37 13 − 11 4 8 4 3 6 31 2
Reduce the 3,1 element by subtracting 2 * row 1 from row 3 1 1 4 0 − 37 4 5 0 2
3 15 8 4 13 11 − 8 4 21 47 4 2
Normalize the 2,2 pivot 1 1 4 0 1 0 5 2
3 15 8 4 1 2 − 6 7 21 47 4 2
Reduce the 3,2 element by subtracting 5/2 * row 2 from row 3 1 1 4 0 1 0 0
3 15 8 4 1 2 − 6 7 421 842 74 37
Normalize the 3,3 pivot to solve for x3 1 1 4 0 1 0 0
3 15 8 4 1 2 − 6 7 1 4
x3 = 4
Reduce the 2,3 element to solve for x2 Subtract -1/6 * row 3 from row 2 1 1 4 0 1 0 0
3 15 8 4 0 1 1 4
x2 = 1
Reduce the 1,3 element by subtracting 3/8 * row 3 from row 1 1 0 0
1 4 1 0
9 0 4 0 1 1 4
Reduce the 1,2 element by subtracting 1/4 * row 2 from row 1 Solve for x1
1 0 0 2 0 1 0 1 0 0 1 4 x1 = 2
Solution check: 8 2 + 2 1 + 3 4 = 30 2 − 9 1 + 2 4 = 1
✓
2 2 + 3 1 + 6 4 = 31
8.3 Solve the following system of equations, shown in vector/matrix form, by hand using Gaussian elimination with pivoting. Comment on your results. What is the determinant of the coefficient matrix?
4 3 −1 6 7 −2 3 x = 9 5 −18 13 3 ===================================================================
4 3 −1 6 7 −2 3 9 5 −18 13 3
Swap rows 1 and 2 7 −2 3 9 4 3 −1 6 5 −18 13 3
Reduce element 2,1 factor = 4/7 row 2 - 4/7 * row 1 3 9 7 −2 0 29 7 −19 7 6 7 5 −18 13 3
Reduce element 3,1 factor = 5/7 row 3 - 5/7 * row 1 −2 3 9 7 0 29 7 −19 7 6 7 0 −116 7 76 7 − 24 7
Reduce element 3,2 factor = -116/7 / (29/7) = -4 row 3 - (-4) * row 2 3 9 7 −2 0 29 7 −19 7 6 7 0 0 0 0
No solution. Singular system. Equations are not independent. 8.4 Use the gausselimpivot1 function to solve the following system of equations. Report your solutions to four significant figures. Write Python code to test your solutions in the equations and comment on any roundoff errors.
−0.083x1 − 0.875 x2 + 0.645 x3 + 0.675 x4 = 62.1 −0.258 x1 − 0.730 x2 + 0.898 x3 − 0.797 x4 = 91.8 0.262 x1 − 0.467 x2 + 0.251x3 − 0.127 x4 = 74.9 0.582 x1 + 0.025 x2 − 0.907 x3 + 0.362 x4 = 130.9 ================================================================== import numpy as np def pivotswap(A,b,ipivot): n = len(b) Amax = abs(A[ipivot,ipivot]) # set maximnum to pivot location imax = ipivot for i in range(ipivot+1,n): # look for greater value below if abs(A[i,ipivot]) > Amax: Amax = abs(A[i,ipivot]) # reset maximum if found imax = i if imax != ipivot: # swap rows of A and b if needed for j in range(ipivot,n):
Atemp = A[ipivot,j] A[ipivot,j] = A[imax,j] A[imax,j] = Atemp btemp = b[ipivot] b[ipivot] = b[imax] b[imax] = btemp return A,b def gausselimpivot1(A,b,tol=1.e-15): n = len(b) # begin forward pass for i in range(n): # step down from row 0 through row n-1 # reduce below the i,i pivot element A,b = pivotswap(A,b,i) if abs(A[i,i]) < tol: return 'system of equations singular or ill-conditioned' for k in range(i+1,n): #step down from row i+1 through row n-1 factor = A[k,i]/A[i,i] # note: this will be skipped when i = n-1 for j in range(i+1,n): A[k,j] = A[k,j] - factor*A[i,j] b[k] = b[k] - factor*b[i] # begin back-substitution pass d = 1 for i in range(n-1,-1,-1): # step back up from row n-1 to row 1 d = d * A[i,i] b[i] = b[i]/A[i,i] for j in range(i-1,-1,-1): # back-substitute from row i-1 to 0 b[j] = b[j] - A[j,i]*b[i] return b,d A = np.array([[-0.083, -0.875, 0.645, 0.675], \ [-0.258, -0.730, 0.898, -0.797], \ [ 0.262, -0.467, 0.251, -0.127], \ [ 0.582, 0.025, -0.907, 0.362]]) b = np.array([62.1, 91.8, 74.9, 130.9]) xsoln,detr = gausselimpivot1(A,b) print(xsoln) print(detr) A1 = np.array([[-0.083, -0.875, 0.645, 0.675], \ [-0.258, -0.730, 0.898, -0.797], \ [ 0.262, -0.467, 0.251, -0.127], \ [ 0.582, 0.025, -0.907, 0.362]]) btest = A1.dot(xsoln) print(btest)
[ -58.36795272 -290.54260467 -221.69779443 0.2112572981580001 determinant [ 62.1 91.8 74.9 130.9] checks
-79.96183963]
solution
Note: Necessary to redefine A because gausselimpivot1 function modifies it.
8.5 A special matrix called the Hilbert matrix is famous for being ill-conditioned. Its form used in the following four equations in four unknowns is
1 1 2 1 3 1 4
1 2 1 3 1 4 2 9 7 1 3 1 4 1 5 x= 1 1 4 1 5 1 6 1 5 1 6 1 7 3 4
(a) Write Python code to compute the determinant of the coefficient matrix. What do you observe? (b) Write Python code to solve the system of equations using the inv function from the NumPy linalg module. (c) Write Python code to solve the system of equations using the solve function from the NumPy linalg module and compare your results to those from part (b). Write code to test your solutions. What do you conclude? =================================================================== (a) import numpy as np n = 4 A = np.zeros((n,n)) for i in range(n): for j in range(n): A[i,j] = 1/(i+j+1) print(A) d = np.linalg.det(A) print('\nDeterminant = {0:10.4e}'.format(d)) Determinant = 1.6534e-07
Determinant is a small number. Coefficient matrix somewhat ill-conditioned. (b) b = np.array([2., 9./7., 1., 3./4.])
Ainv = np.linalg.inv(A) xsoln1 = Ainv.dot(b) print('\nUsing inverse matrix') print(xsoln1) Using inverse matrix [ 12.71428571 -137.14285714
338.57142857 -220.
]
(c) print('\nUsing solve function') xsoln2 = np.linalg.solve(A,b) print(xsoln2) Using solve function [ 12.71428571 -137.14285714
338.57142857 -220.
]
Solutions for parts (b) and (c) are the same. Testing the solution: btest = A.dot(xsoln2) print(btest) [2.
1.28571429 1.
0.75
]
Solution checks. 8.6 A production process for three different electronic components uses three materials: copper, polystyrene, and neoprene. The amounts of these materials required to produce each component are presented in the table below.
Component A B C
Amount Required per Component Copper Polystyrene Neoprene (g) (g) (g) 15 17 19
3 4 5.5
1.2 1.5 1.9
If 98.5 kg of copper, 23.6 kg of polystyrene, and 8.77 kg of neoprene are available, how many of each component can be produced? Solve this problem using a Python script. Round your results down to the nearest integer since we are dealing with discrete components. ===================================================================
Formulate linear equations as 15 x A + 17 xB + 19 xC = 98500 3 A + 4 xB + 5.5 xC = 23600 1.2 x A + 1.5 xB + 1.9 xC = 8770
where xA, xB, and xC are the numbers of each component produced. import numpy as np A = np.array([[15, 17, 19],[3., 4.,5.5],[1.2, 1.5, 1.9]]) b = np.array([98500, 23600, 8770]) x = np.linalg.solve(A,b) n = np.floor(x) print('Number of components:') print('A: {0:4d}'.format(int(n[0]))) print('B: {0:4d}'.format(int(n[1]))) print('C: {0:4d}'.format(int(n[2]))) Number of components: A: 1533 B: 3100 C: 1199
8.7 Figure P8.7 described a bridge support or truss structure. The equations that determine the truss are horizontal and vertical force balances at the seven nodes. For example, at node 2, h2 + f 4 + sf 3 − sf 1 = 0 v2 + cf 1 + cf 3 = 0
where hi : vi : fj : s: c:
horizontal load applied at node i, positive to the right vertical load applied at node i, positive downward force in member j, negative for compression, positive for tension sin 30º cos 30º
The unknowns in the above equations are the member forces and the knowns are the loads. Six horizontal force balances for nodes 2 through 7 and five vertical force balances for nodes 2 through 6 give rise to 11 linear equations in the 11 member forces. Write a Python script that determines the member forces in this truss.
Figure P8.7 Bridge support truss structure. ====================================================================
First, derive the necessary linear equations: Node
Horizontal
Vertical
2
− s f1 + s f 3 + f 4 = h2
c f1 + c f 3 = −v2
3
− f 2 − s f3 + s f5 + f 6 = 0
−c f3 − c f5 = −v3
4
− f 4 − s f5 + s f 7 + f8 = 0
c f 5 + c f 7 = −v4
5
− f 6 − s f 7 + s f 9 + f10 = 0
−c f 7 − c f 9 = −v5
6
− f8 − s f 9 + s f11 = −h6
c f 9 + c f11 = −v6
7
− f10 − s f11 = 0
These are then arranged in matrix/vector form as follows:
−s c 0 0 0 0 0 0 0 0 0
0
s
1
0
0
0
0
0
0
c
0
0
0
0
0
0
−1 − s
0
s
1
0
0
0
0
−c
0
−c
0
0
0
0
0 0
0 0
−1 − s 0 c
0 0
s c
1 0
0 0
0
0
0
0
−1 − s
0
s
0
0
0
0
0
−c
0
−c
0
0
0
0
0
0
−1 − s
0
0
0
0
0
0
0
c
0
0
0
0
0
0
0
0
0 f1 −h2 0 0 f 2 −v2 0 0 f3 0 0 0 f 4 −v3 0 0 f5 0 0 0 f 6 = −v4 1 0 f7 0 0 0 f8 −v5 0 s f9 −h6 0 c f10 −v6 −1 − s f11 0 0
With the values provided for the loads, we can create a Python script to solve for the member forces. import numpy as np s = np.sin(np.radians(30)) c = np.cos(np.radians(30)) h2 = -3; v2 = 5; v3 = 7 ; v4 = 10 ; v5 = 8 ; v6 = 5 ; h6 = 6 A = np.array([[-s, 0, s, 1, 0, 0, 0, 0, 0, 0, 0], \ [ c, 0, c, 0, 0, 0, 0, 0, 0, 0, 0], \ [ 0,-1,-s, 0, s, 1, 0, 0, 0, 0, 0], \ [ 0, 0,-c, 0,-c, 0, 0, 0, 0, 0, 0], \ [ 0, 0, 0,-1,-s, 0, s, 1, 0, 0, 0], \ [ 0, 0, 0, 0, c, 0, c, 0, 0, 0, 0], \ [ 0, 0, 0, 0, 0,-1,-s, 0, s, 1, 0], \ [ 0, 0, 0, 0, 0, 0,-c, 0,-c, 0, 0], \ [ 0, 0, 0, 0, 0, 0, 0,-1,-s, 0, s], \ [ 0, 0, 0, 0, 0, 0, 0, 0, c, 0, c], \ [ 0, 0, 0, 0, 0, 0, 0, 0, 0,-1,-s]]) b = np.array([-h2,-v2,0,-v3,0,-v4,0,-v5,-h6,-v6,0]) f = np.linalg.solve(A,b) n = len(f) print('member','force (kN)') for i in range(n): print('{0:5d} {1:5.3f}'.format(i+1,f[i]))
Results:
member force (kN) 1 -19.015 2 12.507 3 13.241 4 -13.128 5 -5.158 6 21.707 7 -6.389 8 -12.513 9 15.626 10 10.700 11 -21.400
positive force for tension negative force for compression 8.8 The diagram presented in Figure P8.8 describes an electrical circuit comprised of resistors. You are to develop a Python script that solves for the voltages at nodes 2, 3, 6 and 7, and solves for the currents in all branches of the circuit. The values of the resistances in ohms are given in the table below: R12 R56 R23 R36 R48
60 50 10 75 20
R13 R57 R24 R68
60 50 50 60
R15 R67 R34 R78
10 10 50 60
There are two electrical laws that give rise to the equations necessary to solve this circuit. (1) Ohm’s Law states that the current through a resistive branch is proportional to the voltage across the branch and inversely proportional to the resistance of the branch. For the diagram below, A
RAB
VA
B
VB
and
iAB =
VA − VB RAB
iAB (2) Kirchoff’s Law states that the sum of currents at a node must be equal to zero. For the diagram below,
iA iC
iB
and
iA = iB + iC
By writing node equations (Kirchoff’s law) for nodes 2, 3, 6 and 7, and substituting Ohm’s Law relationships for the currents in the equations, one can develop a set of four linear equations in four unknowns, the voltages at the nodes. These equations are solved; then, with the voltages known, Ohm’s Law can be used to compute currents for all 13 branches in the circuit.
Figure P8.8 Electrical circuit comprised of resistors. ====================================================================
First, derive the Kirchoff's Law current equations: Node 2: i12 − i24 − i23 = 0
Node 3: i13 + i23 − i34 − i36 = 0
Node 6: i36 + i56 − i67 − i68 = 0
Node 7: i57 + i67 − i78 = 0
Second, substitute Ohm's Law voltage expresions for the currents: Node 2:
V1 − V2 V2 − V4 V2 − V3 − − =0 R12 R24 R23
Node 3:
V1 − V3 V2 − V3 V3 − V4 V3 − V6 + − − =0 R13 R23 R34 R36
Node 6:
V3 − V6 V5 − V6 V6 − V7 V6 − V8 + − − =0 R36 R56 R67 R68
Node 7:
V5 − V7 V6 − V7 V7 − V8 + − =0 R57 R67 R78
Gather terms in the voltages to formulate the set of linear equations: 1 1 V1 V4 1 1 − − − − V2 + V3 = − R R R R R R24 24 23 12 12 23 1 1 1 V1 V4 1 1 1 − − − − V2 + − V3 + V6 = − R13 R34 R23 R13 R23 R34 R36 R36 1 1 1 V5 V8 1 1 1 − − − − V3 + − V6 + V7 = − R56 R68 R36 R36 R56 R67 R68 R67 1 1 V5 V8 1 1 − − − V6 + − V7 = − R57 R78 R67 R57 R67 R78
Rearrange these in matrix/vector format: 1 1 1 1 V1 V4 − − 0 0 − − R − R R23 R12 R24 R23 24 12 1 1 1 1 1 1 V V V 2 1 4 − − − 0 − − − V3 R13 R34 R23 R13 R23 R34 R56 R36 = 1 1 1 1 1 1 V6 − V5 − V8 0 − − − − V7 R56 R68 R36 R36 R56 R67 R68 R67 V V 5 1 1 1 1 − 8 − 0 0 − − − R57 R78 R67 R57 R67 R78
Given the values provided for the resistors and for the voltages at nodes 1, 4, 5, and 8, we can now create a Python script to solve the problem. import numpy as np R12 = 60; R13 = 60; R15 = 10 R56 = 50; R57 = 50; R67 = 10 R23 = 10; R24 = 50; R34 = 50 R36 = 75; R68 = 60; R78 = 60 R48 = 20
V1 = 100; V5 = 80; V4 = 0; V8 = 0 A = np.array([[-1/R12-1/R24-1/R23, 1/R23, 0, 0], \ [1/R23, -1/R13-1/R23-1/R34-1/R56, 1/R56, 0], \ [0, 1/R36, -1/R36-1/R56-1/R67-1/R68, 1/R67], \ [0,0,1/R67,-1/R57-1/R67-1/R78]]) b = np.array([-V1/R12-V4/R24, -V1/R13-V4/R34, \ -V5/R56-V8/R68, -V5/R57--V8/R78]) V = np.linalg.solve(A,b) V2 = V[0]; V3 = V[1]; V6 = V[2]; V7 = V[3] print('Node voltages in volts') print('V2 ={0:6.2f}'.format(V[0])) print('V3 ={0:6.2f}'.format(V[1])) print('V6 ={0:6.2f}'.format(V[2])) print('V7 ={0:6.2f}'.format(V[3])) i12 = (V1-V2)/R12; i23 = (V2-V3)/R23; i24 = (V2-V4)/R24 i13 = (V1-V3)/R13; i34 = (V3-V4)/R34; i36 = (V3-V6)/R36 i56 = (V5-V6)/R56; i57 = (V5-V7)/R57; i67 = (V6-V7)/R67; i68 = (V6-V8)/R68; i78 = (V7-V8)/R78; i15 = (V1-V5)/R15; i48 = (V4-V8)/R48 print('\nBranch currents in amps') print('i12 ={0:6.2f}'.format(i12)) print('i23 ={0:6.2f}'.format(i23)) print('i24 ={0:6.2f}'.format(i24)) print('i13 ={0:6.2f}'.format(i13)) print('i34 ={0:6.2f}'.format(i34)) print('i36 ={0:6.2f}'.format(i36)) print('i56 ={0:6.2f}'.format(i56)) print('i57 ={0:6.2f}'.format(i57)) print('i67 ={0:6.2f}'.format(i67)) print('i68 ={0:6.2f}'.format(i68)) print('i78 ={0:6.2f}'.format(i78)) print('i15 ={0:6.2f}'.format(i15)) print('i48 ={0:6.2f}'.format(i48)) Node voltages in volts V2 = 45.18 V3 = 45.08 V6 = 43.89 V7 = 43.82 Branch currents in amps i12 = 0.91 i23 = 0.01 i24 = 0.90 i13 = 0.92
i34 = i36 = i56 = i57 = i67 = i68 = i78 = i15 = i48 =
0.90 0.02 0.72 0.72 0.01 0.73 0.73 2.00 0.00
8.9 For the following system of nonlinear equations, x12 + 3 x22 − 5 = 0 x12 − x2 − 1 = 0
(a) Employ successive substitution to obtain a solution. Use initial estimates x1 = x2 = 1.5. (b) Solve the equations using the multivariable Newton-Raphson method with the same initial estimates. Use Python scripts to solve this problem. ===================================================================
(a) Formulate the equations for substitution. Solve the second equation for x1 and the first equation for x2. (Doing it the other way around appears to create an unstable scheme.)
x1 = x2 + 1 x2 =
5 − x12 3
Create the Python script using the fixpt function. import numpy as np def fixpt(g,x1,Ea=1.e-7,maxit=30): for i in range(maxit): # carry out maxit iterations x2 = g(x1) # calculate new x if abs((x2-x1)/x2) < Ea: # check if converged iter = i+1 return x2,iter # if converged, return xnew and iterations x1 = x2 # if not converged, replace x1 with x2 iter = i+1 return x2,iter # here if maxit reached
def g1(x2): return np.sqrt(x2+1) def g2(x2): return np.sqrt((5-g1(x2)**2)/3) x2 = 0.5 x2soln,iter = fixpt(g2,x2) print('x1 = {0:6.4f}'.format(g1(x2soln))) print('x2 = {0:6.4f}'.format(x2soln)) print('number of iterations taken = {0:3d}'.format(iter)) x1 = 1.4142 x2 = 1.0000 number of iterations taken =
10
The solution is evidently x1 = 2 x2 = 1
(b) First, derive the Jacobian matrix: 2 x 6 x2 J = 1 2 x1 −1
Now, create the Python script: import numpy as np def multinewt(f,J,x0,Ea=1.e-12,maxiter=30,decel=1.0): xold = x0 for iter in range(maxiter): Jinv = np.linalg.inv(J(xold)) xnew = xold - decel*Jinv.dot(f(xold)) xdev = xnew - xold xerr = xdev.dot(xdev) if xerr < Ea: break xold = xnew return xnew,iter+1 def f(x): fn = np.zeros(2) fn[0] = x[0]**2 +3*x[1]**2 -5
fn[1] = x[0]**2 -x[1] - 1 return fn def J(x): Jac = np.zeros((2,2)) Jac[0,0] = 2*x[0] Jac[0,1] = 6*x[1] Jac[1,0] = 2*x[0] Jac[1,1] = -1 return Jac x0 = np.array([0.5, 0.5]) xsoln,niter = multinewt(f,J,x0) print(xsoln) print(niter) [1.41421356 1. 6
]
Confirms the results from part (a).
8.10 Solve the following set of nonlinear equations, x12 − x1 + x2 − 0.75 = 0 x12 − 5 x1 x2 − x2 = 0
(a) using the multivariable Newton-Raphson method, and (b) using the root function from the SciPy optimize submodule. For both parts, try initial estimates {0.5,0.5} and {1.5,1.5} and comment on your results. ==================================================================== (a)
First, derive the Jacobian matrix: 2 x1 − 1 2 x − 5x 1 2
−5 x1 − 1 1
Next, create the Python script: import numpy as np def multinewt(f,J,x0,Ea=1.e-12,maxiter=30,decel=1.0): xold = x0 for iter in range(maxiter): Jinv = np.linalg.inv(J(xold)) xnew = xold - decel*Jinv.dot(f(xold)) xdev = xnew - xold xerr = xdev.dot(xdev) if xerr < Ea: break xold = xnew return xnew,iter+1 def f(x): fn = np.zeros(2) fn[0] = x[0]**2 - x[0] + x[1] - 0.75 fn[1] = x[0]**2 - 5*x[0]*x[1] - x[1] return fn def J(x): Jac = np.zeros((2,2)) Jac[0,0] = 2*x[0]-1 Jac[0,1] = 1 Jac[1,0] = 2*x[0]-5*x[1] Jac[1,1] = -5*x[0]-1
return Jac x0 = np.array([0.5, 0.5]) xsoln,niter = multinewt(f,J,x0) print(xsoln) print(niter) [-0.5852738 9
-0.17781921]
x0 = np.array([1.5, 1.5]) xsoln,niter = multinewt(f,J,x0) print(xsoln) print(niter) [1.37206541 0.23950193] 5
Evidently, there are multiple solutions. (b) import numpy as np from scipy.optimize import root def f(x): fn = np.zeros(2) fn[0] = x[0]**2 - x[0] + x[1] - 0.75 fn[1] = x[0]**2 - 5*x[0]*x[1] - x[1] return fn x0 = np.array([0.5, 0.5]) result = root(f,x0) print(result) print(result.x) fjac: array([[ 0.42581703, 0.90480929], [-0.90480929, 0.42581703]]) fun: array([1.13242749e-14, 1.65423231e-14]) message: 'The solution converged.' nfev: 15 qtf: array([ 8.24169686e-11, -1.34127759e-11]) r: array([-3.56575219, -0.01299748, -0.87131087]) status: 1 success: True x: array([-0.18679161, 0.52831729]) [-0.18679161 0.52831729] import numpy as np from scipy.optimize import root
def f(x): fn = np.zeros(2) fn[0] = x[0]**2 - x[0] + x[1] - 0.75 fn[1] = x[0]**2 - 5*x[0]*x[1] - x[1] return fn x0 = np.array([1.5, 1.5]) result = root(f,x0) print(result) print(result.x) fjac: array([[-0.49343364, 0.86978345], [-0.86978345, -0.49343364]]) fun: array([-7.84927678e-14, 1.87128091e-12]) message: 'The solution converged.' nfev: 12 qtf: array([ 6.31390403e-10, -3.23856140e-10]) r: array([-3.96075405, -8.99354583, 3.86194152]) status: 1 success: True x: array([1.37206541, 0.23950193]) [1.37206541 0.23950193]
Note that for the {0.5,0.5} initial estimates, root finds a different solution than Newton-Raphson. Yet, for {1.5,1.5}, it finds the same solution. 8.11 Solve the following set of nonlinear equations to determine positive roots. x12 − x2 + 1 = 0
2 cos ( x1 ) − x2 = 0
(a) using successive substitution with the wegstein1 function from Chapter 7, (b) using the multivariable Newton-Raphson method, and (c) using the root function from Python's SciPy optimize submodule. Use a phase plot (x2 vs. x1) of the two equations to determine suitable initial estimates. =================================================================== Formulate equations for plotting x2 vs. x1: x2 = x12 + 1
x2 = 2 cos ( x1 )
import numpy as np import matplotlib.pyplot as plt
x1 = np.linspace(-2.5,2.5,100) f1 = lambda x1: x1**2 +1 f2 = lambda x1: 2*np.cos(x1) plt.plot(x1,f1(x1),c='k',label='Equation 1') plt.plot(x1,f2(x1),c='k',ls='--',label ='Equation 2') plt.grid() plt.xlabel('x1') plt.ylabel('x2') plt.legend()
Two roots evident at {0.7,1.5} and {-0.7,1.5}. Try initial estimates {1,2} for the root to the right. (a) Rearrange equations for successive substitution: x1 = x2 − 1 x2 = 2 cos ( x1 )
Python script using the wegstein1 function: import numpy as np def wegstein(g,x1,x2,Ea=1.e-7,maxit=30): for i in range(maxit): # carry out maxit iterations x3 = (x1*g(x2)-x2*g(x1))/(x1-x2+g(x2)-g(x1)) # calculate new x if abs((x3-x2)/x3) < Ea: # check if converged iter = i+1 return x3,iter # if converged, return xnew and iterations
x1 = x2 # if not converged, replace x1 with x2 x2 = x3 # and replace x2 with x3 iter = i+1 return x3,iter # here if maxit reached def g1(x2): return np.sqrt(x2-1) def g2(x2): return 2*np.cos(g1(x2)) x20 = 2 ; x21 = 1.9 x2soln,iter = wegstein(g2,x20,x21) print('x1 = {0:6.4f}'.format(g1(x2soln))) print('x2 = {0:6.4f}'.format(x2soln)) print('number of iterations taken = {0:3d}'.format(iter)) x1 = 0.7146 x2 = 1.5107 number of iterations taken =
4
(b) Derive the Jacobian matrix. −1 2 x1 −2sin x ( 1 ) −1 import numpy as np def multinewt(f,J,x0,Ea=1.e-12,maxiter=30,decel=1.0): xold = x0 for iter in range(maxiter): Jinv = np.linalg.inv(J(xold)) xnew = xold - decel*Jinv.dot(f(xold)) xdev = xnew - xold xerr = xdev.dot(xdev) if xerr < Ea: break xold = xnew return xnew,iter+1 def f(x): fn = np.zeros(2) fn[0] = x[0]**2 - x[1] + 1 fn[1] = 2*np.cos(x[0]) - x[1] return fn def J(x): Jac = np.zeros((2,2))
Jac[0,0] = 2*x[0] Jac[0,1] = -1 Jac[1,0] = -2*np.sin(x[0]) Jac[1,1] = -1 return Jac x0 = np.array([2.0, 1.9]) xsoln,niter = multinewt(f,J,x0) print(xsoln) print(niter) [0.71462106 1.51068326] 5
Confirms result from (a). Takes one more iteration. (c) import numpy as np from scipy.optimize import root def f(x): fn = np.zeros(2) fn[0] = x[0]**2 - x[1] + 1 fn[1] = 2*np.cos(x[0]) - x[1] return fn x0 = np.array([2.0, 1.9]) result = root(f,x0) print(result) print(result.x) fjac: array([[-0.74770739, 0.66402836], [-0.66402836, -0.74770739]]) fun: array([ 3.66373598e-15, -1.77635684e-15]) message: 'The solution converged.' nfev: 12 qtf: array([-1.16817219e-10, -3.78428278e-11]) r: array([-2.03447192, -0.11682036, 1.34678925]) status: 1 success: True x: array([0.71462106, 1.51068326]) [0.71462106 1.51068326]
Confirms the result from (a) and (b). Takes 12 iterations.
8.12 The following chemical reactions take place in equilibrium at a fixed temperature and pressure. 2A + B
C
A+D
C
The equilibrium of each reaction is described in terms of concentrations of the reactants and two equilibrium constants, K1 and K2. K1 =
cc ca2 cb
K2 =
cc ca cd
If x1 and x2 represent the number of moles of C that are produced by the first and second reactions, respectively, reformulate the equilibrium relationships based on the initial concentrations of the constituents before equilibrium is established. For example, cc = cc,0 + x1 + x2
and
ca = ca,0 – 2x1 – x2
Solve the resulting nonlinear algebraic equations for the following parameter values: K1 = 410–4, K2 = 3.710–2, ca,0 = 50, cb,0 = 5, cc,0 = 0, cd,0 = 10. Use the root function from the Python SciPy optimize submodule to solve the equations. Report the x1 and x2 solutions and the values of ca, cb, cc, and cd. ==================================================================== The remaining two relationships are cb = cb ,0 − x1 cd = cd ,0 − x2
Substitute the four relationship into the equilibrium equations:
K1 =
cc ,0 + x1 + x2
(c − 2x − x ) (c − x ) 2
a ,0
1
2
b ,0
1
K2 =
cc ,0 + x1 + x2
( c − 2 x − x )( c a ,0
These are rearranged into two nonlinear equations:
cc ,0 + x1 + x2
(c − 2x − x ) (c − x ) 2
a ,0
1
2
b ,0
cc ,0 + x1 + x2
( c − 2 x − x )( c a ,0
1
2
d ,0
− K1 = 0
1
− x2 )
− K2 = 0
1
2
d ,0
− x2 )
Given the data from the problem statement, we can create the Python script. import numpy as np from scipy.optimize import root K1 = 4e-4 ; K2 = 3.7e-2 ca0 = 50 ; cb0 = 5 ; cc0 = 0 ; cd0 = 10 def f(x): x1 = x[0] ; x2 = x[1] fn = np.zeros(2) fn[0] = (cc0+x1+x2)/(ca0-2*x1-x2)/(cb0-x1) fn[1] = (cc0+x1+x2)/(ca0-2*x1-x2)/(cd0-x2) return fn x0 = np.array([1,1]) result = root(f,x0) print(result) print(result.x) x1 = result.x[0] x2 = result.x[1] print('ca = {0:7.2f}'.format(ca0-2*x1-x2)) print('cb = {0:7.2f}'.format(cb0-x1)) print('cc = {0:7.2f}'.format(cc0+x1+x2)) print('cd = {0:7.2f}'.format(cd0-x2)) and the results are fjac: array([[-0.9668184 , -0.25546462], [ 0.25546462, -0.9668184 ]]) fun: array([9.45410851e-18, 4.20182598e-18]) message: 'The solution converged.' nfev: 9 qtf: array([-9.77794906e-12, -1.57691761e-12]) r: array([-0.00678423, -0.00379946, -0.00109902]) status: 1 success: True x: array([ 0.3846154, -0.3846154]) [ 0.3846154 -0.3846154] ca = 49.62 cb = 4.62 cc = 0.00 cd = 10.38
Chapter 9 9.1 Indicate which solution strategy would be appropriate for the following differential equations. (a)
dy et = dt 1 + et
y ( 0 ) = 0.5
0 t 1
(b)
2 dy = x y e− x dx
y ( 0) = 1
0 x 1
(c)
d2y = −9 y dt 2
y ( 0) = 1
dy (0) = 0 dt
(d)
d2y dy + 2x =0 2 dx dx
y ( 0) = 1
0t 4
y (10 ) = 0.1
=================================================================== (a) This equation is separable because the right-hand side is only a function of the independent variable, t. Quadrature would be an appropriate numerical method to find a solution. (b) This equation is separable and can be rearranged as
x 2 dy = ln ( y ) − ln ( y0 ) = ln ( y ) = xe − x dx y0 y 0 y
and quadrature can be applied to the right-hand side. The dependent variable, y, can then be determined by taking the exponent of the right-hand side result. (c) This second-order ODE can be decomposed into two first-order ODEs as
dy = y1 dt dy1 = −9 y dt
y ( 0) = 1 y1 ( 0 ) = 0
and solved numerically using methods like Euler, Heun, and solve_ivp. It might be mentioned that there is an analytical solution to these equations. (d) This second-order ODE can be decomposed into two first-order ODEs, but there is no initial condition for the first derivative.
dy = y1 dx dy1 = −2 xy1 dx
y ( 0) = 1
y (10 ) = 0.1
This is then a two-point boundary value problem. One approach to solving the equations would be to guess an initial value for y1, solve the equations numerically to x = 10, and check the value of y there to see if it is equal to 0.1. Then adjust the initial value of y1 by trial-and-error until the condition at x = 10 is met. There are more sophisticated ways of doing this also.
9.2 Draw information flow diagrams for the equations in parts (c) and (d) in Problem 9.1. =================================================================== (c)
dy ( 0) dt
y ( 0) dy dt
d2y = −9 y dt 2
y (t )
(d) Note: not specified
d2y dy = −2 x dx 2 dx
dy ( 0) dx
y ( 0) dy dx
y ( x)
It is noted in the diagram that the initial value of dy/dx is not known and must be estimated.
9.3 Hand draw a plot on graph paper of the function f ( t ) = ln ( 5 − 4cos ( x ) )
over the domain 0 x . Using an interval of x = 0.2, employ the counting the squares method to produce low and high estimates of
f ( x ) dx 0
and then average the two results to obtain your final estimate. ====================================================================
9.4 Employ trapezoidal rule to compute the integral in Problem 9.3. Use Python to compare the results from the chapter's trap function with 16 intervals and Python's built-in quad function. =================================================================== import numpy as np f = lambda x: np.log(5-4*np.cos(x)) def trap(f,a,b,n=100): x = a # set x to left side a h = (b-a)/n # compute interval width sm = f(a) # first term of sum for i in range(n-1): x = x + h # advance x sm = sm + 2*f(x) # add 2 * f(x) to sum sm = sm + f(b) # add last term to sum ar = sm*h/2 # complete integral formula return ar y = trap(f,0,np.pi,n=16) print('trapezoidal rule = {0:5.3f}'.format(y)) trapezoidal rule = 4.355 import numpy as np from scipy.integrate import quad f = lambda x: np.log(5-4*np.cos(x)) a = 0 b = np.pi y,ey = quad(f,a,b) print('quad function = {0:5.3f}'.format(y)) quad function = 4.355
Same results for both methods. Note: The manual result from Problem 9.3 was 4.214. The error is 4.214 − 4.355 100% 3.2% 4.355
9.5 Many vessels (tanks) that store liquids have a circular cross-section, but the radius (or diameter) and cross-sectional area vary with height. This is illustrated in Figure P9.5. A practical way of calculating the volume of the vessel is to know the radius, r, as a function of height, h, and then use the integral and associated formula for area, A. V = A ( h ) dh H
0
A(h) = r 2 (h)
The relationship, r(h), might be obtained from measurements or an analytical formula used to design the vessel. Consider a vessel with r ( h ) = 8 + 0.34 h − 0.002 h2
cm
and H = 50 cm
Use Python and the method of your choice to compute the volume of this vessel. Display the result in liters.
Figure P9.5 Vessel with circular cross-section.
=================================================================== import numpy as np from scipy.integrate import quad H = 50 r = lambda h: 8+0.34*h-0.002*h**2 f = lambda h: np.pi*r(h)**2 V,er = quad(f,0,H) print('Volume = {0:6.2f} liters'.format(V/1000)) Volume =
36.47 liters
9.6 An important task in public utilities and industry is the totalization of flow of materials, whether gases, liquids, or solids. Measurements are often made of the flow rate of a material, but monetary charges are calculated for the total volume of material that flows over a given time interval. The relationship is given by an integral,
V = q ( t ) dt tf
t0
where
q(t) is the flow rate, for a liquid in units like liters/min or m3/s, and V is the volume that flowed over the interval from t0 to tf.
Flow rate data are typically samples collected at equal intervals of time. The data in the table below were collected for a liquid chemical that is transferred from one company to another through a pipeline. Use the built-in Python function trapz to totalize the flow for these data. Time (min)
Flow Rate (L/min)
0 5 10 15 20 25 30 35 40 45 50
17.0 16.6 16.3 16.1 17.1 16.9 16.8 17.4 17.1 17.0 16.7
Time (min)
Flow Rate (L/min)
Time (min)
Flow Rate (L/min)
55 60 65 70 75 80 85 90 95 100
17.4 17.2 17.4 17.4 17.0 17.3 17.2 17.4 16.8 17.1
105 110 115 120 125 130 135 140 145 150
17.4 17.4 17.5 17.4 17.6 17.4 17.3 17.0 17.8 17.5
================================================================ import numpy as np
def trapdata(t,y): n = len(t) sm = 0 for i in range(n-1): ar = (y[i]+y[i+1])/2*(t[i+1]-t[i]) sm = sm + ar return sm t,q = np.loadtxt('Problem9-6.csv',delimiter=',',unpack=True) V = trapdata(t,q) print('Volume of flow = {0:7.5g} L'.format(V)) Volume of flow =
2571.2 L
9.7 For the differential equation,
dy = −0.2 y 2 dt
y ( 0) = 3
(a) Compute by hand one step of the Euler method for t = 1. (b) Compute by hand one step of the Heun method for t = 1, including five iterations of the predictor-corrector procedure. Does the method appear to be converging? =================================================================== (a)
y (1) y ( 0 ) +
dy ( 0 ) t = 3 − 1.8 = 1.2 dt
(b) y (1) = y ( 0 ) + f ( 0,3) t f ( 0,3) = −1.8 y (1) = 1.2 f ( 0,3) + f (1,3) −1.8 − 0.288 = −1.044 2 2 y 2 (1) = y ( 0 ) + f 1t = 3 − 1.044 1.956 f1 =
f ( 0,3) + f (1,1.956 ) −1.282 2 y 3 = 3 − 1.282 1.717 f2=
f 3 −1.195 y 4 = 3 − 1.195 1.805 f 4 = −1.226 y 5 = 1.774
See below. The values of y appear to be converging. The process eventually converges to y(1) 1.782. For this step size, the Euler method has significant error.
9.8 A famous second-order differential equation, called the van der Pol equation, is d2y dy = (1 − y 2 ) − y 2 dt dt
For the initial conditions, y(0) =1 and dy/dt(0) = 0, use the Euler method to solve the equation over the domain 0 t 10. Find a step size, t, that yields an accurate solution. Produce a plot of your results. =================================================================== Formulate two first-order differential equations.
dy = y1 dt dy1 = (1 − y 2 ) y1 − y dt
y (0) = 1 y1 ( 0 ) = 0
import numpy as np import matplotlib.pyplot as plt def eulerm(f,y0,t0,tf,h): n = len(y0) # number of equations m = int((tf-t0)/h)+1 # number of intervals t = np.zeros(m) # empty t array y = np.zeros((m,n)) # empty two-dimensional y array y[0,:] = y0 # initial conditions assigned to y array for i in range(1,m): # loop to step through solution t[i] = t0 + i*h # t at end of current interval if t[i] > tf: # check whether tf is exceeded t[i] = tf # if so, set t[i] equal to tf h = t[i]-t[i-1] # and adjust h y[i] = y[i-1,:] + f(t[i-1],y[i-1,:])*h # Euler step if t[i] >= tf: break # out of while loop when done
return t,y def f(t,y): dy = np.zeros(2) dy[0] = y[1] dy[1] = (1-y[0]**2)*y[1]-y[0] return dy t0 = 0 tf = 10. y0 = np.array([1., 0.]) h = 0.01 t,y = eulerm(f,y0,t0,tf,h) plt.plot(t,y[:,0],c='k') plt.grid() plt.xlabel('t') plt.ylabel('y')
9.9 Solve this differential equation using the Python built-in solve_ivp function.
dy = −750 y − 1100 e−2t dt
y ( 0) = 5
0t 5
Compare the performance of the default RK45 method and the LSODA method. Adjust the error tolerances if needed. Produce a plot of your results. ====================================================================
RK45 -- the default method
import numpy as np from scipy.integrate import solve_ivp import matplotlib.pyplot as plt def f(t,y): return -750*y - 1100*np.exp(-2*t) tf = 5. tspan = [0., tf] teval = np.linspace(0.,tf,100) y0 = [5] result = solve_ivp(f,tspan,y0,t_eval=teval) tm = result.t ym = result.y[0,:] plt.plot(tm,ym,c='k') plt.grid() plt.xlabel('t') plt.ylabel('y')
LSODA method result = solve_ivp(f,tspan,y0,t_eval=teval,method='LSODA')
Plot is identical.
9.10 Figure P9.10 is a schematic diagram of a simple electrical circuit with five components, a battery source, on-off switch, coil or inductor, resistor, and capacitor.
Figure P9.10 Electrical circuit.
When the switch is closed, the voltage shown initially as V0, changes. A differential equation describes this: LC
d 2V dV + RC +V = 0 2 dt dt
V ( 0 ) = V0
dV (0) = 0 dt
Solve this equation for 0 t 0.2 seconds, V0 = 100 volts, and the parameter values3 C = 2×10−6 farads
L = 0.5 henries
R = 100 ohms
Compare the solution using the eulerm function with a step size, t = 0.0001 seconds, and the solve_ivp built-in function. Show the comparison on a plot. ================================================================== Rearrange the second-order differential equation: d 2V R dV 1 =− − V 2 dt L dt LC
Convert this second-order equation into two first-order equations:
3
One ohm () is the resistance that provides a current of one ampere (A) if the voltage difference across the resistor is one volt (V). The henry is a unit of inductance that corresponds to the inductance in a circuit producing one volt of potential caused by a current variation of one ampere (A) per second. One farad is the capacitance unit which, when charged with one coulomb, provides a potential difference of one volt (V).
dV = V1 dt dV1 R 1 = − V1 − V dt L LC
V ( 0 ) = V0 V1 ( 0 ) = 0
import numpy as np import matplotlib.pyplot as plt from scipy.integrate import solve_ivp def eulerm(f,y0,t0,tf,h): n = len(y0) # number of equations m = int((tf-t0)/h)+1 # number of intervals t = np.zeros(m) # empty t array y = np.zeros((m,n)) # empty two-dimensional y array y[0,:] = y0 # initial conditions assigned to y array for i in range(1,m): # loop to step through solution t[i] = t0 + i*h # t at end of current interval if t[i] > tf: # check whether tf is exceeded t[i] = tf # if so, set t[i] equal to tf h = t[i]-t[i-1] # and adjust h y[i] = y[i-1,:] + f(t[i-1],y[i-1,:])*h # Euler step if t[i] >= tf: break # out of while loop when done return t,y C = 2.e-6 L = 0.5 R = 100 V0 = 100 def f(t,y): dy = np.zeros(2) V = y[0] V1 = y[1] dy[0] = V1 dy[1] = -R/L*V1 -V/L/C return dy t0 = 0 tf = 0.2 y0 = np.array([2., 0.]) h = 0.0001 t,y = eulerm(f,y0,t0,tf,h) tspan = [0., tf] teval = np.linspace(0.,tf,200) result = solve_ivp(f,tspan,y0,t_eval=teval) tm = result.t ym = result.y[0,:]
plt.plot(t,y[:,0],c='k',label='eulerm') plt.plot(tm,ym,c='r',label='solve_ivp') plt.grid() plt.xlabel('t') plt.ylabel('V') plt.legend()
There is significant error with the Euler method. 9.11 In the chapter, we extended the euler function to its eulerm counterpart with the ability to solve multiple differential equations. In a similar fashion, extend the heun function to a new function called heunm that will solve multiple differential equations. Solve the Equations 9.8 using your heunm function and confirm the solution by comparison to Figure 9.23. Hint: The criterion to determine the convergence of the corrector step has to change from the heun function because there are multiple equations and thus multiple errors. One way to manage this is to use the maximum absolute error as the criterion. Another is to use the sum of squares of the errors of the equations. ==================================================================== import numpy as np import matplotlib.pyplot as plt def sumsq(a): n = len(a) smsq = 0 for i in range(n): smsq = smsq + a[i]**2
return smsq def heunm(f,y0,t0,tf,h,ea=1.e-7): n = len(y0) # number of equations m = int((tf-t0)/h) # number of intervals t = np.zeros(m) # initialize empty arrays y = np.zeros((m,n)) y1 = np.zeros(n) fbar = np.zeros(n) y2 = np.zeros(n) er = np.zeros(n) zs = np.zeros((1,n)) y[0,:] = y0 # set initial conditions for i in range(1,m): # set time array values t[i] = i*h if t[m-1] < tf: # check for extra interval t = np.append(t,tf) y = np.append(y,zs,axis=0) for i in range(m): # loop to step through intervals h = t[i+1]-t[i] # current step size y1 = y[i,:] + f(t[i],y[i,:])*h # Euler step while True: # loop for Heun convergence fbar = (f(t[i],y[i,:])+f(t[i+1],y1))/2 # updated derivatives y2 = y[i,:] + fbar*h # updated Euler step er = abs((y1-y2)/y2) # compute errors if np.sqrt(sumsq(er)) < ea: break # test for convergence y1 = y2 # not converged -- loop back y[i+1,:] = y2 #converged, on to next step return t,y def f(t,y): dy = np.zeros(2) dy[0] = -2*y[0]**2 + 2*y[0] + y[1] -1 dy[1] = -y[0] -3*y[1]**2 +2*y[1] + 2 return dy t0 = 0 tf = 2.0 y0 = np.array([2., 0.]) h = 0.001 t,y = heunm(f,y0,t0,tf,h) plt.plot(t,y[:,0],c='k',label='x1') plt.plot(t,y[:,1],c='k',ls='--',label='x2') plt.grid() plt.xlabel('t') plt.ylabel('x1 and x2') plt.legend()
9.12 The second-order differential equation, d 2 y 1 dy = +y dt 2 4 dt
y ( 0) = 5
y (10 ) = 8
0 t 10
You will note that there is no initial condition for dy/dt but rather a final condition for y. To solve this problem, we suggest that you guess an initial condition for dy/dt. Then solve the equation with solve_ivp. Check your solution's value of y(10) versus the requirement of 8. Then, adjust dy/dt(0), and, by trial-and-error, find a solution that matches closely the y(10) = 8 requirement. This is a manual strategy to solve a two-point boundary value problem. There are more sophisticated, automatic techniques, but these are not requested here. ==================================================================== Develop two first-order differential equations.
dy = y1 y ( 0) = 5 y (10 ) = 8 dt dy1 1 = y1 + y y1 ( 0 ) = estimate dt 4 import numpy as np from scipy.integrate import solve_ivp import matplotlib.pyplot as plt def f(t,y): dy = np.zeros(2)
dy[0] = y[1] dy[1] = y[1]/4 + y[0] return dy tf = 10. tspan = [0., tf] teval = np.linspace(0., tf) y1est = -4.41367 y0 = np.array([5., y1est]) result = solve_ivp(f,tspan,y0,t_eval=teval) tm = result.t y = result.y[0,:] y1 = result.y[1,:] plt.plot(tm,y,c='k',label='y') plt.plot(tm,y1,c='k',ls='--',label='dy/dt') plt.grid() plt.xlabel('t') plt.ylabel('y and dy/dt') plt.legend()
Results are very sensitive to the initial value of dy/dt. The value determined by trial-and-error is
dy ( 0 ) = −4.41367 dt and
y ( 5) 9.94
The final condition on y is met, approximately.
9.13 Differential equations are important in structural engineering. Figure P9.13 illustrates a cantilever beam (beam fixed at one end) subject to a load (force) at its end. The profile of deflection of the beam is described by a second-order differential equation,
d2y F =− ( L − x) 2 dx EI where
y ( 0) = 0
dy ( 0) = 0 dx
0 x L
y : vertical displacement of the beam at a given value of x, m, x : location on the beam from 0 to L, m, F: downward load placed at the beam end, N, E: modulus of elasticity (stiffness) of the beam material, Pa, I: moment of inertia of the beam cross-section, m4, and L: length of the beam, m.
For the following parameter values and conditions, L = 4 m, F = 5,000 N, E = 5×1011 Pa, I = 4×10−4 m4 , solve the differential equation using a numerical method of your choice. It turns out that there is an analytical solution to this differential equation, y=−
FL 2 F 3 x + x 2E I 6E I
Plot and compare the analytical result along with your numerical solution.
Figure P9.13 Deflection of cantilever beam under a load placed at the end.
===================================================================
Formulate two first-order differential equations.
dy = y1 dx dy1 F = − ( L − x) dx EI
y (0) = 0 y1 ( 0 ) = 0
Here is the Python script: import numpy as np from scipy.integrate import solve_ivp import matplotlib.pyplot as plt L = 4 F = 5000 E = 5e11 I = 4e-4 def f(x,y): dy = np.zeros(2) dy[0] = y[1] dy[1] = -F/E/I*(L-x) return dy xf = L xspan = [0., xf] xeval = np.linspace(0.,xf,100) y0 = np.array([0,0]) result = solve_ivp(f,xspan,y0,t_eval=xeval) xm = result.t y = result.y[0,:] def fa(x): return -F*L/2/E/I*x**2 + F/6/E/I*x**3 plt.plot(xm,y,c='k',label='numerical') plt.plot(xm,fa(xm),c='k',ls='--',label='analytical') plt.grid() plt.xlabel('x - m') plt.ylabel('y - m') plt.legend()
Numerical and analytical solutions coincide.
9.14 Use a modified version of the Python code from Figure 9.10 to evaluate the integral (Equation 9.9), y=
2
0
t cos ( t ) dt
Starting with n = 4, progressively double the number of intervals 20 times. For each function call, return the integral estimate, the step size, and the error, where error = |true value – estimated value| where the true value is computed with Equation 9.10. Generate log-log plots of the step sizes and the errors versus n. =================================================================== import numpy as np import matplotlib.pyplot as plt def trap(f,a,b,n=100): x = a # set x to left side a h = (b-a)/n # compute interval width sm = f(a) # first term of sum for i in range(n-1): x = x + h # advance x sm = sm + 2*f(x) # add 2 * f(x) to sum
sm = sm + f(b) # add last term to sum ar = sm*h/2 # complete integral formula return ar f = lambda t: t*np.cos(t) a = 0 b = np.pi/2 n = [4] for i in range(20): n = np.append(n,2*n[i]) fexact = np.cos(b)+b*np.sin(b)-np.cos(a)-a*np.sin(a) y = np.zeros(21) er = np.zeros(21) for i in range(21): y[i] = trap(f,a,b,n[i]) er[i] = abs(fexact-y[i]) plt.loglog(n,np.pi/2/n,c='k') plt.grid() plt.xlabel('number of intervals') plt.ylabel('interval width') plt.figure() plt.loglog(n,er,c='k') plt.grid() plt.xlabel('number of intervals') plt.ylabel('integral error')
Evidence of round-off error as the number of intervals excedes 2×105.
Chapter 10 10.1 Table 3.12 lists the number of Atlantic hurricanes from 1851 to 2020, and the data are presented in a plot in Figure 3.11. (a) (b)
Using the concepts discussed in Section 10.1.1, describe this data set. Provide your observations from the plot. Write a Python script to extract from the data set two parts: 1851-1949 and 19502000. Compute the average of each subset and comment on the results.
==================================================================== (a) Table 3.12 Number of Atlantic Hurricanes per year from 1951 to 2020. 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870
3 5 4 3 4 4 3 6 7 6 6 3 5 3 3 6 7 3 7 10
1871 6 1891 7 1911 3 1931 3 1872 4 1892 5 1912 4 1932 6 1873 3 1893 10 1913 4 1933 11 1874 4 1894 5 1914 0 1934 7 1875 5 1895 2 1915 5 1935 5 1876 4 1896 6 1916 10 1936 7 1877 3 1897 3 1917 2 1937 4 1878 10 1898 5 1918 4 1938 4 1879 6 1899 5 1919 2 1939 3 1880 9 1900 3 1920 4 1940 6 1881 4 1901 6 1921 5 1941 4 1882 4 1902 3 1922 3 1942 4 1883 3 1903 7 1923 4 1943 5 1884 4 1904 4 1924 5 1944 8 1885 6 1905 1 1925 1 1945 5 1886 10 1906 6 1926 8 1946 3 1887 11 1907 0 1927 4 1947 5 1888 6 1908 6 1928 4 1948 6 1889 6 1909 6 1929 3 1949 7 1890 2 1910 3 1930 2 1950 11
1951 8 1971 1952 6 1972 1953 6 1973 1954 8 1974 1955 9 1975 1956 4 1976 1957 3 1977 1958 7 1978 1959 7 1979 1960 4 1980 1961 8 1981 1962 3 1982 1963 7 1983 1964 6 1984 1965 4 1985 1966 7 1986 1967 6 1987 1968 4 1988 1969 12 1989 1970 5 1990
The nature of the data is that they are integer counts. So, their resolution is 1.
6 3 4 4 6 6 5 5 5 9 7 2 3 5 7 4 3 5 7 8
1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010
4 4 4 3 11 9 3 10 8 8 9 4 7 9 15 5 6 8 3 12
2011 7 2012 10 2013 2 2014 6 2015 4 2016 7 2017 10 2018 8 2019 6 2020 13
The series does not appear to be starionary. The period 1850-1925 is somewhat stationary, then there is a shift upward for the period 1925-2000. There may be another shift upward for the period 2000-2020, but this is difficult to discern because the dispersion appears to increase. A general observation on the dispersion is that a high year is followed by a low year, and vice versa. (b) import numpy as np yr,no = np.loadtxt('AtlanticHurricaneHistory.csv',delimiter=',',unpack=True) yr1 = yr[0:100] no1 = no[0:100] yr2 = yr[99:150] no2 = no[99:150] avg1 = np.mean(no1) avg2 = np.mean(no2) print('Average hurricanes/year, 1851-1949: {0:5.2f}'.format(avg1)) print('Average hurricanes/year, 1950-2000: {0:5.2f}'.format(avg2)) Average hurricanes/year, 1851-1949: Average hurricanes/year, 1950-2000:
Average increases by 1 hurricane/year.
4.92 5.94
10.2 The table below reports biological oxygen demand (BOD) measurements taken at two-hour intervals from a small river. This measurement requires taking two samples at each site. One is tested immediately for dissolved oxygen, and the second is incubated in the dark at 20°C for five days and then tested for the amount of dissolved oxygen remaining. The difference in these two measurements is the BOD. (a) (b) (c)
Compute the average, median, sample standard deviation, and MAD for these data. Create a boxplot of the data and interpret the result. If there are one or more outliers, imagine that an investigation has justified its removal from the data set. Do so and repeat the calculations of part (a). Comment on the results.
Sample Number
BOD (mg/L)
1 2 3 4 5 6 7 8 9 10
6.5 5.8 13.1 6.4 7.0 6.3 7.0 9.2 6.7 6.7
==================================================================== (a) import numpy as np def MAD(x): xmed = np.median(x) xdev = abs(x-xmed) dmed = np.median(xdev) return dmed/0.6745 BOD = np.array([6.5,5.8,13.1,6.4,7.0,6.3,7.0,9.2,6.7,6.7]) BODavg = np.mean(BOD) BODmedian = np.median(BOD) BODs = np.std(BOD) BOD_MAD = MAD(BOD) print("BOD Sample Statistics") print('Average = {0:5.2f}'.format(BODavg)) print('Median = {0:5.2f}'.format(BODmedian))
print('Sample standard deviation = {0:5.2f}'.format(BODs)) print('MAD = {0:5.2f}'.format(BOD_MAD)) BOD Sample Statistics Average = 7.47 Median = 6.70 Sample standard deviation = MAD = 0.44
2.06
(b) import numpy as np import matplotlib.pyplot as plt x = np.array([6.5,5.8,13.1,6.4,7.0,6.3,7.0,9.2,6.7,6.7]) def pctile(x,pct): y = np.sort(x) n = len(y) mp = pct/100*(n-1) mp1 = int(mp) xp = (mp-mp1)*y[mp1]+(1-(mp-mp1))*y[mp1+1] return xp xmed = np.median(x) x25 = pctile(x,25.) x75 = pctile(x,75.) iqr = x75 - x25 xwhiskhi = x75 + 1.5*iqr xwhisklo = x25 - 1.5*iqr print('median = {0:5.2f}'.format(xmed)) print('25th %-ile = {0:5.2f}'.format(x25)) print('75th %-ile = {0:5.2f}'.format(x75)) print('iqr = {0:5.2f}'.format(iqr)) print('low whisker = {0:5.2f}'.format(xwhisklo)) print('high whisker = {0:5.2f}'.format(xwhiskhi)) plt.boxplot(x) plt.grid() median = 6.70 25th %-ile = 6.47 75th %-ile = 7.00 iqr = 0.53 low whisker = 5.69 high whisker = 7.79
Notice that the boxplot doesn't show the high whisker. But there are clearly two outliers, and the value above 13 is an extreme outlier. (c) import numpy as np def MAD(x): xmed = np.median(x) xdev = abs(x-xmed) dmed = np.median(xdev) return dmed/0.6745 BOD = np.array([6.5,5.8,6.4,7.0,6.3,7.0,6.7,6.7]) BODavg = np.mean(BOD) BODmedian = np.median(BOD) BODs = np.std(BOD) BOD_MAD = MAD(BOD) print("BOD Sample Statistics") print('Average = {0:5.2f}'.format(BODavg)) print('Median = {0:5.2f}'.format(BODmedian)) print('Sample standard deviation = {0:5.2f}'.format(BODs)) print('MAD = {0:5.2f}'.format(BOD_MAD)) OD Sample Statistics Average = 6.55 Median = 6.60 Sample standard deviation = MAD = 0.37
0.37
Average and median closer. Sample standard deviation not inflated by outliers, now equals the MAD. x = np.array([6.5,5.8,6.4,7.0,6.3,7.0,6.7,6.7]) median = 6.60 25th %-ile = 6.32 75th %-ile = 6.92 iqr = 0.60 low whisker = 5.42 high whisker = 7.83
No outliers present. It is notable that Python boxplot limits do not agree with calculations. 10.3 Carry out a study of the piston diameter data in Table 5.6 as follows: (a) (b) (c)
Apply the sign test to support the claim that the median diameter is less than 74.01 mm. Evaluate and test whether the data appear to have been drawn from a normal distribution. Assuming the data appear to be normally distributed, carry out a parametric test to support the same claim as in part (a).
=================================================================== 74.030 73.994 73.982 73.994
73.995 74.004 74.004 74.008
73.988 73.983 74.010 74.001
74.002 74.006 74.015 74.015
73.992 73.984 73.982 74.030
74.009 74.000 74.012 74.001
73.995 73.994 73.995 74.015
73.985 74.006 73.987 74.035
74.008 73.984 74.008 74.017
73.998 74.000 74.003 74.010
(a) import numpy as np import scipy.special as sp def cumbinom(p,n,r1,r2): sump = 0 for r in range(r1,r2+1): sump = sump + sp.comb(n,r)*p**r*p**(n-r) return sump diam = np.loadtxt('pistondata.txt') n = len(diam) medtest = 74.01 diff = diam - medtest rminus = 0 for i in range(n): if diam[i] < medtest: rminus = rminus + 1 print(rminus) p = 1- cumbinom(0.5,n,0,rminus) print('probability = {0:6.2f} %'.format(100*p)) 30 probability =
0.03 %
For the assumed median, 40.01, there are 30 samples below this value. The P value computed is 0.03%, indicating that the risk of being wrong with the claim that the median is less than 74.01 is extremely small. Therefore, we can feel safe in making the claim. (b) import numpy as np from scipy import stats diam = np.loadtxt('pistondata.txt') result = stats.anderson(diam,dist='norm') print(result) AndersonResult(statistic=0.3906184920869151, critical_values=array([0.531, 0.605, 0.726, 0.847, 1.007]), significance_level=array([15. , 10. , 5. , 2.5, 1. ]))
The Anderson-Darling test attempts to prove that the underlying distribution is not normal. The P-value here is 39%, much too high a risk to make such a claim. Therefore, we accept the assumption that the distribution is normal. (c) import numpy as np from scipy import stats diam = np.loadtxt('pistondata.txt') mu0 = 74.01 n = len(diam) diambar = np.mean(diam) s = np.std(diam) # probability to the left of xbar P = stats.norm.cdf(diambar,mu0,s/np.sqrt(n)) print('probability = {0:6.3f}%'.format(P*100)) probability =
0.005%
The risk of being wrong in making the claim that the true mean is less than 74.01 is tiny.
10.4 A product is produced in batches. The process operates in batches of 16 hours/day in two shifts. Data in the table below have been collected during the day and night shifts. (a) (b) (c)
Create side-by-side boxplots of the data, and comment on your observations. Apply the Wilcoxon rank sum test to the data to assess whether the mean quality is different between shifts. Apply the test illustrated with the A and B samples in the chapter to test whether the mean quality is different between shifts. Comment on the differences between the conclusions in part (b) and (c). Shift Day
Night
40 27 39 46 32 46 40 44 48 41
47 42 41 34 45 52 49 35 43 44
==================================================================== (a) import numpy as np import matplotlib.pyplot as plt day = np.array([40,27,39,46,32,46,40,44,48,41]) night = np.array([47,42,41,34,45,52,49,35,43,44]) x = np.array([day,night]).transpose() plt.boxplot(x,labels=('day','night')) plt.grid()
Night shift median and box is higher, but there is significant overlap. (b) import numpy as np from scipy import stats day = np.array([40,27,39,46,32,46,40,44,48,41]) night = np.array([47,42,41,34,45,52,49,35,43,44]) result = stats.ranksums(day,night,alternative='less') print('P-value = {0:5.2f}%'.format(result.pvalue*100)) P-value = 14.50%
The risk of making the claim that the mean day is < the mean night is likely too high, so we wouldn't support the claim.
(c) import numpy as np from scipy import stats day = np.array([40,27,39,46,32,46,40,44,48,41]) night = np.array([47,42,41,34,45,52,49,35,43,44]) result = stats.ttest_ind(day,night) p = result.pvalue/2 print('P-value = {0:5.2f}%'.format(p*100)) P-value = 15.16%
The P-value is slightly higher with the same conclusion as in part (b).
10.5 Fit a straight-line model to the data of Problem 10.1 and interpret the results. ================================================================== import numpy as np import matplotlib.pyplot as plt yr,no = np.loadtxt('AtlanticHurricaneHistory.csv',delimiter=',',unpack=True) n = len(yr) x = yr y = no Sx = np.sum(x) Sy = np.sum(y) Sx2 = 0 Sxy = 0 ybar = np.mean(y) xbar = np.mean(x) for i in range(n): Sx2 = Sx2 + x[i]**2 Sxy = Sxy + x[i]*y[i] a = (n*Sxy-Sx*Sy)/(n*Sx2-Sx**2) b = ybar - a*xbar print('slope = {0:6.2f} hurricanes/yr'.format(a)) print('intercept = {0:8.0f} hurricanes'.format(b)) nop = lambda year: a*year+b plt.plot(yr,no,c='k')
plt.plot(yr,nop(yr),c='k',ls='--') plt.grid() plt.xlabel('Year') plt.ylabel('No. of Hurricanes')
The straight-line fit indicates an increasing trend in the number of hurricanes per year with an overall increase of about two.
10.6 Table 5.1 presents freezing point data for antifreeze solutions. Fit an appropriate polynomial model to these data. Document how you arrive at your final selection and be sure to address the adequacy of your model. ====================================================================
Glycol (% by volume) Freezing point (oC)
First, a plot (Figure 5.2).
0 0
10 −3.4
20 −7.9
30 −13.7
40 −23.5
50 −36.8
60 −52.8
80 −46
90 −30
100 −12.8
Lower-order models, like straight-line and quadratic, would not be adequate here. We will start with 3rd-order polynomial and build up, looking at the residuals and standard error. We will use the built-in polyfit function. import numpy as np import matplotlib.pyplot as plt TC = [0., 10., 20., 30., 40., 50., 60., 80., 90., 100.] FP = [0., -3.4, -7.9, -13.7, -23.5, -36.8, -52.8, -46., -30., -12.8] n = len(TC) order = 3 coeff = np.polyfit(TC,FP,order) Tp = np.linspace(0.,100.,100) plt.scatter(TC,FP,c='k') plt.plot(Tp,np.polyval(coeff,Tp),c='k') plt.grid() plt.xlabel('Temperature - degC') plt.ylabel('Freezing Point Depression - degC') res = FP - np.polyval(coeff,TC) plt.figure() plt.scatter(np.polyval(coeff,TC),res,c='k',marker='o') plt.grid() plt.xlabel('Predicted Freezing Point Depression - degC') plt.ylabel('Residual Error --degC') se = np.sqrt(res.dot(res)/(n-(order+1))) print('Standard Error of the Estimate = {0:5.2f} degC'.format(se))
For the 3rd-order polynomial:
Standard Error of the Estimate = 3.98 degC For a 6th-order polynomial, here are the results:
Standard Error of the Estimate = 1.98 degC Model appears to be adequate. Higher-order models show more curvature local to the data. Lower-order models do not capture the pattern of the data as well. There is no one right answer here. It is a judgment situation. The standard error continues to decrease with higher-order models, but they do not perform suitably between the data points.
10.7 Figure 5.3 presents a plot of the Wolf sunspot data. (a) (b) (c)
Based on observations from the plot, comment on the data set. Fit a straight-line model to the data and add it to the plot. Comment on the stationarity of the data set. Produce a histogram plot of the data. Analyze whether the data appear to be drawn from a normally-distributed population.
===================================================================
(a) The data, of course, are cyclic. There is a quiet period in the early 1800s. Based on the bottom of the pattern, there appears to be cyclic behavior at a lower frequency. Overall, the series looks stationary. (b) import numpy as np import matplotlib.pyplot as plt year,nspots,sd,n1,n1 = np.loadtxt('SN_y_tot_V2.0.csv', \ delimiter=';',unpack=True) plt.plot(year,nspots,c='k') coeff = np.polyfit(year,nspots,1) plt.plot(year,np.polyval(coeff,year),c='r') plt.grid() plt.xlabel('Year') plt.ylabel('Wolf Sunspot Number')
The straight-line fit shows a gradual upward trend from the 60s to the 90s. So, this reveals that the series is not stationary. (c) import numpy as np import matplotlib.pyplot as plt year,nspots,sd,n1,n1 = np.loadtxt('SN_y_tot_V2.0.csv', \ delimiter=';',unpack=True) plt.plot(year,nspots,c='k') coeff = np.polyfit(year,nspots,1) plt.plot(year,np.polyval(coeff,year),c='r') plt.grid() plt.xlabel('Year') plt.ylabel('Wolf Sunspot Number') n = len(year) nbins = int(np.sqrt(n)) hist,bin_edges = np.histogram(nspots,bins=nbins,range=[0,300]) print(bin_edges) print(hist) bin_width = bin_edges[1]-bin_edges[0] m = len(hist) bin_centers = np.zeros((m)) for i in range(m): bin_centers[i] = (bin_edges[i]+bin_edges[i+1])/2 plt.figure() plt.bar(bin_centers,hist,width=bin_width,color='w',edgecolor='k') plt.xlabel('Wolf Sunspot Number')
plt.ylabel('Frequency')
Population is definitely not described by the normal distribution.
10.8 Table 5.5 presents densities of NaCl and MgCl salt solutions at 0°C for different salt concentrations. (a) (b)
Fit straight-line models for each salt and compare the results. Analyze the residuals of each fit and comment on model adequacy.
==================================================================== (a) import numpy as np import matplotlib.pyplot as plt Conc = [2, 4, 8, 12, 16, 20] NaCl = [1.01509, 1.03038, 1.06121, 1.09244, 1.12419, 1.15663] MgCl = [1.0168, 1.0338, 1.0683, 1.1035, 1.1395, 1.1764] coeffNa = np.polyfit(Conc,NaCl,1) coeffMg = np.polyfit(Conc,MgCl,1) print('Model Coefficients') print('NaCl',coeffNa) print('MgCl',coeffMg) plt.scatter(Conc,NaCl,marker='o',c='k',label='NaCl') plt.plot(Conc,np.polyval(coeffNa,Conc),c='k')
plt.scatter(Conc,MgCl,marker='s',c='k',label='MgCl') plt.plot(Conc,np.polyval(coeffMg,Conc),c='k') plt.grid() plt.xlabel('Concentration - wt%') plt.ylabel('Density - gm/cc') plt.title('Density of Salt and Mag Chloride Solutions') plt.legend() Model Coefficients NaCl [0.0078531 0.99884134] MgCl [0.00885521 0.99821288]
The first parameters are the slopes, and they document what the plot reveals. The MgCl line has a higher slope. The second parameters are the intercepts. We would expect them to be the same (H2O at 0 degC), and they are - two three significant figures.
(b) erNa = NaCl - np.polyval(coeffNa,Conc) erMg = MgCl - np.polyval(coeffMg,Conc) plt.figure() plt.plot(Conc,erNa,c='k',marker='o',label='NaCl') plt.plot(Conc,erMg,c='k',marker='s',label='MgCl') plt.grid() plt.xlabel('Concentration') plt.ylabel('Residual') plt.legend()
Although small, the residuals for both fits show systematic behavior. This indicates that the straight-line models are inadequate, and a model with curvature should be considered.
10.9 Write a Python script that generates 100 random numbers according to the uniform distribution in the range −1 to 1. (a) Compute the average and sample standard deviation of the data set and compare that to the theoretical values for the uniform distribution. (b) Modify your script to compute the 100-sample data set 100 times. Store the 100 sample averages in an array and create a histogram plot of the results. Comment on the plot. (c) Create a probability plot based on the normal distribution of your 100 averages from part (b). Also, perform and interpret the Anderson-Darling test. Interpret your results. You may have illustrated a principle named the Central Limit Theorem. Look this up and document it as part of your solution. =================================================================== (a) import numpy as np x = np.random.uniform(-1,1,100) avgx = np.mean(x) sx = np.std(x) print('sample average = {0:5.3f}'.format(avgx)) print('sample standard deviation = {0:5.3f}'.format(sx))
sample average = -0.012 sample standard deviation = 0.575
Theoretical values: Mean = 0 1 0.5774 3 Sample values are close to these.
Standard deviation =
(b) import numpy as np import matplotlib.pyplot as plt xbar = np.zeros(100) for i in range(100): x = np.random.uniform(-1,1,100) xbar[i] = np.mean(x) plt.hist(xbar) plt.grid(axis='y')
The histogram has a central tendency and might be modeled as a normal distribution. (c) from scipy import stats result = stats.anderson(xbar,dist='norm') print(result)
stats.probplot(xbar,plot=plt) plt.grid() AndersonResult(statistic=0.34657317267601684, critical_values=array([0.555, 0.632, 0.759, 0.885, 1.053]), significance_level=array([15. , 10. , 5. , 2.5, 1. ]))
From the probability plot, the distribution appears to be normal. The Anderson-Darling P-value is 35%. We cannot make a strong claim that the distribution is not normal. The central limit theorem states that if you have a population with mean μ and standard deviation σ and take sufficiently large random samples from the population with replacement , then the distribution of the sample means will be approximately normally distributed.
10.10 Table 5.6 presents 40 measurements of the inside diameter of piston rings, and these data are used in the histogram plots of Figures 10.12 and 10.13. Analyze whether it is likely that these data are drawn from a normally-distributed population. Utilize a probability plot and the Anderson-Darling test. =================================================================== import numpy as np from scipy import stats import matplotlib.pyplot as plt diam = np.loadtxt('PistonData.txt') result = stats.anderson(diam,dist='norm') print(result) stats.probplot(diam,plot=plt) plt.grid()
AndersonResult(statistic=0.3906184920869151, critical_values=array([0.531, 0.605, 0.726, 0.847, 1.007]), significance_level=array([15. , 10. , 5. , 2.5, 1. ]))
There is some concern in the probability plot with the deviations in the tails, but the AndersonDarling P-value is 39%, so we're not justified in claiming that the distribution is not normal.
10.11 Compute and plot the cumulative probability distribution for the Weibull distribution with = 2.0 and = 4.5 for 0 x 12. =================================================================== import numpy as np from scipy import stats import matplotlib.pyplot as plt x = np.linspace(0,12,200) n = len(x) f = np.zeros(n) alpha = 2.0 beta = 4.5 for i in range(n): f[i] = alpha/beta**alpha * x[i]**(alpha-1) * np.exp((x[i]/beta)**alpha) P = np.zeros(n) for i in range(n): P[i] = np.trapz(f[0:i],x[0:i])
plt.plot(x,P,c='k') plt.grid() plt.xlabel('x') plt.ylabel('Cumulative Probability')
10.12 In the sugar industry, a concentrated sucrose solution is produced and then sent to a crystallizer. The sugar concentration in the solution is measured with the refractive index4. The table below reports careful laboratory measurements of percent sucrose versus refractive index. Plot the data and fit an appropriate model. Add your model curve to the plot. Assess model adequacy and check whether the residuals appear to be normally distributed. Percent sucrose 0 5 10 15 20 25
Refractive index 1.3330 1.3403 1.3479 1.3557 1.3639 1.3723
Percent sucrose 30 35 40 45 50 55
Refractive index 1.3811 1.3902 1.3997 1.4096 1.4200 1.4307
Percent sucrose 60 65 70 75 80 85
Refractive index 1.4418 1.4532 1.4651 1.4774 1.4901 1.5033
=================================================================== import numpy as np import matplotlib.pyplot as plt
4
Refractive index - Wikipedia
pct = np.arange(0.,90.,5.) ref = np.array([1.3330, 1.3403, 1.3479, 1.3557, 1.3639, \ 1.3723, 1.3811, 1.3902, 1.3997, 1.4096, \ 1.4200, 1.4307, 1.4418, 1.4532, 1.4651, \ 1.4774, 1.4901, 1.5033]) plt.scatter(pct,ref,c='k') plt.grid() plt.xlabel('Percent Sucrose') plt.ylabel('Refractive Index')
There is some curvature, so a straight line would not be an adequate model. n = len(pct) m=2 coeff = np.polyfit(pct,ref,m) res = ref - np.polyval(coeff,pct) SSE = ref.dot(ref) se = np.sqrt(SSE/(n-(m+1))) print('Polynomial Order ',m) print('Standard Error = {0:5.3f}'.format(se)) pctp = np.linspace(0,85,100) refp = np.polyval(coeff,pctp) plt.plot(pctp,refp,c='k',ls=':') plt.figure() plt.plot(pct,res,c='k',marker='s') plt.grid() plt.xlabel('Percent Sucrose') plt.ylabel('Residual') result = stats.anderson(res,dist='norm') print(result)
Polynomial Order 2 Standard Error = 1.545 AndersonResult(statistic=0.467467782587633, critical_values=array([0.503, 0.573, 0.687, 0.802, 0.954]), significance_level=array([15. , 10. , 5. , 2.5, 1. ]))
The plot shows that the model is not adequate. The Anderson-Darling test supports a normal distribution. For a cubic polynomial,
There is no strong pattern. Polynomial Order 3 Standard Error = 1.600 AndersonResult(statistic=0.3517850979647399, critical_values=array([0.503, 0.573, 0.687, 0.802, 0.954]), significance_level=array([15. , 10. , 5. , 2.5, 1. ]))
The standard error for a 4th-order polynomial is 1.660, greater than that for a cubic polynomial. The cubic polynomial is the model of choice. Residuals appear to be normally distributed. Also, residuals are tiny. The model is n = 1.33308 + 1.4245110−3 p + 5.82559 10−6 p 2 + 1.28999 10 −8 p 3
10.13 The data in the table below show the depression in the freezing point of water (H2O) that occurs with the addition of hydrochloric acid (HCl) to different concentrations. (a) (b)
Create a plot of the data as shown in the table. Then create a plot of freezing point depression versus log10(HCl concentration). Fit an appropriate model based on the second plot of part (a). Assess the adequacy of the model.
HCl Concentration (mol/kgH2O)
Freezing Pt Depression (°C)
0.001 0.005 0.01 0.02 0.05 0.1 0.2 0.5 1 2
3.690 3.635 3.601 3.568 3.532 3.523 3.540 3.680 3.950 4.430
=================================================================== (a) import numpy as np import matplotlib.pyplot as plt HCl = np.array([0.001,0.005,0.01,0.02,0.05,0.1,0.2,0.5,1.0,2.0]) fpd = np.array([3.690,3.635,3.601,3.568,3.532,3.523, \ 3.540,3.680,3.950,4.430]) plt.plot(HCl,fpd,c='k',marker='o') plt.grid() plt.xlabel('HCl Concentration - mol/kgH2O') plt.ylabel('Freezing Pt Depression - degC')
logHCl = np.log10(HCl)
plt.figure() plt.plot(logHCl,fpd,c='k',marker='o') plt.grid() plt.xlabel('log10(HCl Concentration') plt.ylabel('Freezing Pt Depression - degC')
(b) import numpy as np import matplotlib.pyplot as plt HCl = np.array([0.001,0.005,0.01,0.02,0.05,0.1,0.2,0.5,1.0,2.0]) fpd = np.array([3.690,3.635,3.601,3.568,3.532,3.523, \ 3.540,3.680,3.950,4.430]) plt.plot(HCl,fpd,c='k',marker='o') plt.grid() plt.xlabel('HCl Concentration - mol/kgH2O') plt.ylabel('Freezing Pt Depression - degC') logHCl = np.log10(HCl) plt.figure() plt.plot(logHCl,fpd,c='k',marker='o') plt.grid() plt.xlabel('log10(HCl Concentration)') plt.ylabel('Freezing Pt Depression - degC') n = len(HCl) m = 7 coeff = np.polyfit(logHCl,fpd,m) print(coeff) res = fpd - np.polyval(coeff,logHCl)
SSE = res.dot(res) se = np.sqrt(SSE/(n-(m+1))) print('Standard Error',se) plt.figure() plt.plot(logHCl,res,c='k',marker='s') plt.grid() plt.xlabel('log10(HCl Concentration)') plt.ylabel('Residual') logHClp = np.linspace(-3,0.31,100) plt.figure() plt.scatter(logHCl,fpd,c='k',marker='o') plt.plot(logHClp,np.polyval(coeff,logHClp),c='k',ls=':') plt.grid() plt.xlabel('log10(HCl Concentration)') plt.ylabel('Freezing Pt Depression - degC') [-0.00689431 -0.06498327 -0.21866284 -0.24584662 1.17890939 1.22066102 3.95009144] Standard Error 0.0013552340519271744
0.30128988
A 7th-order polynomial has the minimum standard error. See the coefficients above from highest to lowest power of log10(HCl Concentration). This is the model added to the plot.
Here is the residual plot.
There is no systematic pattern.
10.14 Table 5.7 shows the wind capacity in GW for the ten countries that are the largest producers of wind power. Collect data on the population of each country and compute the wind power generation per capita. Study the results, apply any calculations you see as appropriate, create any plots, and comment on your observations. ===================================================================
China USA Germany India Spain UK France Brazil Canada Italy
Capacity (GW)
Population
W Per Capita
221.0 96.4 59.3 35.0 23.0 21.7 15.3 14.5 12.8 10.0
1,439,323,776 331,002,651 83,783,942 1,380,004,385 46,754,778 67,886,011 65,273,511 212,559,417 37,742,154 60,461,826
153.5 291.2 707.8 25.4 491.9 319.7 234.4 68.2 339.1 165.4
W Per Capita Germany Spain Canada UK USA France Italy China Brazil India
707.8 491.9 339.1 319.7 291.2 234.4 165.4 153.5 68.2 25.4
import matplotlib.pyplot as plt country = ['Germany','Spain','Canada','UK','USA' \ , 'France', 'Italy', 'China', 'Brazil', 'India'] WpC = [707.8,491.9,339.1,319.7,291.2,234.4,165.4,153.5,68.2,25.4] plt.bar(country,WpC,edgecolor='k',facecolor='c')
plt.xticks(rotation=-90) plt.grid(axis='y') plt.title('Wind Power Capacity in W/person by Country in 2021')
Germany has the highest per capita generation by far. This represents also the greatest investment in wind power per capita. The U.S. is in the middle, and the large population countries China and India are at the low end. This is in some contrast to the plot from Chapter 5 where China, U.S., and India are near the high end along with Germany. This shows that Germany has been the most agressive in adopting wind power.
10.15 Global temperature data can be obtained from https://www.ncdc.noaa.gov/cag/global/time-series for the years 1880 to 2021. These data are reported as a temperature anomaly, the difference to the Jan 1901-Dec 2000 average temperature. (a)
(b) (c) (d)
Download these data in a text file. Using either a text editor, for example, Notepad, WordPad, or Excel, create a text file with only the data, no headings. Write a Python script that loads the text file and creates a plot of the data, just lines connecting the data points and no markers. Comment on your observations of the plot. Fit a straight-line model to the data. Report the R2 and se values. Assess the model's adequacy by plotting the residuals versus year. Fit an appropriate higher-order polynomial model to the data and carry out the same steps as in part (b). You may need to evaluate several polynomial models. Use your chosen model to predict the temperature anomaly in 2030, 2040 and 2050.
===================================================================================
(a) import numpy as np import matplotlib.pyplot as plt yr,ta = np.loadtxt('GlobalTemperatureAnomaly.csv',delimiter=',', \ unpack=True) plt.plot(yr,ta,c='k') plt.grid() plt.xlabel('Year') plt.ylabel('Temperature Anomaly - degC')
Looking behind the noise, there appears to be a decrease from 1880 to 1920 and a steady increase since then. There is a significant peak during WWII.
(b) m=1 coeff = np.polyfit(yr,ta,m) plt.plot(yr,np.polyval(coeff,yr),c='g') resid = ta - np.polyval(coeff,yr) plt.figure() plt.plot(yr,resid,c='k') plt.grid() plt.xlabel('Year') plt.ylabel('Residual - degC') n = len(yr) SSE = resid.dot(resid) SST = np.var(ta)*(n-1) Rsq = 1 - SSE/SST se = np.sqrt(SSE/(n-(m+1))) print('R-squared = {0:7.4f}'.format(Rsq)) print('Standard error = {0:7.3f} degC'.format(se))
R-squared = 0.6426 Standard error = 0.222 degC
Model is inadequate because residual plot shows an overall down-up trend. The R2 value is relatively low because of the noise in the data. (c) Quadratic polynomial model R-squared = 0.7647 Standard error = 0.181 degC
Residuals now look random. Significant improvement in R2. Standard error lower. Accept this model. Coefficients: [ 8.59272083e-05 -3.27936039e-01 3.12673090e+02] Model: T = 312.7 − 0.3279 yr + 8.593 10 −5 yr 2 (d) print('2030 prediction = {0:5.2f} degC'.format(np.polyval(coeff,2030))) print('2040 prediction = {0:5.2f} degC'.format(np.polyval(coeff,2040))) print('2050 prediction = {0:5.2f} degC'.format(np.polyval(coeff,2050)))
2030 prediction = 2040 prediction = 2050 prediction =
1.06 degC 1.28 degC 1.51 degC