Now we'll extend the curve fitting into three dimensions, this is usually referred to as 'surface fitting'. From a programmatic point of view, there's hardly any difference in what we have already done earlier in the tutorial.
First, our 'user input' page will need a list of 3D equations to choose from, so this time we need the controller to provide them from the 3D equation set instead of the 2D set. Add the following to fitter.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | # this comment forces the wiki to indent correctly, and is not needed
def index3D(self):
c.equationNameList = []
# make a list of all 3D function names. Do not include any functions that require user configuration, such as
# the user-selectable polynomials or polyfunctionals. Code borrowed from the ListAllEquations.py example
for submodule in inspect.getmembers(PythonEquations.Equations3D):
if True == inspect.ismodule(submodule[1]):
for equationClass in inspect.getmembers(submodule[1]):
if True == inspect.isclass(equationClass[1]):
equation = equationClass[1]() # need to make an instance so the offset forms can auto-generate themselves properly
if equation.polynomialFlag == 0 and equation.polyfunctionalFlag == 0:
c.equationNameList.append(equation.name)
c.equationNameList.sort()
return render('/index3D.mako')
|
You can see at the end of the method we will render a template named index3D.mako. Make a file in the fittertutorial/templates directory named index3D.mako, and give it the following contents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 | <PRE>
<form action="/fitter/FitAndReturnResults3D">
Simple 3D surface fitting example
<b>Step 1: </b>Text data entry
<TEXTAREA WRAP=OFF NAME="USERDATA" COLS="50" ROWS="7">
Example XYZ data. All lines of text that do
not begin with three numbers are ignored.
3.017 2.175 0.320
2.822 2.624 0.629
2.632 2.839 0.950
2.287 3.030 1.574
2.207 3.057 1.725
2.048 3.098 2.035
1.963 3.115 2.204
1.784 3.144 2.570
1.712 3.153 2.721
2.972 2.106 0.313
2.719 2.542 0.643
2.495 2.721 0.956
2.070 2.878 1.597
1.969 2.899 1.758
1.768 2.929 2.088
1.677 2.939 2.240
1.479 2.957 2.583
1.387 2.963 2.744
2.843 1.984 0.315
2.485 2.320 0.639
2.163 2.444 0.954
1.687 2.525 1.459
1.408 2.547 1.775
1.279 2.554 1.927
1.016 2.564 2.243
0.742 2.568 2.581
0.607 2.571 2.753
</TEXTAREA>
<b>Step 2: </b>Select an equation
<select NAME="EQUATIONNAME">
% for equationName in c.equationNameList:
<option>${equationName}
% endfor
</select>
<b>Step 3: </b>Push this button!
<input type="submit" name="submit" value="Submit">
</form>
</PRE>
|
The users again need to input their data, so the template again has a TEXTAREA for cut-n-paste ASCII text entry - however this time it has 3D data. The (now 3D) equation list was provided by the controller, and Mako once again turns this into our equation selector. At the top of the template it shows that the form will be sent to our controller's FitAndReturnResults3D() method - so paste this code into our controller file fitter.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | # this comment forces the wiki to indent correctly, and is not needed
def FitAndReturnResults3D(self):
# since we only have the equation name, find and instantiate the appropriate equation - then fit
for submodule in inspect.getmembers(PythonEquations.Equations3D):
if True == inspect.ismodule(submodule[1]):
for equationClass in inspect.getmembers(submodule[1]):
if True == inspect.isclass(equationClass[1]):
eq = equationClass[1]() # need to make an instance so the offset forms can auto-generate themselves properly
if eq.name == request.params['EQUATIONNAME']:
c.equation = eq
c.equation.fittingTarget = 'SSQABS' # see the Equation base class for a list of fitting targets
c.equation.ConvertTextToData(request.params['USERDATA']) # Equations have ASCII text data for testing and examples
c.equation.Initialize() # now that the equation has data, set up the cache
c.equation.GuessInitialCoefficients() # estimate initial parameters if needed
c.equation.FitToCacheData() # perform the fit
c.equation.CalculateErrors() # so we can output the errors
return render('/results3D.mako')
|
The only difference here is that we iterate over the 3D equations instead of the 2D, and render the output with a file named results3D.mako. Make a file in the fittertutorial/templates directory named results3D.mako, and give it the following contents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 | <PRE>
Result for ${c.equation.name}
${c.equation.HTML}
Coefficients:
% for i in range(len(c.equation.coefficientDesignatorTuple)):
${c.equation.coefficientDesignatorTuple[i]}: ${c.equation.coefficientArray[i]}
% endfor
<table border=1>
<TR>
<TD align=center>X</TD>
<TD align=center>Y</TD>
<TD align=center>Z</TD>
<TD align=center>Equation Output</TD>
<TD align=center>Absolute Error</TD>
<TD align=center>Relative Error</TD>
<TD align=center>Percent Error</TD>
</TR>
% for i in range(len(c.equation.DependentDataArray)):
<TR>
<TD>${c.equation.IndependentDataArray[0][i]}</TD>
<TD>${c.equation.IndependentDataArray[1][i]}</TD>
<TD>${c.equation.DependentDataArray[i]}</TD>
<TD>${c.equation.PredictedArray[i]}</TD>
<TD>${c.equation.AbsoluteErrorArray[i]}</TD>
<TD>${c.equation.RelativeErrorArray[i]}</TD>
<TD>${c.equation.PercentErrorArray[i]}</TD>
</TR>
% endfor
</table>
<code>
## uncomment your choice of computing languages here...
##${c.equation.CodeJAVA()}
##${c.equation.CodeCPP()}
${c.equation.CodePYTHON()}
##${c.equation.CodeCS()}
##${c.equation.CodeSCILAB()}
##${c.equation.CodeMATLAB()}
</code>
</PRE>
|
Try http://127.0.0.1:5000/fitter/index3D
- you should now have a fully functional surface fitter. You are done, except for the next section on adding graphics. Again, this is not strictly required for a curve and surface fitting web site, but it greatly eases and speeds analysis of the fitting results.
Next: --> Adding graphical output