ENGG1001-Python-Assignment 3

Assignment 3: Optimization of a Rocket Nose Cap (20 Marks) ENGG1001: Programming for Engineers — Semester 1, 2022 Due: Friday 3 June 2022 at 4:00pm Introduction The Artemis I mission is planned to launch in June 2022, not long after this assignment is due. Artemis I will be an uncrewed mission and the first test of NASA’s SLS (Space Launch System). The SLS is a super heavy-lift launch vehicle with a liquid rocket for the core stage and solid rocket boosters. The SLS stack is shown in Figure 1. The solid rocket booster is shown in exploded view and at the top is the nose assembly. In this assignment, you are asked to re-design the nose assembly shape of the solid rocket booster to give it better aerodynamic performance. Figure 1: Space Launch System with exploded view of solid rocket booster. Source: NASA https://www.nasa.gov/exploration/systems/sls/fs/solid-rocket-booster.html Design requirement You are asked to find the nose shape with the minimum drag for a given length, L, and base radius, Rb. You will do that by computing the coefficient of drag, CD, for a range of nose shapes and determining which has the smallest value for CD. The nose shapes of interest are described by a power law: y(x) = Rb ( x L )d (1) where y is the height of the body above the axis, x is position along the axis, Rb is the base radius, L is the length (measured along the axis), and d is the exponent in the power law. The following assumptions are made in this design scenario. 1 The nose cap is flying at zero angle-of-attack. This means the analysis can be restricted to the x y plane. (See Figure 2.) Only pressure drag is included in the analysis to find a minimum drag shape. The frictional air drag can be ignored. The flight condition for our design target is when the rocket is flying in the supersonic/hypersonic regime.1 The length, L, of the solid rocket nose assembly is 4.88m and the base radius, Rb, is 1.83m. L Rb (a) full nose cap aligned for flight along x-axis (b) nose cap profile in 2D used for analysis Figure 2: Nose cap geometry used for analysis with length and base radius labelled. Task 1 (2 marks) Write a function nose_shape that accepts a position x and the parameters of a power-law body. The function should compute and return the height y above the axis of a power-law body at a position x . 1 def nose_shape(x: float, L: float, Rb: float, d: float) -> float: 2 “”” 3 Parameters: 4 x (float): x-position along body 5 L (float): length of power-law body 6 Rb (float): base radius of power-law body 7 d (float): exponent in power-law body 8 Returns: 9 (float): y-position on body corresponding to x 10 “”” 11 … Write a function plot_nose_shape that accepts the parameters of a power-law body and a number of sample points. The sample points are used to create the plot and should be evenly distributed in the x direction. The plotting function does not return anything, but should produce a plot of the power-law body to the screen. 1In reality, there would be an optimal nose shape at every different flight speed. Fortunately, at supersonic and hypersonic flight speeds, the optimal shape changes only slightly over a large range of speeds. So we target this speed regime for our design. 2 1 def plot_nose_shape(L: float, Rb: float, d: float, number_samples: int) -> None: 2 “”” 3 Parameters: 4 L (float): length of power-law body 5 Rb (float): base radius of power-law body 6 d (float): exponent in power-law body 7 number_samples (int): number of sample points for constructing plot 8 Returns: 9 None 10 Side-effect: 11 Plots power-law body as x-y plot to screen 12 “”” 13 … Produce a plot of the power-law body using 20 sample points. An example with L = 4.88, Rb = 1.83, d = 0.7 is shown in Figure 3. This was produced by a console session shown below. >>> L = 4.88; Rb = 1.83; d = 0.7; number_samples = 20 >>> plot_nose_shape(L, Rb, d, number_samples) Figure 3: Example power-law body with L = 4.88, Rb = 1.83, d = 0.7 and 20 sample points used for plotting. 3 Analysis method to compute drag coefficient Assume that the rocket is flying along the x-axis. This simplifies the analysis of the coefficient of drag. To estimate the drag coefficient, we will divide the nose shape profile into panels, as shown in Figure 4. On each panel, we can compute a local drag coefficient. To get the total drag coefficient, we will do an area-weighted sum of the contributions from individual panels. -0.5 0 0.5 1 1.5 2 0 1 2 3 4 5 0 1 2 3 4cd,2 y x Figure 4: Example nose shape divided into five panels (0–4) for computing CD using an area- weighted sum (see Eq. 3). Black dots: nose shape; Solid blue lines: panels to approximate nose shape. The panels are spaced at equal increments in the x direction. cd,2 indicates the local drag coefficient on panel 2. Let’s talk about how to compute the local drag coefficient on a panel, such as the value cd,2 shown in Figure 4. As the nose shape flies, each panel is at its own angle of θ with respect to the direction it pushes through the air. A single panel with its geometry labelled is shown in Figure 5. Figure 5: Geometry of a panel with side view and front view. Using a simple flow theory thanks to Newton2, the local drag coefficient on the panel is cd,p = 2 sin 3 θ. (2) 2Newton proposed this idea for working out the force of water on ship hulls. The idea is that the force on the body is only due to the fluid’s normal component of momentum, and the tangential component has no effect. At low speeds (like ships in water), this is not a good approximation. However, in the mid 20th century, when high-speed flight was being worked on, Newton’s theory had its day! His approximation of how the momentum is transferred is a very good one in the supersonic-hypersonic flight regime. 4 Task 2 (1 mark) Write a function drag_coeff_panel that computes and returns cd,p on a panel with end points (x0, y0) and (x1, y1). 1 def drag_coeff_panel(x0: float, y0: float, x1: float, y1: float) -> float: 2 “”” 3 Compute local c_d on a panel with end points (x0, y0) and (x1, y1) 4 5 Parameters: 6 x0 (float): x-position at left end of panel 7 y0 (float): y-position at left end of panel 8 x1 (float): x-position at right end of panel 9 y1 (float): y-position at right end of panel 10 Returns: 11 (float): local drag coefficient 12 “”” 13 … An example output from this function is shown here. >>> x0 = 0.0; y0 = 0.0; x1 = math.sqrt(3)/2; y1 = 0.5 >>> c_d_panel = drag_coeff_panel(x0, y0, x1, y1) >>> c_d_panel 0.25 Computing the total drag coefficient We mentioned earlier the use of an area-weighted sum of the individual panels to estimate the total drag coefficient, CD. Mathematically, this sum is CD ≈ 1 A n 1∑ p=0 cd,pdA (3) where A is the total area of the nose shape, cd,p is the local drag coefficient and dA is the area of a panel. In the analysis method, A can be computed as the sum of the individual panel areas, dA. When computing the area of the panel, we need to recognise the fact that the 2D projection of the panel is rotated about the x-axis. This is also shown in Figure 5. The area of a panel, dA, is computed using the panel length and multiplying by the perimeter around the nose shape at that point: dA = dL× perimeter (4) = dL× 2π × ym (5) When dividing the shape into panels, use panels with an equal length in x . Appendix A shows the results of panel calculations in tabular form for the 5-panel example in Figure 4. This might be useful if you are attempting to build, test and debug your own implementation. 5 Task 3 (3 marks) Write a function that computes and returns the total coefficient of drag, CD, on a power-law body. The function should accept the parameters of a power-law body and the number of panels to use in the summation. In this new function, you should call previous functions that you have built and tested. 1 def drag_coeff(L: float, Rb: float, d: float, number_panels: int) -> float: 2 “”” 3 Compute drag coefficient for a power-law body. 4 5 Parameters: 6 L (float): length of power-law body 7 Rb (float): base radius of power-law body 8 d (float): exponent in power-law body 9 number_panels (int): number of panels for use in C_D calculation 10 Returns: 11 (float): drag coefficient 12 “”” 13 … If we use a cone (d = 1.0) as a test, we can choose parameters so that the total CD should be the same as the single panel test in Task 2. An example output from this function using 50 panels is shown here. >>> L = math.sqrt(3)/2; Rb = 0.5; d = 1.0; number_panels = 50 >>> C_D = drag_coeff(L, Rb, d, number_panels) >>> C_D 0.25000000000000006 Task 4 (1 mark) Write a function to produce a table of CD values for various choices of the exponent d . 1 def print_table(L: float, Rb: float, number_panels: int, d_start: float, number_entries: int, step: float) -> None: → 2 “”” 3 Print table of C_Ds for various choices of exponent d. 4 5 Parameters: 6 L (float): length of power-law body 7 Rb (float): base radius of power-law body 8 number_panels (int): number of panels in C_D calculation 9 d_start (float): first exponent 10 number_entries (int): number of entries displayed in table 11 step (float): difference between each exponent d 12 Returns: 13 None 14 Side-effect: 15 Table is printed to screen 16 “”” 6 Recall that our goal is to find a nose cap configuration with a minimum drag coefficient. The table produced by this function will help us in our goal. The table can help us explore what the influence of changing the exponent in the power-law has on drag coefficient. Use the length and base radius constraints for the nose cap of the SLS solid rocket booster. An example output from this function is below. >>> L = 4.88; Rb = 1.83; number_panels = 50; d_start = 0.5; number_entries = 11; step = 0.05 → >>> print_table(L, Rb, number_panels, d_start, number_entries, step) ***************************** * d * C_D * ***************************** * 0.50 * 0.0649 * * 0.55 * 0.0622 * * 0.60 * 0.0611 * * 0.65 * 0.0612 * * 0.70 * 0.0625 * * 0.75 * 0.0648 * * 0.80 * 0.0679 * * 0.85 * 0.0717 * * 0.90 * 0.0761 * * 0.95 * 0.0811 * * 1.00 * 0.0866 * ***************************** Hint: You should create the above table by using f-string formatting. Check the Appendix B for layout dimensions. Discussion 1: Converting to object-oriented design At this point, you could actually answer the design question of a minimum-drag nose shape. Your table should show some values where the drag coefficient reaches a minimum. To improve your estimate of the exponent value d , you could rebuild the table with smaller steps around the values of minimum CD. However, we will proceed to find this minimum-drag nose shape in a more rigorous manner using an optimization method. In terms of code design, you might notice up to this point that there has been a lot of repetition of the power-law body parameters as arguments to the functions. This is a hint to us that we could capture those parameters as the state of some object. Then we would be able to pass a single object to functions rather than a long list of parameters. An object-oriented design would help here. In the next part of this assignment, you will rebuild part of the code in object-oriented form. You can and should leave your existing functions in place. Task 5 (3 marks) Write a class called PowerLawBody to represent the power-law body. At a minimum, your class should contain the following methods. You may write additional helper methods to aid your program design. 7 __init__() an initialiser method to store the parameters of the power-law body and the number of panels used to compute drag coefficient. shape() a method to return the height of body above the axis at a given x position. You can copy and adapt your nose_shape() function from Task 1 and convert to a method. drag_coeff() a method to return the drag coefficient for the power-law body. You can copy and adapt your drag_coeff() function from Task 2 and convert to a method. The parameters and return types for the methods are shown here. 1 class PowerLawBody(object): 2 3 def __init__(self, L: float, Rb: float, d: float, number_panels: int) -> None: 4 “”” 5 Parameters: 6 L (float): length of power-law body 7 Rb (float): base radius of power-law body 8 d (float): exponent in power-law body 9 number_panels (int): number of panels for use in C_D calculation 10 Returns: 11 None 12 “”” 13 14 def shape(self, x: float) -> float: 15 “”” 16 Parameters: 17 x (float): x-position along body 18 Returns: 19 (float): y-position on body corresponding to x 20 “”” 21 22 def drag_coeff(self) -> float: 23 “”” 24 Computes C_D for the body 25 26 Returns: 27 (float): drag coefficient 28 “”” An example of using this class to create an object and compute the drag coefficient is shown here. We reuse the case when d = 1.0 from earlier so that we can check that the results are the same after we have rewritten in object-oriented form. >>> L = math.sqrt(3)/2; Rb = 0.5; d = 1.0; number_panels = 50 >>> nose_cap = PowerLawBody(L, Rb, d, number_panels) >>> y_mid = nose_cap.shape(L/2) >>> y_mid 0.25 >>> C_D = nose_cap.drag_coeff() >>> C_D 0.25000000000000006 8 Task 6 (1 mark) Write a function that produces a plot of CD for a range of selections for the value d . 1 def plot_drag_coeff(nose_cap: PowerLawBody, d_start: float, d_stop: float, number_samples: int) -> None: → 2 “”” 3 Parameters: 4 nose_cap (PowerLawBody): the power-law body of interest 5 d_start (float): the start-point power-law exponent for plotting 6 d_stop (float): the stop-point power-law exponent for plotting 7 number_samples (int): the total number of sample points for use in plotting → 8 Returns: 9 None 10 Side-effect: 11 Plot to screen C_D as a function of d 12 “”” 13 … HINT: It may be convenient to add a setter method to your PowerLawBody class that sets the design parameter, d . A name like set_design_param would be a good choice. This will allow you to vary the value d and compute the resulting drag coefficient without instantiating a new PowerLawBody for every point on the plot. An example output from a console session is shown below, and the resulting plot is in Figure 6. >>> L = 4.88; Rb = 1.83; d = 0.5; number_panels = 50 >>> nose_cap = PowerLawBody(L, Rb, d, number_panels) >>> d_start = 0.5; d_stop = 1.0; number_samples = 50 >>> plot_drag_coeff(nose_cap, d_start, d_stop, number_samples) Figure 6: Example of effect of power-law exponent, d , on drag coefficient for L = 4.88 and Rb = 1.83 9 Discussion 2: Using an optimization method Again, we have enough information to select the minimum drag nose cap design. Using the plot you produced in Task 6, you could read off the value d that corresponds to minimum CD. Indeed, this would be good enough at a preliminary design stage. However, we are going to try an algorithmic approach to seeking the value d that corresponds to minimum drag. To do that, we will implement and use an optimization method. You may recall in Assignment 1 that the bonus task asked you to search for a maximum drag coefficient that could be accommodated on the runway. That is a form of optimization problem. In that assignment, a direct search by looping was a sufficient solution. Now, in Assignment 3, we will use a more robust and efficient method to find an optimal solution to our design challenge. Specifically, we will use the Golden Section Search. We describe here how that method works, how to test it on a simple system (Task 7), and how to apply it to our design scenario (Task 8). Golden Section Search The Golden Section Search is a method for optimisation in one dimension. The one dimension refers to the number of design parameters. So in our case, a one dimensional search is appropriate because we have one design choice, d , the exponent in the power law. (The length and base radius of our nose cap are fixed constraints on the design.) An optimisation method seeks to find the minimum value to some function of the design parameters. In our case, drag coefficient is a function of d , so drag coefficient is the function we will try to minimise. Let’s explain the method, in words first, in relation to a simple function f (d) = d2 2d. (6) With a little high school calculus, we can compute the minimum of the function f analytically: we would set f ′(d) = 0 and solve for d to find where the minimum occurs. If you try that, you will get a minimum at d = 1 and f (d = 1.0) = 1. Let’s now describe how to use the Golden Section Search to find that same answer. As a sneak peak, Figure 7 shows a search strategy of placing test points that is narrowing in on that value of d = 1 as corresponding to the minimum of the function f (d) = d2 2d . The Golden Section Search starts with the user choosing two points in the design space that are either side of the minimum. Often, it is easy to select those bounding points because we will have some information about where we expect the minimum. In our case, we can pick a bounding interval from the table (Task 4) or the plot (Task 6) that we built. When you have no information at all about a reasonable starting guess for the search, there are bracketing methods available to help. We will not need those here. After the initial selection of two points, the search strategy involves placing test points between the bounding interval. We then decide which of the test points bracket the minimum and work with them. In the new bracket, we place new test points and continue our search. This step continues until our bracket on the minimum value becomes very small. The word ‘Golden’ in the method name comes from the choice of locating the test points; it turns out that the optimal location of the test points for an efficient search is to place them at the Golden Ratio in the bracketing interval. Let’s describe the method with discrete steps and a graphical illustration shown in Figure 7. 0. Select points a and b (with a < b) that bound the interval over the minimum. (See Figure 7(a).) 1. Choose two points, shown in Figure 7(b), dL (for left) and dR (for right) such that a < dL < dR < b 10 2. Replace the original bracketing interval by a smaller one that still contains the minimum. Use this rule: if f(dL) < f(dR), use [a, dR] as new interval if f(fL) > f(dR), use [dL, b] as new interval (In Figure 7(b), f (dL) < f (dR) so we apply the rule that the new interval is [a, dR]. Figure 7(c) shows the shuffled positions of the points (a, dL, dR, b) at the end of Step 2.) 3. Repeat the bracketing step until the interval containing the minimum is as small as the user desires. (Figure 7(d) shows a subsequent step. In practice, this is repeated several more times until the difference between dL and dR is very small.) The steps just described give you the concept of the strategy, but they do not tell you how to select the new points on each step of the search. In Figure 7, the points shown in red are where new selections are made and, as such, new evaluations of the function. Here we list an algorithm for implementing the Golden Section Search. It explicitly tells you how to select the new points using the Golden Ratio, g = 0.618034. The algorithm contains everything you need to build a Python implementation of the Golden Section Search. -2 -1 0 1 2 3 4 0 0.5 1 1.5 2 2.5 3 a f(a) b f(b) f( d) d -2 -1 0 1 2 3 4 0 0.5 1 1.5 2 2.5 3 a f(a) b f(b) dL f(dL) dR f(dR) f( d) d (a) Initial guess on bounds (c) Step 2 -2 -1 0 1 2 3 4 0 0.5 1 1.5 2 2.5 3 a f(a) b f(b) dL f(dL) dR f(dR) f( d) d -2 -1 0 1 2 3 4 0 0.5 1 1.5 2 2.5 3 a f(a) b f(b) dL f(dL) dR f(dR) f( d) d (b) Step 1 (d) Step 3 Figure 7: Example of first steps of Golden Section Search. The objective function is f (d) = d2 2d and initial guesses are a = 0.1 and b = 2.3. Red markers indicate new evaluations of the objective function. 11 Algorithm: Golden Section Search User provides: f -- function to minimize, f(d) a -- left end to starting search interval b -- right end to starting search interval tol -- tolerance for stopping: when bracket is smaller than this, stop 0. Set g = 0.618034 1. Compute dL, dR and fL = f(dL), fR = f(dR) dL = a + (1 - g)*(b - a) dR = a + g*(b - a) 2. Repeat until (dR - dL) < tol: if fL < fR then Set b = dR; dR = dL; fR = fL Set dL = a + (1 - g)*(b - a) Compute new fL = f(dL) else Set a = dL; dL = dR; fL = fR Set dR = a + g*(b - a) Compute new fR = f(dR) 3. When tolerance is achieved, minimum lies between dL and dR. Return mid-point in that interval: 0.5*(dL + dR) Task 7 (3 marks) Write a function called golden_section_search to implement the Golden Section Search described in the preceding discussion. 1 def golden_section_search(f, a: float, b: float, tol: float): 2 """ 3 Use Golden Section Search to find a minimum of f. 4 5 Parameters: 6 f : function in one variable, accepts float, returns float 7 a (float) : left end for search interval 8 b (float) : right end for search interval 9 tol (float): tolerance for stopping search 10 11 Returns: 12 (float): minimum of function f 13 """ 14 ... 12 Test your Golden Section Search on the function: f (d) = d2 2d with a = 0.1, b = 2.3 and tol = 1.0e-3. An example output from running that test is shown here. >>> def f(d): return d**2 – 2*d >>> d_opt = golden_section_search(f, 0.1, 2.3, 1.0e-3) >>> d_opt 1.0003212623278848 Task 8 (1 mark) Use the function from Task 7, golden_section_search, to find the value d that gives a minimum- drag power-law body. You will need to make your PowerLawBody object behave like a function since that is what the golden_section_search is expecting. There are several ways to do that in Python. Here we suggest the use of the special method __call__. The golden_section_search accepts a function as the first parameter. However, the nose_cap is an object, not a function. We will add the special method __call__ so that our object can be used as a function. This means we do not have to write two versions of the Golden Section Search depending on whether we have functions or objects. Add a __call__ method to your PowerLawBody class. 1 def __call__(self, d: float) -> float: 2 “”” 3 Parameters: 4 d (float): exponent in power-law body 5 Returns: 6 (float): drag coefficient for value d 7 “”” As a helper, an example of making a Line class callable using the __call__ method is shown in Appendix C. An example of using the golden_section_search with a PowerLawBody object is shown below. In the example, we determine the optimum value for d and then recompute the drag coefficient corresponding to that optimum value. That gives us the minimum drag coefficient for a power-law body subject to the length and base radius design constraints. >>> L=4.88; Rb=1.83; d=0.7; number_panels = 50; a=0.5; b=1.0; tol=1.0e-3 >>> nose_cap = PowerLawBody(L, Rb, d, number_panels) >>> d_opt = golden_section_search(nose_cap, a, b, tole) >>> d_opt 0.6175541411861165 >>> nose_cap.set_design_param(d_opt) >>> C_D_min = nose_cap.drag_coeff() >>> C_D_min 0.060966875426631005 13 Task 9 (BONUS) (3 marks) This is an optional task for bonus marks towards functionality. In this task, we will explore the use of a different curve type to represent the nose cap. The method to compute the drag coefficient remains identical. Try a three-point Bézier curve to represent the nose cap. An example is shown in Figure 8. In Figure 8, the y value for the middle control point is called d — this will be the design parameter. The arrow in Figure 8 indicates how d moves to control the nose shape. Our new optimization goal is to find the value of d for this Bézier curve that minimises the coefficient of drag. The curve shape y as a function of x for a three-point Bézier curve with the control points located at equal spacing in x is: y(x) = 2d ( x L )( 1 x L ) + Rb ( x L )2 (7) where d is the design parameter (y -coordinate for middle control point), L is the length of the nose cap and Rb is the base radius of the nose cap. -0.5 0 0.5 1 1.5 2 0 1 2 3 4 5 (0,0) (L/2,d) (L,Rb) y x Figure 8: Example nose shape represented by a three-point Bézier curve with L = 4.88 and Rb = 1.83 corresponding to Eq. 7. The solid line is the Bézier curve and the dashed line is the control point polygon. The design parameter d is the y -coordinate of the middle control point. The arrows indicate how d can vary to influence the nose shape. To complete this task, you will need to write a new class to represent the BezierCap. If done correctly, you can reuse the golden_section_search without any modifications. A good coding solution will require one new class in your code: BezierCap. An excellent coding solution will recognise that there is a lot of commonality between BezierCap and PowerLawBody. As such, a base class NoseCap would be a good addition and then BezierCap and PowerLawBody can become derived classes from NoseCap. Use your new code to find the optimal value of d in Equation 7 that minimizes the drag coefficient. What do you notice about the minimum CD value for the optimal three-point Bézier cap when compared to the power-law body An example output when minimizing CD for a BezierCap with L = 4.88, Rb = 1.83 and the number of panels set to 50 is shown below. 14 >>> L = 4.88; Rb = 1.83; d = 1.281; number_panels = 50 >>> nose_cap = BezierCap(L, Rb, d, number_panels) >>> a = Rb/2; b = Rb; tol = 1.0e-3 >>> d_opt = golden_section_search(nose_cap, a, b, tol) >>> d_opt 1.3965747737364322 >>> nose_cap.set_design_param(d_opt) >>> nose_cap.drag_coeff() 0.06780735302664186 Assessment and Marking Criteria The maximum achievable mark for this assignment is 20 marks. This includes the bonus marks if you are granted those marks. Functionality Assessment 15 marks The functionality will be marked out of 15. For each task, your assignment will be put through a set of tests, and the corresponding functionality mark for each task will be proportional to the number of tests you pass. If, for example, there are three functionality tests for Task 3 and you pass two of them, then your functionality mark for Task 3 will be 2/3× 3. You will be given the functionality tests before the due date for the assignment so that you can gain a good idea of the correctness of your assignment yourself before submitting. You should, however, make sure that your program meets all the specifications given in the task sheet. That will ensure that your code passes all the tests. Note: Functionality tests are automated and so string outputs need to exactly match what is expected. Do not leave it until the last minute because it generally takes time to get your code to pass the tests. The bonus marks in Task 9 contribute towards the functionality assessment marks only. The maximum achievable functionality assessment marks is 15 marks including the bonus marks, if they are awarded to you. Code Style Assessment 5 marks The style of your assignment will be assessed by one of the tutors, and you will be marked according to the style rubric provided with the assignment. The style mark will be out of 5. Assignment Submission You must submit your completed assignment to Gradescope. The only file you submit should be a single Python file called a3.py (use this name — all lower case). This should be uploaded to Gradescope. You may submit your assignment multiple times before the deadline; only the last submission will be marked. Late submission of the assignment will not be accepted. In the event of exceptional personal or medical circumstances that prevent you from handing in the assignment on time, you may submit a request for an extension. See the course profile for details on how to apply for an extension. Requests for extensions must be made according to the guidelines in the ECP. 15 Appendix A Tabular calculation for five panels The example shown in Figure 4 is a power-law body with L = 4.88, Rb = 1.83 and d = 0.7. It has been divided into five panels, labelled 0–4. Shown here in tabular form are the results of calculations for individual panels. ***************************************************************************************** * panel * (x0,y0) * (x1,y1) * theta (rad) * c_d,p * dA * ***************************************************************************************** * 0 * (0.000,0.000) * (0.976,0.593) * 0.546 * 0.2802 * 2.1283 * * 1 * (0.976,0.593) * (1.952,0.964) * 0.363 * 0.0894 * 5.1055 * * 2 * (1.952,0.964) * (2.928,1.280) * 0.313 * 0.0586 * 7.2309 * * 3 * (2.928,1.280) * (3.904,1.565) * 0.285 * 0.0443 * 9.0896 * * 4 * (3.904,1.565) * (4.880,1.830) * 0.265 * 0.0358 * 10.7868 * ***************************************************************************************** * total area, A = 34.3411 * ***************************************************************************************** B Table Layout Table dimensions: ***************************** * d * C_D * ***************************** * 0.50 * 0.0649 * * 0.55 * 0.0622 * * 0.60 * 0.0611 * * 0.65 * 0.0612 * * 0.70 * 0.0625 * * 0.75 * 0.0648 * * 0.80 * 0.0679 * * 0.85 * 0.0717 * * 0.90 * 0.0761 * * 0.95 * 0.0811 * * 1.00 * 0.0866 * ***************************** 12 chars 14 chars 16 C Example using special method __call__ This shows a small Line class and how the object can act with function-like syntax if a __call__ method is implemented in the class. 1 class Line(object): 2 3 def __init__(self, x0, y0, x1, y1): 4 self._x0 = x0 5 self._y0 = y0 6 self._x1 = x1 7 self._y1 = y1 8 9 def eval(self, t): 10 x = (1 – t)*self._x0 + t*self._x1 11 y = (1 – t)*self._y0 + t*self._y1 12 return (x, y) 13 14 def __call__(self, t): 15 return self.eval(t) An example console session below uses the line ab like a function when called with t=0.5. 1 >>> ab = Line(0, 0, 1, 1) 2 >>> mid = ab(0.5) 3 >>> mid 4 (0.5, 0.5) 17