├── .gitignore
├── GEOL157L
├── GEOL157L_lab1_answer.ipynb
├── GEOL157L_lab4_Energy_balance_model.ipynb
├── GEOL157_Lab5_paleclimate_records.ipynb
├── GEOL157_Lab6.ipynb
└── q_profile.txt
├── GEOL351
├── .ipynb_checkpoints
│ └── ZeroD-checkpoint.ipynb
├── CoursewareModules
│ ├── CVS
│ │ ├── Entries
│ │ ├── Repository
│ │ ├── Root
│ │ └── _notes
│ │ │ └── dwsync.xml
│ ├── ClimateGraphics.py
│ ├── ClimateGraphics.pyc
│ ├── ClimateGraphicsMPL.py
│ ├── ClimateGraphicsMPL.pyc
│ ├── ClimateUtilities.py
│ ├── ClimateUtilities.pyc
│ ├── DummyGraphics.py
│ ├── DummyGraphics.pyc
│ ├── _notes
│ │ └── dwsync.xml
│ ├── phys.py
│ ├── phys.pyc
│ ├── planets.py
│ └── setpath.py
├── ENSO_recharge.ipynb
├── MOC_stability.ipynb
├── ZeroD.ipynb
└── lorenz_climatechange.ipynb
├── LICENSE
├── README.md
└── research
└── SEA_high_internal_variability.ipynb
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | GEOL157L/.ipynb_checkpoints/GEOL157_Lab6-checkpoint.ipynb
3 | GEOL157L/.ipynb_checkpoints/GEOL157L_lab4_Energy_balance_model-checkpoint.ipynb
4 |
--------------------------------------------------------------------------------
/GEOL157L/q_profile.txt:
--------------------------------------------------------------------------------
1 | 5.000000000000000409e-06
2 | 9.506670117881900093e-06
3 | 9.097150936618963202e-06
4 | 7.527250195670583047e-06
5 | 6.908494999558786973e-06
6 | 7.148576084876385717e-06
7 | 7.439902737179132002e-06
8 | 8.086488188364808119e-06
9 | 9.177937965851563101e-06
10 | 1.100210218045303382e-05
11 | 1.555236740757286743e-05
12 | 2.383667492375768343e-05
13 | 3.496137466513514524e-05
14 | 4.944577726386650829e-05
15 | 6.782710553782606369e-05
16 | 9.065589793907860713e-05
17 | 1.184920678314881374e-04
18 | 1.519015425460615385e-04
19 | 1.914534088636971965e-04
20 | 2.377174974838387387e-04
21 | 2.912623461519483364e-04
22 | 3.526534883802538538e-04
23 | 4.224520215736473011e-04
24 | 5.012134146273041976e-04
25 | 5.894865206156140553e-04
26 | 6.878127650483849883e-04
27 | 7.967254843771071895e-04
28 | 9.167493930554411821e-04
29 | 1.048400160563851824e-03
30 | 1.192184082465396703e-03
31 | 1.348597831830106956e-03
32 | 1.518128279304811149e-03
33 | 1.701252371761337963e-03
34 | 1.898437060871891943e-03
35 | 2.110139274170407756e-03
36 | 2.336805922195085011e-03
37 | 2.578873936194886994e-03
38 | 2.836770331644327629e-03
39 | 3.110912293465108516e-03
40 | 3.401707279415249092e-03
41 | 3.709553138590777725e-03
42 | 4.034838242402277730e-03
43 | 4.377941625749019218e-03
44 | 4.739233136423780073e-03
45 | 5.119073591051624336e-03
46 | 5.517814936098099435e-03
47 | 5.935800412684168713e-03
48 | 6.373364724121080224e-03
49 | 6.830834205229197389e-03
50 | 7.308526992637921597e-03
51 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/CVS/Entries:
--------------------------------------------------------------------------------
1 | /DummyGraphics.py/1.1.1.1/Sat May 1 20:38:29 2010//
2 | /planets.py/1.1.1.1/Sat May 1 20:38:30 2010//
3 | /ClimateGraphics.py/1.2/Fri Oct 28 14:31:38 2011//
4 | /ClimateUtilities.py/1.2/Thu Oct 13 01:46:00 2011//
5 | /ClimateGraphicsMPL.py/1.2/Tue Nov 12 21:55:32 2013//
6 | /phys.py/1.2/Sun May 20 20:04:40 2012//
7 | D
8 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/CVS/Repository:
--------------------------------------------------------------------------------
1 | PlanetaryClimateCourseware/CoursewareModules
2 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/CVS/Root:
--------------------------------------------------------------------------------
1 | moomintroll.uchicago.edu:/home/rtp1/cvsPlanetaryClimate
2 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/CVS/_notes/dwsync.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/ClimateGraphics.py:
--------------------------------------------------------------------------------
1 | #----------Section 3: Plotting utilities-------------------------------
2 | #These need to be localized, for systems that don't support Ngl
3 | #
4 | #This is imported into ClimateUtilities. If you want to use a
5 | #different graphics package (e.g. MatPlotLib) as a graphics driver
6 | #in place of Ngl, you only need to rewrite this module. The most
7 | #important thing to rewrite is the plot(...) function, which takes
8 | #a Curve object as input and produces a line plot. The plot(...)
9 | #function returns a plotObj object to allow further manipulation
10 | #(mostly saving the plot in various formats), but if you have a plotting
11 | #package that provides other ways of saving the plots, or if you just
12 | #want to save the plots by doing screen dumps, you can just have
13 | #the plot(...) function return None, or some dummy object.
14 | #
15 | #The other main function to customize is contour(...) which produces
16 | #contour plots of an array. This function is not very extensively
17 | #used in the courseware, so it is a lower priority for modification.
18 | #-----------------------------------------------------------------------
19 |
20 |
21 | #Note: On some older installations, Ngl has to
22 | #be imported as PyNGL instead of Ngl. If there
23 | #is a trouble with the import, fiddle with that.
24 | try:
25 | import Ngl
26 | except:
27 | try:
28 | import PyNGL as Ngl
29 | except:
30 | print "Ngl not found. Trying MatPlotLib"
31 | raise('Ngl Graphics Import Error')
32 |
33 | #A dummy class useful for passing parameters.
34 | #Just make an instance, then add new members
35 | #at will. Not sure why it also has to be defined
36 | #here, since it is also defined in ClimateUtilities,
37 | #but it seems to be necessary
38 | class Dummy:
39 | pass
40 |
41 | # A little class to make resources for Ngl plot options
42 | class resource:
43 | def __init__(self):
44 | self.trYReverse = False
45 | self.trXReverse = False
46 | self.trXLog = False
47 | self.trYLog = False
48 | self.nglFrame = False
49 | self.nglDraw = False
50 | #ToDo: Missing data code resource, line styles, line colors
51 |
52 | # A little class for use as a return object from plot(), so
53 | # the user has an easy way to delete a window or save a plot
54 | # to an eps file.
55 | class plotObj:
56 | def __init__(self,workstation,plot,WorkstationResources=None):
57 | self.workstation = workstation
58 | self.plot = plot
59 | self.WorkstationResources = WorkstationResources
60 | #Deletes a plot window
61 | def delete(self):
62 | Ngl.destroy(self.workstation)
63 | def save(self,filename = 'plot'):
64 | #'eps' workstation doesn't work reliably, but 'ps' is OK
65 | weps = Ngl.open_wks('ps',filename,self.WorkstationResources)
66 | Ngl.change_workstation(self.plot,weps)
67 | Ngl.draw(self.plot)
68 | Ngl.change_workstation(self.plot,self.workstation)
69 | Ngl.destroy(weps)
70 |
71 | #ToDo:
72 | # *Implement use of missing data coding.
73 | # *Provide some way to re-use window (e.g. by
74 | # returning w, and having it be an optional
75 | # argument. How to clear a window for re-use?
76 | # *Make some use of dashed lines in line styles
77 | # *The behavior of axis options like reverseX and
78 | # XlogAxis is confusing when switchXY = True. We
79 | # need to think about the semantics of what we
80 | # mean by the "X" axis, and stick to it. As
81 | # currently implemented the "X" axis means the
82 | # horizontal axis for these options. (However,
83 | # the semantics is different for the labeling options!)
84 | # If we change this (and we probably should), the
85 | # scripts plotting soundings will also need to be changed.
86 | # as well as the Workbook intructions and problem sets.
87 | #
88 | lineColors =range(3,203,20)+range(3,203,20)
89 | #Line colors for old versions of Ngl
90 | #lineColors =range(3,16)+range(3,16)
91 | lineThickness = [2,3,4,2,3,4,2,3,4,2,3,4,2,3,4,2,3,4,2,3,4]
92 | plotSymbols = [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5]
93 | def plot(c):
94 | r = resource()
95 | r.trYReverse = c.reverseY
96 | r.trXReverse = c.reverseX
97 | r.trXLog = c.XlogAxis
98 | r.trYLog = c.YlogAxis
99 | #
100 | #Line styles
101 | r.xyLineColors = lineColors
102 | r.xyLineThicknesses = lineThickness
103 | #Plot title
104 | r.tiMainString = c.PlotTitle
105 | #Axis labels (ToDo: add defaults)
106 | #X and Y axis labels
107 | r.tiXAxisString = c.Xlabel
108 | r.tiYAxisString = c.Ylabel
109 | if c.switchXY:
110 | r.tiXAxisString,r.tiYAxisString = r.tiYAxisString,r.tiXAxisString
111 |
112 | # Legends, for multicurve plot
113 | legends = []
114 | for id in c.listVariables():
115 | if not id == c.Xid:
116 | if len(c.label[id]) > 0:
117 | legends.append(c.label[id])
118 | else:
119 | legends.append(id)
120 | #ToDo: Add option to skip legends
121 | #Legends are redundant if there's only one curve
122 | if len(legends) > 1:
123 | r.pmLegendDisplayMode = "Always"
124 | r.xyExplicitLegendLabels = legends
125 | #
126 | #Suppress line drawing and just plot symbol for scatter plot curves
127 | r.xyMarkers = plotSymbols
128 | r.xyMarkerColors = lineColors
129 | r.xyMarkerSizeF = .01
130 | r.xyMarkLineModes = []
131 | for id in c.listVariables():
132 | if not id == c.Xid:
133 | if c.scatter[id]:
134 | r.xyMarkLineModes.append('Markers')
135 | else:
136 | r.xyMarkLineModes.append('Lines')
137 | #
138 | w = Ngl.open_wks('x11','Climate Workbook')
139 | if c.switchXY:
140 | plot = Ngl.xy(w,c.Y(),c.X(),r)
141 | else:
142 | plot = Ngl.xy(w,c.X(),c.Y(),r)
143 | #
144 | #Now draw the plot
145 | r.nglDraw = True
146 | Ngl.panel(w,[plot],[1,1],r)
147 | return plotObj(w,plot) #So that user can delete window or save plot
148 |
149 | # A basic contour plotter, which will plot a contour plot
150 | # of a Numeric array. The x and y scales can optionally
151 | # be specified using keyword arguments x and y. For example,
152 | # if we want the x scale to be the array (or list) lat, and
153 | # the y scale to be the array (or list) lon, we would call
154 | # contour as contour(A,x=lat,y=lon).
155 | def contour(A,**kwargs):
156 | #The following allows an expert user to pass
157 | #Ngl options directly to the plotter.
158 | #ToDo: Note that
159 | #options explicitly specified later will over-ride
160 | #what the user specified. This should be fixed,
161 | #by checking for things already specified. We should
162 | #also allow for using this resource to specify a color
163 | #map.
164 | if 'resource' in kwargs.keys():
165 | r = kwargs['resource']
166 | else:
167 | r = Dummy()
168 | #
169 | r.cnFillOn = True #Uses color fill
170 | # Set axes if they have been specified
171 | # as keyword arguments
172 | if 'x' in kwargs.keys():
173 | r.sfXArray = kwargs['x']
174 | if 'y' in kwargs.keys():
175 | r.sfYArray = kwargs['y']
176 | #
177 | # Now create the plot
178 |
179 | rw = Dummy()
180 | #Set the color map
181 | if 'colors' in kwargs.keys():
182 | if (kwargs['colors'] == 'gray') or (kwargs['colors'] == 'grey') :
183 | #Set the default greyscale
184 | rw.wkColorMap = 'gsdtol'
185 | else:
186 | rw.wkColorMap = kwargs['colors']
187 | else:
188 | #Default rainbow color table
189 | rw.wkColorMap = "temp1"
190 |
191 | w = Ngl.open_wks('x11','Climate Workbook',rw)
192 | r.nglDraw = False
193 | r.nglFrame = False
194 | plot = Ngl.contour(w,A,r)
195 | #Now draw the plot
196 | r.nglDraw = True
197 | Ngl.panel(w,[plot],[1,1],r)
198 | return plotObj(w,plot,rw) #So user can delete or save plot
199 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/ClimateGraphics.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CommonClimate/notebooks/576108231d5bbca8cbe6636752317f823b59429c/GEOL351/CoursewareModules/ClimateGraphics.pyc
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/ClimateGraphicsMPL.py:
--------------------------------------------------------------------------------
1 | #----------Section 3: Plotting utilities-------------------------------
2 | #Graphics interface customized for MatPlotLib
3 | #
4 | #This is imported into ClimateUtilities. If you want to use a
5 | #different graphics package (e.g. MatPlotLib) as a graphics driver
6 | #in place of Ngl, you only need to rewrite this module. The most
7 | #important thing to rewrite is the plot(...) function, which takes
8 | #a Curve object as input and produces a line plot. The plot(...)
9 | #function returns a plotObj object to allow further manipulation
10 | #(mostly saving the plot in various formats), but if you have a plotting
11 | #package that provides other ways of saving the plots, or if you just
12 | #want to save the plots by doing screen dumps, you can just have
13 | #the plot(...) function return None, or some dummy object.
14 | #
15 | #The other main function to customize is contour(...) which produces
16 | #contour plots of an array. This function is not very extensively
17 | #used in the courseware, so it is a lower priority for modification.
18 | #This routine has not yet been implemented for MatPlotLib
19 | #-----------------------------------------------------------------------
20 |
21 | #Change Log:
22 | # 1/25/2012: Fixed bug in legends that made problem with Enthought 7.2
23 | #
24 | # 11/12/2013: plot()--Implemented plotting of multiple curves with
25 | # differing resolution (MPL only, not NGL yet)
26 |
27 |
28 | #Try to import matplotlib plotting routines
29 | try:
30 | import matplotlib as mpl
31 | #Configure the backend so things work properly
32 | #with the Python intepreter and idle. Not needed if you are
33 | #using ipython. To work properly with idle, idle needs
34 | #to be started up with the command "idle -n". Note that
35 | #this trick limits the options for saving a file; only png
36 | #format is supported. If you want higher resolution formats
37 | #(notably eps) you should run using ipython -pylab , and eliminate
38 | #the following two commands.
39 | mpl.rcParams['backend'] = 'TkAgg'
40 | mpl.rcParams['interactive'] = True
41 | #
42 | import pylab as pl
43 | except:
44 | print 'matplotlib not found on your system'
45 | print 'You can still run the courseware, but'
46 | print 'will not be able to plot from inside Python'
47 |
48 | #A dummy class useful for passing parameters.
49 | #Just make an instance, then add new members
50 | #at will. Not sure why it also has to be defined
51 | #here, since it is also defined in ClimateUtilities,
52 | #but it seems to be necessary
53 | class Dummy:
54 | pass
55 |
56 | # A little class to make resources for Ngl plot options
57 | class resource:
58 | def __init__(self):
59 | self.trYReverse = False
60 | self.trXReverse = False
61 | self.trXLog = False
62 | self.trYLog = False
63 | self.nglFrame = False
64 | self.nglDraw = False
65 | #ToDo: Missing data code resource, line styles, line colors
66 |
67 | # A little class for use as a return object from plot(), so
68 | # the user has an easy way to delete a window or save a plot
69 | # to an eps file. Plot objects not implemented yet for MPL
70 | # Is there a way to save plots non-interactively in MPL?
71 | class plotObj:
72 | def __init__(self,workstation,plot,WorkstationResources=None):
73 | self.workstation = workstation
74 | self.plot = plot
75 | self.WorkstationResources = WorkstationResources
76 | #Deletes a plot window
77 | def delete(self):
78 | print "In MatPlotLib just click the goaway button to dispose a plot"
79 | def save(self,filename = 'plot'):
80 | #**ToDo: Can we implement non-interactive save in MPL?
81 | print "In MatPlotLib save plots interactively using the file button"
82 | print " in the plot window"
83 |
84 | #ToDo:
85 | # *Implement use of missing data coding.
86 | # *Provide some way to re-use window (e.g. by
87 | # returning w, and having it be an optional
88 | # argument. How to clear a window for re-use?
89 | # *Make some use of dashed lines in line styles
90 | # *The behavior of axis options like reverseX and
91 | # XlogAxis is confusing when switchXY = True. We
92 | # need to think about the semantics of what we
93 | # mean by the "X" axis, and stick to it. As
94 | # currently implemented the "X" axis means the
95 | # horizontal axis for these options. (However,
96 | # the semantics is different for the labeling options!)
97 | # If we change this (and we probably should), the
98 | # scripts plotting soundings will also need to be changed.
99 | # as well as the Workbook intructions and problem sets.
100 | #
101 |
102 | #List of line colors and line styles to use
103 | lineColors = ['b','g','r','c','m','y','k']
104 | lineStyles = ['-','--','-.',':']
105 | lineThickness = [2,3,4]
106 | plotSymbols = ['.','o','v','<','s','*','+','x']
107 | def plot(*curves):
108 | '''
109 | Plots Curve objects. plot(...) takes any number of Curve objects
110 | as arguments, and plots all of them on a single graph. If multiple
111 | Curve objects are passed as arguments, the plot appearance data
112 | (axis options, title, axis labels, etc) are taken from the first
113 | Curve object. The Curve objects need not all have the same data length.
114 | This is convenient for plotting data from multiple sources, with varying
115 | resolution.
116 | '''
117 | cMaster = curves[0] #Appearance options are taken from this Curve
118 |
119 | fig = pl.figure() #Always start a new figure
120 | #**ToDo: Make sure log-axis options so they work consistently
121 | # with Ngl implementation when x/y axes are switched.
122 | # (Looks OK, but is that implementation the logical way?)
123 | #r.trXLog = c.XlogAxis
124 | #r.trYLog = c.YlogAxis
125 | if cMaster.XlogAxis:
126 | pl.semilogx()
127 | if cMaster.YlogAxis:
128 | pl.semilogy()
129 | if cMaster.XlogAxis & cMaster.YlogAxis:
130 | pl.loglog()
131 | #
132 | #Line styles (Not needed in MPL, handled automatically)
133 | #r.xyLineColors = lineColors
134 | #r.xyLineThicknesses = lineThickness
135 | #Plot title
136 | #r.tiMainString = c.PlotTitle
137 | pl.title(cMaster.PlotTitle)
138 | #Axis labels (ToDo: add defaults)
139 | #X and Y axis labels
140 | #r.tiXAxisString = c.Xlabel
141 | #r.tiYAxisString = c.Ylabel
142 | if cMaster.switchXY:
143 | pl.ylabel(cMaster.Xlabel)
144 | pl.xlabel(cMaster.Ylabel)
145 | else:
146 | pl.xlabel(cMaster.Xlabel)
147 | pl.ylabel(cMaster.Ylabel)
148 |
149 | # Legends, for multicurve plot
150 | legends = []
151 | for c in curves:
152 | for id in c.listVariables():
153 | if not id == c.Xid:
154 | if len(c.label[id]) > 0:
155 | legends.append(c.label[id])
156 | else:
157 | legends.append(id)
158 | #ToDo: Add option to skip legends
159 | #
160 | #Suppress line drawing and just plot symbol for scatter plot curves
161 | #**ToDo: Implement for MatPlotLib
162 | #r.xyMarkers = plotSymbols
163 | #r.xyMarkerColors = lineColors
164 | #r.xyMarkerSizeF = .01
165 | #r.xyMarkLineModes = []
166 | formatList = []
167 | count = 0
168 | for c in curves:
169 | for id in c.listVariables():
170 | if not id == c.Xid:
171 | if c.scatter[id]:
172 | #r.xyMarkLineModes.append('Markers')
173 | color = lineColors[count%len(lineColors)]
174 | symbol = plotSymbols[count%len(plotSymbols)]
175 | formatList.append(color+symbol)
176 | else:
177 | color = lineColors[count%len(lineColors)]
178 | style = lineStyles[count%len(lineStyles)]
179 | formatList.append(color+style)
180 | count += 1
181 | #
182 | plotList = [] #Mainly so we can add legends. Could be done with label = ...
183 | #Note that pl.plot returns a list of lines, even if there is only
184 | # one line in the plot, so for legends to work correctly,
185 | # we need to extract just the first element.
186 | countAll = 0
187 | for c in curves:
188 | if cMaster.switchXY:
189 | count = 0
190 | for data in c.Y():
191 | plotList.append(pl.plot(data,c.X(),formatList[countAll])[0])
192 | count += 1
193 | countAll += 1
194 | else:
195 | count = 0
196 | for data in c.Y():
197 | plotList.append(pl.plot(c.X(),data,formatList[countAll])[0])
198 | count += 1
199 | countAll += 1
200 | #Do the legends
201 | #11/12/2013: Added Jonah's trick to put legends on the side
202 | ax = pl.subplot(111)
203 | box = ax.get_position()
204 | ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
205 | pl.legend(plotList,legends,loc='center left', bbox_to_anchor=(1, 0.5))
206 | #
207 | #Do the axis reversal
208 | #We do it here, since we don't know the axis limits until
209 | #plotting is done
210 | #r.trYReverse = c.reverseY
211 | #r.trXReverse = c.reverseX
212 | axes = pl.gca() #Gets current axis
213 | if cMaster.reverseX:
214 | axes.set_xlim(axes.get_xlim()[::-1]) #::-1 reverses the array
215 | if cMaster.reverseY:
216 | axes.set_ylim(axes.get_ylim()[::-1])
217 | #Now re-draw the plot
218 | pl.draw()
219 | #(Insert commands needed to show plot, if necessary)
220 | return plotObj(None,fig) #Eventually we will use this to make subplots and do save option
221 |
222 | # A basic contour plotter, which will plot a contour plot
223 | # of a Numeric array. The x and y scales can optionally
224 | # be specified using keyword arguments x and y. For example,
225 | # if we want the x scale to be the array (or list) lat, and
226 | # the y scale to be the array (or list) lon, we would call
227 | # contour as contour(A,x=lat,y=lon).
228 | def contour(A,**kwargs):
229 | #**ToDo: Add labeled contour lines, option to change contour levels,
230 | # and to change palette.
231 | #
232 | fig = pl.figure() #Always start a new figure
233 | # Set axes if they have been specified
234 | # as keyword arguments
235 | if 'x' in kwargs.keys():
236 | x = kwargs['x']
237 | else:
238 | x = range(A.shape[1])
239 | if 'y' in kwargs.keys():
240 | y = kwargs['y']
241 | else:
242 | y = range(A.shape[0])
243 | cs = pl.contourf(x,y,A)
244 | cbar = pl.colorbar(cs)
245 | return plotObj(None,fig)
246 | ## #The following allows an expert user to pass
247 | ## #Ngl options directly to the plotter.
248 | ## #ToDo: Note that
249 | ## #options explicitly specified later will over-ride
250 | ## #what the user specified. This should be fixed,
251 | ## #by checking for things already specified. We should
252 | ## #also allow for using this resource to specify a color
253 | ## #map.
254 | ## if 'resource' in kwargs.keys():
255 | ## r = kwargs['resource']
256 | ## else:
257 | ## r = Dummy()
258 | ##
259 | ## rw = Dummy()
260 | ## #Set the color map
261 | ## if 'colors' in kwargs.keys():
262 | ## if (kwargs['colors'] == 'gray') or (kwargs['colors'] == 'grey') :
263 | ## #Set the default greyscale
264 | ## rw.wkColorMap = 'gsdtol'
265 | ## else:
266 | ## rw.wkColorMap = kwargs['colors']
267 | ## else:
268 | ## #Default rainbow color table
269 | ## rw.wkColorMap = "temp1"
270 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/ClimateGraphicsMPL.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CommonClimate/notebooks/576108231d5bbca8cbe6636752317f823b59429c/GEOL351/CoursewareModules/ClimateGraphicsMPL.pyc
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/ClimateUtilities.py:
--------------------------------------------------------------------------------
1 |
2 | #ToDo: *Check for right column length in setitem and addCurve
3 | #
4 | # *Implement show,hide for curves (in X() and Y())
5 | # *Implement missing data coding .
6 | # self.setMissingDataCode(code) (char or numeric)
7 | # possibly translate, check and force consistency
8 | # *Possibly handle missing data for plotting using a
9 | # fill() or interp() method to fill in missing data using
10 | # the interpolation routine. The best way to handle
11 | # missing data in computations is by using Masked Arrays
12 | #
13 | # *Make and import a PathFinder module, which the instructor
14 | # will customize for the site. This module will help the
15 | # students find locations of datasets and chapter scripts.
16 | # Could provide "links" to find script that produced a given
17 | # figure in the text. Alternately, we could just
18 | # define directory strings like WorkbookDatasets here in
19 | # ClimateUtilities.
20 | #
21 | # *Note: Other courseware modules should avoid
22 | # importing numpy or Numeric directly. Get it
23 | # by using "from ClimateUtilities import *" so
24 | # that the preferred version is imported automatically
25 | #
26 | # *Looking ahead to Python 3, all print statements should
27 | # be changed to some kind of "message" function, which
28 | # can invoke either python 2.xx "print" or python 3 print(...)
29 | #
30 | #
31 |
32 | #-----------------------------------------------
33 | #
34 | #Import array package
35 | #
36 | #First try to import numpy, then fall back
37 | #on Numeric. Arrange things so that the array
38 | #package can be interchangeably referenced as
39 | #either numpy.* or Numeric.* , though it is really
40 | #numpy that is fully supported. (Almost everything
41 | #will continue to work with Numeric, though, which
42 | #may be useful for older installations)
43 | #------------------------------------------------
44 |
45 | try:
46 | import numpy
47 | import numpy as Numeric #For backwards compatibility
48 | numpy.Float = numpy.float #For backwards compatibility
49 | except:
50 | try:
51 | import Numeric
52 | import Numeric as numpy #For frontwards compatibility
53 | numpy.float = numpy.Float #For frontwards compatibility
54 | print "numpy not found. Using Numeric instead"
55 | print "Everything should still work, but consider upgrading to numpy"
56 | except:
57 | print "Neither numpy nor Numeric found."
58 | print "Please install numpy (preferred) or Numeric."
59 |
60 | #-----------------------------------------------------
61 | #Import graphics utilities
62 | #
63 | #First try to import ClimateGraphicsMPL. which contains the
64 | #implementation of the plotting commands using MatPlotLib as a driver.
65 | #The alternative version, ClimateGraphics, uses PyNgl as a driver,
66 | #and if MPL is not found, the script looks for the version using
67 | #Ngl. It is fairly easy to adapt either ClimateGraphics module to
68 | #use other graphics drivers. If the import fails, a
69 | #dummy graphics stub module is imported, which allows the courseware
70 | #to be run without the user needing to explicitly comment out
71 | #the plot calls in the Chapter Scripts.
72 | #------------------------------------------------------
73 |
74 | try:
75 | from ClimateGraphicsMPL import * #Try importing MatPlotLib
76 | print "Using MatPlotLib graphics"
77 | except:
78 | # If Ngl not found, try importing the graphics interface
79 | # that uses MatPlotLib. If you have both installed and
80 | #prefer MatPlotLib you can change the order of imports here
81 | #or just do "from ClimateGraphicsMPL import *" after you
82 | #import ClimateUtilities .
83 | try:
84 | from ClimateGraphics import * #Try importing Ngl driver
85 | print "Using Ngl graphics"
86 | except:
87 | print " "
88 | print "Graphics not implemented."
89 | print "Plot routines will not produce graphs."
90 | print "Instead, you can save results from a Curve"
91 | print "object c into a text file using c.dump()"
92 | print "and then plot the data using the graphics program"
93 | print "of your choice."
94 | from DummyGraphics import *
95 |
96 | #Section 1: -----Data handling utilities-------------------------------
97 |
98 | # ToDo: Put documentation on use of Curve object here!
99 | #
100 | # Add keyword arguments for axes, etc,
101 | #
102 | # How to handle missing data codes? Note that
103 | # since lists are converted to Numeric arrays,
104 | # text codes can't be used.
105 | # Provide a method to set which data is X.
106 | #
107 | # Add output option to put out a LaTeX formatted table
108 | #
109 | # Provide an easy way to add data column long-names
110 | #
111 | class Curve:
112 | def __init__(self):
113 | self.Xid = None # id of data to be considered as X
114 | self.data = {} # Dictionary of data columns
115 | self.label = {} #Data column label dictionary
116 | self.scatter = {} #Marker for making a curve a scatter plot
117 | #i.e. suppress line drawing and plot symbol
118 | self.idList = [] #Keeps track of original order of data
119 | self.NumCurves = 0
120 | self.description = None # A string providing general information
121 | self.PlotTitle = '' # Title of the plot
122 | self.switchXY = 0 # Switches X and Y axes for plotting
123 | self.reverseX = 0 # Reverses X axis for plotting
124 | self.reverseY = 0 # Reverses Y axis for plotting
125 | self.XlogAxis = 0 # Use logarithmic axis for X
126 | self.YlogAxis = 0 # Use logarithmic axis for Y
127 | self.Xlabel = '' #X axis label
128 | self.Ylabel = '' #Y axis label
129 | #
130 | # Colors and line titles
131 | #
132 | #
133 | #Install a new curve in the data set, optionally with a variable name and label
134 | #Any 1D indexable object can be installed here:a Numeric array, a Masked Array,
135 | #a Masked Variable (as in cdms) or an ordinary list.
136 | def addCurve(self,data,id = '',label = ''):
137 | self.NumCurves += 1
138 | if len(id) == 0:
139 | id = 'v%d'%(self.NumCurves-1)
140 | #Transform data from list to a Numeric array here
141 | if type(data) == type([]):
142 | data = Numeric.array(data)
143 | self.data[id] = data
144 | if self.Xid == None:
145 | self.Xid = id #Sets the default id for the X variable
146 | self.idList.append(id) #Keep track of order in which columns added
147 | self.label[id] = label
148 | self.scatter[id] = False
149 | #ToDo: Add checking for consistent lengths, type, etc. of what's being added
150 | def listVariables(self):
151 | return self.idList
152 | def __getitem__(self,id):
153 | return self.data[id]
154 | def __setitem__(self,id,data):
155 | try:
156 | n = len(data[:])
157 | except:
158 | print "Object on RHS is not indexable"
159 | return None
160 | #Transform data from list to a Numeric array here
161 | if type(data) == type([]):
162 | data = Numeric.array(data)
163 | if id in self.data.keys():
164 | self.data[id] = data
165 | else:
166 | self.addCurve(data,id)
167 |
168 | #Method to return Numeric abcissa array for plotting.
169 | def X(self):
170 | #Use of cross section lets us get data from any indexed object
171 | #However, since Masked variables and Masked Arrays yield
172 | #their same types as cross sections, we have to check
173 | #explicitly for a _data component.
174 | #
175 | #This method is used mainly for generating arrays used in plotting,
176 | #and should be streamlined at some point.
177 | temp = self.data[self.Xid][:]
178 | if hasattr(temp,'_data'):
179 | temp = temp._data[:]
180 | return Numeric.array(temp,Numeric.Float)
181 |
182 | #Method to return Numeric ordinate array for plotting
183 | def Y(self):
184 | #Use of cross section lets us get data from any indexed object
185 | outArray = []
186 | for id in self.idList:
187 | if not (id == self.Xid):
188 | column = self.data[id]
189 | if hasattr(column,'_data'):
190 | outArray.append(column._data[:]) #Deals with masked arrays and variables
191 | else:
192 | outArray.append(column[:])
193 | return Numeric.array(outArray,Numeric.Float)
194 |
195 | #Dumps curve to a tab-delimited ascii file with column header
196 | def dump(self,fileName = 'out.txt'):
197 | outfile = open(fileName,'w')
198 | # Write out the data description if it is available.
199 | if not (self.description == None):
200 | if not self.description[-1] == '\n':
201 | self.description += '\n' #Put in a newline if needed
202 | outfile.write(self.description)
203 | header = ""
204 | fmt = ""
205 | ids = self.idList
206 | for id in ids:
207 | header += id+'\t'
208 | fmt += '%e\t'
209 | header = header[0:-1]+'\n' # Replaces last tab with a newline.
210 | fmt = fmt[0:-1] + '\n'
211 | outfile.write(header)
212 | for i in range(len(self.data[ids[0]])):
213 | out = tuple([(self.data[id])[i] for id in ids])
214 | outfile.write(fmt%out)
215 | outfile.close()
216 |
217 | #Extracts a subset of the data and returns it as a new Curve.
218 | #This is useful if you only want to plot some of the columns.
219 | #The input argument dataList is a list of column names
220 | def extract(self,dataList):
221 | c = Curve()
222 | for dataName in dataList:
223 | c.addCurve(self[dataName],dataName)
224 | return c
225 |
226 |
227 |
228 |
229 |
230 | from string import atof
231 |
232 |
233 | # Scans a list of lines, locates data lines
234 | # and size, splits of column headers and
235 | # splits off general information text.
236 | #
237 | #A header can optionally be specified as input.
238 | #The delimiter need not be specified if the file
239 | #uses any whitespace character (including tabs) as
240 | #column delimiters. Commas are not whitespace characters,
241 | #and so need to be specified if they are used.
242 | #
243 | #ToDo:
244 | # *Implement missing data coding (with default '-').
245 | # self.setMissingDataCode(code) (char or numeric)
246 | # possibly translate, check and force consistency
247 | # *Replace optional positional arguments with keyword arguments
248 | def scan(buff,inHeader=None,delimiter = None):
249 | if inHeader == None:
250 | inHeader = []
251 | #First delete blank lines
252 | buff = clean(buff)
253 | #
254 | #Now look for patterns that indicate data lines
255 | #
256 | startDataLine,endDataLine = findData(buff)
257 | # Found number of items. Now read in the data
258 | #
259 | #Read in the first line. Is it a header?
260 | header = []
261 | if delimiter == None:
262 | line = buff[startDataLine].split()
263 | else:
264 | line = buff[startDataLine].split(delimiter)
265 | #
266 | try:
267 | atof(line[0])
268 | except:
269 | header = line
270 | if len(header) == 0:
271 | header = ['V%d'%i for i in range(len(line))]
272 | istart = 0
273 | else:
274 | istart = 1
275 | # Replace with inHeader if inHeader has been specified
276 | # (Only use the input header if its length is consistent)
277 | if len(inHeader) == len(line):
278 | header = inHeader
279 | #
280 | # Read in the rest of the lines
281 | #
282 | varlist = [[] for i in range(len(header))]
283 | for line in buff[(startDataLine+istart):endDataLine]:
284 | if delimiter == None:
285 | items = line.split()
286 | else:
287 | items = line.split(delimiter)
288 | try:
289 | for i in range(len(varlist)):
290 | varlist[i].append(atof(items[i]))
291 | except:
292 | print items
293 | vardict = {}
294 | for name in header:
295 | vardict[name] = varlist[header.index(name)]
296 | return vardict,header #header is returned so we can keep cols in orig order
297 |
298 | #Eliminates blank lines
299 | def clean(buff):
300 | buff = [line.strip() for line in buff]
301 | while(1):
302 | try:
303 | buff.remove('')
304 | except:
305 | break
306 | return buff
307 |
308 | def findData(buff):
309 | runStarts = []
310 | for i in range(len(buff)-1):
311 | dn = abs(len(buff[i].split())-len(buff[i+1].split()))
312 | if not dn==0:
313 | runStarts.append(i+1)
314 | runStarts.append(len(buff))
315 | #Find index of run with max length
316 | nmax = -1
317 | for i in range(len(runStarts)-1):
318 | n = runStarts[i+1]-runStarts[i]
319 | if n > nmax:
320 | nmax = n
321 | imax = runStarts[i]
322 | #Deal with case where entire file is one run
323 | if nmax == -1:
324 | nmax = len(buff)
325 | imax = 0
326 | return imax,imax+nmax
327 |
328 |
329 | #
330 | # Function to read space or tab-delimited file into a curve object
331 | # The input header is a list of names of the variables in each
332 | # columns, which can be input optionally, mainly to deal with
333 | # the case in which this information is not in the file being read
334 | import string
335 | def readTable(filename,inHeader = None,delimiter = None):
336 | f = open(filename)
337 | buff = f.readlines()
338 | data,header = scan(buff,inHeader,delimiter)
339 | c = Curve()
340 | for key in header:
341 | c.addCurve(data[key],key)
342 | return c
343 |
344 |
345 |
346 |
347 | #==============================================
348 | #---Section 2: Math utilities-------------------------------------------
349 | #==============================================
350 |
351 | #A dummy class useful for passing parameters.
352 | #Just make an instance, then add new members
353 | #at will.
354 | class Dummy:
355 | pass
356 |
357 | #---Polynomial interpolation and extrapolation (adapted
358 | #from Numerical Recipes.
359 | #
360 | #It's used in Romberg extrapolation, but could be useful
361 | #for polynomial OLR fits and so forth as well. Also
362 | #needs online documentation
363 | def polint(xa,ya,x):
364 | n = len(xa)
365 | if not (len(xa) == len(ya)):
366 | print "Input x and y arrays must be same length"
367 | return "Error"
368 | #Set up auxiliary arrays
369 | c = Numeric.zeros(n,Numeric.Float)
370 | d = Numeric.zeros(n,Numeric.Float)
371 | c[:] = ya[:]
372 | d[:] = ya[:]
373 | #Find closest table entry
374 | ns = 0
375 | diff = abs(xa[0]-x)
376 | for i in range(n):
377 | difft = abs(xa[i]-x)
378 | if difft < diff:
379 | diff = difft
380 | ns = i
381 | y=ya[ns]
382 | for m in range(1,n):
383 | for i in range(n-m):
384 | ho=xa[i]-x
385 | hp=xa[i+m]-x
386 | w=c[i+1]-d[i]
387 | c[i] = ho*w/(ho-hp)
388 | d[i] = hp*w/(ho-hp)
389 | if 2*ns < (n-m):
390 | dy = c[ns]
391 | else:
392 | ns -= 1
393 | dy = d[ns]
394 | y += dy
395 | #You can also return dy as an error estimate. Here
396 | #to keep things simple, we just return y.
397 | return y
398 |
399 | #---------------------------------------------------------
400 | # Class for doing polynomial interpolation
401 | # from a table, using polint
402 | #---------------------------------------------------------
403 | #
404 | #Usage:
405 | # Let xa be a list of independent variable
406 | # values and ya be a list of the corresponding
407 | # dependent variable values. Then, to create a function
408 | # f (actually a callable object, techically) that interpolates
409 | # or extrapolates to any value x, create f using
410 | # f = interp(xa,ya)
411 | # Then you can get the value you want by invoking f(x)
412 | # for your desired x.
413 | #
414 | # By default, the interpolator does fourth-order interpolation
415 | # using the four nearest neighbors. You can change this by
416 | # using an optional third argument to the creator. For
417 | # example
418 | #
419 | # f = interp(xa,ya,8)
420 | # will use the 8 nearest neighbors (if they are available)
421 | #------------------------------------------------------------
422 | class interp:
423 | def __init__(self,xa,ya,n=4):
424 | self.xa = Numeric.array(xa)
425 | self.ya = Numeric.array(ya)
426 | self.n = n
427 | def __call__(self,x):
428 | #Find the closes index to x
429 | if self.xa[0] < self.xa[-1]:
430 | i = Numeric.searchsorted(self.xa,x)
431 | else:
432 | i = Numeric.searchsorted(-self.xa,-x)
433 | i1 = max(i-self.n,0)
434 | i2 = min(i+self.n,len(self.xa))
435 | return polint(self.xa[i1:i2],self.ya[i1:i2],x)
436 |
437 |
438 | #----Quadrature (definite integral) by Romberg extrapolation.
439 | #**ToDo: Add documentation and help string
440 |
441 | #Before developing a general quadrature class, we'll
442 | #implement a class which efficiently carries out trapezoidal rule
443 | #integration with iterative refinement
444 | class BetterTrap:
445 | def __init__(self,f,params,interval,nstart):
446 | self.f = f
447 | self.n = nstart
448 | self.interval = interval
449 | self.params = params
450 | self.integral = self.dumbTrap(nstart)
451 | def dumbTrap(self,n):
452 | a = self.interval[0]
453 | b = self.interval[1]
454 | dx = (b-a)/n
455 | sum = dx*(self.f(a,self.params)+self.f(b,self.params))/2.
456 | for i in range(1,n):
457 | x = a+i*dx
458 | sum = sum + self.f(x,self.params)*dx
459 | return sum
460 | def refine(self):
461 | #Compute the sum of f(x) at the
462 | #midpoints between the existing intervals.
463 | #To get the refinement of the trapezoidal
464 | #rule sum we just add this to half the
465 | #previous result
466 | sum = 0.
467 | a = self.interval[0]
468 | b = self.interval[1]
469 | dx = (b-a)/self.n
470 | #Remember: n is the number of subintervals,
471 | #not the number of endpoints. Therefore we
472 | #have one midpoint per subinterval. Keeping that
473 | #in mind helps us get the range of i right in
474 | #the following loop
475 | for i in range(self.n):
476 | sum = sum + self.f(a+(i+.5)*dx,self.params)*(dx/2.)
477 | #The old trapezoidal sum was multiplied by
478 | #the old dx. To get its correct contribution
479 | #to the refined sum, we must multiply it by .5,
480 | #because the new dx is half the old dx
481 | self.integral = .5*self.integral + sum
482 | #
483 | #Update the number of intervals
484 | self.n = 2*self.n
485 |
486 |
487 | #Here I define a class called
488 | #romberg, which assists in carrying out evaluation of
489 | #integrals using romberg extrapolation. It assumes polint has
490 | #been imported
491 |
492 | class romberg:
493 | def __init__(self,f,nstart=4):
494 | self.nstart = nstart
495 | self.trap = None
496 | #
497 | #-------------------------------------------------
498 | #This snippit of code allows the user to leave the
499 | #parameter argument out of the definition of f
500 | #if it isn't needed
501 | #
502 | self.fin = f
503 | #Find the number of arguments of f and append a
504 | #parameter argument if there isn't any.
505 | nargs = f.func_code.co_argcount
506 | if nargs == 2:
507 | self.f = f
508 | elif nargs ==1:
509 | def f1(x,param):
510 | return self.fin(x)
511 | self.f = f1
512 | else:
513 | name = f.func_name
514 | print 'Error: %s has wrong number of arguments'%name
515 | #-----------------------------------------------------
516 | #
517 | #We keep lists of all our results, for doing
518 | #Romberg extrapolation. These are re-initialized
519 | #after each call
520 | self.nList = []
521 | self.integralList = []
522 | def refine(self):
523 | self.trap.refine()
524 | self.integralList.append(self.trap.integral)
525 | self.nList.append(self.trap.n)
526 | dx = [1./(n*n) for n in self.nList]
527 | return polint(dx,self.integralList,0.)
528 | #
529 | #Use a __call__ method to return the result. The
530 | #__call__ method takes the interval of integration
531 | #as its mandatory first argument,takes an optional
532 | #parameter argument as its second argument, and
533 | #an optional keyword argument specifying the accuracy
534 | #desired.
535 | #**ToDo: Introduce trick to allow parameter argument of
536 | #integrand to be optional, as in Integrator. Also, make
537 | #tolerance into a keyword argument
538 | #
539 | def __call__(self,interval,params=None,tolerance=1.e-6):
540 | self.nList = []
541 | self.integralList = []
542 | #Make a trapezoidal rule integrator
543 | self.trap = BetterTrap(self.f,params,interval,self.nstart)
544 | self.nList.append(self.nstart)
545 | self.integralList.append(self.trap.integral)
546 | #
547 | #Refine initial evaluation until
548 | oldval = self.refine()
549 | newval = self.refine()
550 | while abs(oldval-newval)>tolerance:
551 | oldval,newval = newval,self.refine()
552 | return newval
553 |
554 |
555 |
556 | #-------Runge-Kutta ODE integrator
557 | #**ToDo:
558 | # * Implement a reset() method which resets to initial conditions.
559 | # Useful for doing problem over multiple times with different
560 | # parameters.
561 | #
562 | # * Referring to the independent variable as 'x' is awful, and
563 | # confusing in many contexts. Introduce a variable name
564 | # dictonary with default names like 'independent' and 'dependent'
565 | # (and short synonyms) so if fi is the integrator object
566 | # fi['indep'] is the current value of the independent variable
567 | # and fi['dep'] is the current (vector) value of the dependent
568 | # variable. Then allow user to rename or synonym these to
569 | # the actual user-supplied names. This is an alternative
570 | # to using the list returned by fi.next(). Then expunge
571 | # all references to things like fi.x from the examples and
572 | # chapter scripts. They are too confusing. Similarly,
573 | # change the name of the increment from dx to something else
574 | #
575 | # * Similarly, we could introduce a dictionary of some sort
576 | # to make it easier to set up multidimensional systems and
577 | # refer to the different vector components by name
578 | # (e.g. refer to v[0] at T, v[1] as dTdy , etc. )
579 | #
580 | # * Make the integrator object callable. The call can return
581 | # a list of results for all the intermediate steps, or optionally
582 | # just the final value.
583 | class integrator:
584 | '''
585 | Runge-Kutta integrator, for 1D or multidimensional problems
586 |
587 | Usage:
588 |
589 | First you define a function that returns the
590 | derivative(s), given the independent and dependent
591 | variables as arguments. The independent variable (think
592 | of time) is always a scalar. The dependent variable (think
593 | of the x and y coordinates of a planet) can be either a scalar
594 | or a 1D array, according to the problem. For the
595 | multidimensional case, this integrator will work with any
596 | kind of array that behaves like a Numeric array, i.e. supports
597 | arithmetic operations. It will not work with plain Python lists.
598 | The derivative function should return an array of derivatives, in
599 | the multidimensional case. The derivative function can have any name.
600 |
601 | The derivative function can optionally have a third argument, to provide
602 | for passing parameters (e.g. the radius of the Earth) to the
603 | function. The "parameter" argument, if present, can be any Python
604 | entity whatsoever. If you need to pass multiple constants, or
605 | even tables or functions, to your derivative function, you can
606 | stuff them all into a list or a Python object.
607 |
608 |
609 | Example:
610 | In the 1D case, to solve the equation
611 | dz/dt = -a*t*t/(1.+z*z)
612 | in which z is the dependent variable and t is the
613 | independent variable, your derivative function would be
614 | def g(t,z):
615 | return -a*t*t/(1.+z*z)
616 |
617 | treating the parameter a as a global, or perhaps
618 | def g(t,z,params):
619 | return -params.a*t*t/(params.b+z*z)
620 |
621 | while in a 2D case, your function might look like:
622 | def g(t,z):
623 | return Numeric.array([-z[1],z[0]])
624 |
625 | or perhaps something like:
626 | def g(t,z):
627 | return t*Numeric.sin(z)
628 |
629 | or even
630 | def g(t,z,params):
631 | return Numeric.matrixmultiply(params(t),z)
632 |
633 | where params(t) in this case is a function returning
634 | a Numeric square matrix of the right dimension to multiply z.
635 |
636 | BIG WARNING: Note that all the examples which return a
637 | Numeric array return a NEW INSTANCE (i.e. copy) of an
638 | array. If you try to set up a global array and re-use
639 | it to return your results from g, you will really be
640 | just returning a REFERENCE to the same array each time,
641 | and each call will change the value of all the previous
642 | results. This will mess up the computation of intermediate
643 | results in the Runge-Kutta step. An example of the sort of thing
644 | that will NOT work is:
645 | zprime = Numeric.zeros(2,Numeric.Float)
646 | def g(t,z,params):
647 | zprime[0] = z[1]
648 | zprime[1] = -z[0]
649 | return zprime
650 | Try it out. This defines the harmonic oscillator, and a plot
651 | of the orbit should give a circle. However, it doesn't The problem
652 | reference/value distinction. The right way to define the function
653 | would be
654 | def g(t,z):
655 | return Numeric.array([z[1],-z[0]])
656 | Try this one. It should work properly now. Note that any arithmetic
657 | performed on Numeric array objects returns a new instance of an Array
658 | object. Hence, a function definition like
659 | def g(t,z):
660 | return t*z*z+1.
661 | will work fine.
662 |
663 | Once you have defined the derivitave function,
664 | you then proceed as follows.
665 |
666 | First c reate an integrator instance:
667 | int_g = integrator(g,0.,start,.01)
668 |
669 | where "0." in the argument list is the initial value
670 | for the independent variable, "start" is the initial
671 | value for the dependent variable, and ".01" is the
672 | step size. You then use the integrator as follows:
673 |
674 | int_g.setParams(myParams)
675 | while int_g.x < 500:
676 | print int_g.next()
677 |
678 | The call to setParams is optional. Just use it if your
679 | function makes use of a parameter object. The next() method
680 | accepts the integration increment (e.g. dx) as an optional
681 | argument. This is in case you want to change the step size,
682 | which you can do at any time. The integrator continues
683 | using the most recent step size it knows.
684 |
685 | Each call to int_g.next returns a list, the first of whose
686 | elements is the new value of the independent variable, and
687 | the second of whose elements is a scalar or array giving
688 | the value of the dependent variable(s) at the incremented
689 | independent variable.
690 |
691 | '''
692 | def __init__(self, derivs,xstart,ystart,dx=None):
693 | self.derivsin = derivs
694 | #
695 | #The following block checks to see if the derivs
696 | #function has a parameter argument specified, and
697 | #writes a new function with a dummy parameter argument
698 | #appended if necessary. This allows the user to leave
699 | #out the parameter argument from the function definition,
700 | #if it isn't needed.
701 | nargs = derivs.func_code.co_argcount
702 | if nargs == 3:
703 | self.derivs = derivs
704 | elif nargs == 2:
705 | def derivs1(x,y,param):
706 | return self.derivsin(x,y)
707 | self.derivs = derivs1
708 | else:
709 | name = derivs.func_name
710 | print 'Error: %s has wrong number of arguments'%name
711 | #
712 | #
713 | self.x = xstart
714 | #The next statement is a cheap trick to initialize
715 | #y with a copy of ystart, which works whether y is
716 | #a regular scalar or a Numeric array.
717 | self.y = 0.+ ystart
718 | self.dx = dx #Can instead be set with the first call to next()
719 | self.params = None
720 | #Sets the parameters for the integrator (optional).
721 | #The argument can be any Python entity at all. It is
722 | #up to the user to make sure the derivative function can
723 | #make use of it.
724 | def setParams(self,params):
725 | self.params = params
726 | #Computes next step. Optionally, takes the increment
727 | #in the independent variable as an argument. The
728 | #increment can be changed at any time, and the most
729 | #recently used value is remembered, as a default
730 | def next(self,dx = None):
731 | if not (dx == None):
732 | self.dx = dx
733 | h = self.dx
734 | hh=h*0.5;
735 | h6=h/6.0;
736 | xh=self.x+hh;
737 | dydx = self.derivs(self.x,self.y,self.params)
738 | yt = self.y+hh*dydx
739 | dyt = self.derivs(xh,yt,self.params)
740 | yt =self.y+hh*dyt
741 | dym = self.derivs(xh,yt,self.params)
742 | yt =self.y+h*dym
743 | dym += dyt
744 | dyt = self.derivs(self.x+h,yt,self.params)
745 | self.y += h6*(dydx+dyt+2.0*dym)
746 | self.x += h
747 | return self.x,self.y
748 |
749 |
750 |
751 | #**ToDo:
752 | #
753 | # Store the previous solution for use as the next guess(?)
754 | #
755 | # Handle arithmetic exceptions in the iteration loop
756 | #
757 | class newtSolve:
758 | '''
759 | Newton method solver for function of 1 variable
760 | A class implementing Newton's method for solving f(x) = 0.
761 |
762 | Usage: solver = newtSolve(f), where f is a function with
763 | calling sequence f(x,params). Values of x such that
764 | f(x,params) = 0 are
765 | then found by invoking solver(guess), where guess
766 | is the initial guess. The solver returns the string
767 | 'No Convergence' if convergence fails. The argument
768 | params allows parameters to be passed to the function.
769 | It can be left out of the function definition if you don't
770 | need it. Note that params can be any Python object at all
771 | (including,e.g.,lists, functions or more complex user-defined objects)
772 |
773 | Optionally, one can specify the derivative function
774 | in the creator,e.g. solver = newtSolve(f,fp).
775 | If the derivative function isn't specified, the solver
776 | computes the derivative approximately using a centered
777 | difference. Note that in either case you can access
778 | the derivative function by invoking solver.deriv(x)
779 | As for f, fp can be optionally defined with a parameter
780 | argument if you need it. The same parameter object is
781 | passed to f and fp.
782 |
783 | Use solver.setParams(value) to set the parameter object
784 | Alternately, the parameter argument can be passed as
785 | an optional second argument in the solver call. (see
786 | example below).
787 |
788 | Adjustable constants:
789 | eps Increment for computing numerical approximation to
790 | the derivative of f
791 | tolerance Accuracy criterion for ending the iteration
792 | (an approximation to the error in the root)
793 | nmax maximum number of iterations
794 |
795 | e.g. to change the maximum number of iterations for an instance
796 | of the class, set solver.nmax = 10 .
797 | ----------------Usage Examples-----------------------------
798 |
799 | Example 1: Function without parameters:
800 | def g(x):
801 | return x*x - 1.
802 | roots = newtSolve(g)
803 | roots(2.)
804 |
805 | Example 2, Function with parameters:
806 | def g(x,constants):
807 | return constants.a*x*x - constants.b
808 | roots = newtSolve(g)
809 | constants = Dummy()
810 | constants.a = 1.
811 | constants.b = 2.
812 | roots.setParam(constants)
813 | roots(2.)
814 | roots(1.)
815 |
816 | Example 2a:
817 | Instead of using roots.setParam(...) we could do
818 | roots(2.,constants)
819 | roots(1.) the parameters are remembered
820 | constants.a = 3.
821 | roots(1.,constants) We changed the constants
822 |
823 | Example 3, using scan to find initial guesses:
824 | def g(x):
825 | return x*x - 1.
826 | roots = newtSolve(g)
827 | guesses = roots.scan([-2.,2.],100)
828 | for guess in guesses:
829 | print roots(guess)
830 | '''
831 | def __init__(self,f,fprime = None):
832 | self.fin = f
833 | #Find the number of arguments of f and append a
834 | #parameter argument if there isn't any.
835 | nargs = f.func_code.co_argcount
836 | if nargs == 2:
837 | self.f = f
838 | elif nargs ==1:
839 | def f1(x,param):
840 | return self.fin(x)
841 | self.f = f1
842 | else:
843 | name = f.func_name
844 | print 'Error: %s has wrong number of arguments'%name
845 | self.eps = 1.e-6
846 | def deriv(x,params):
847 | return (self.f(x+self.eps,params)- self.f(x-self.eps,params))/(2.*self.eps)
848 | if fprime == None:
849 | self.deriv = deriv
850 | else:
851 | #A derivative function was explicitly specified
852 | #Check if it has a parameter argument
853 | nargs = fprime.func_code.co_argcount
854 | if nargs == 2:
855 | self.deriv = fprime #Has a parameter argument
856 | elif nargs == 1:
857 | self.fprimein = fprime
858 | def fprime1(x,param):
859 | return self.fprimein(x)
860 | self.deriv = fprime1
861 | else:
862 | name = fprime.func_name
863 | print 'Error: %s has wrong number of arguments'%name
864 | self.tolerance = 1.e-6
865 | self.nmax = 100
866 | self.params = None
867 | def __call__(self,xGuess,params = None):
868 | if not (params == None):
869 | self.setParams(params)
870 | x = xGuess
871 | for i in range(self.nmax):
872 | dx = (self.f(x,self.params)/self.deriv(x,self.params))
873 | x = x - dx
874 | if abs(dx) < self.tolerance:
875 | return x
876 | return 'No Convergence'
877 | def setParams(self,params):
878 | #**ToDo: Check if f1 has a parameter argument
879 | #defined, and complain if it doesn't
880 | self.params = params
881 | def scan(self,interval,n=10):
882 | #Finds initial guesses to roots in a specified
883 | #interval, subdivided into n subintervals.
884 | #e.g. if the instance is called "solver"
885 | #solver.scan([0.,1.],100) generates a list
886 | #of guesses between 0. and 1., with a resolution
887 | #of .01. The larger n is, the less is the chance that
888 | #a root will be missed, but the longer the search
889 | #will take. If n isn't specified, the default value is 10
890 | #
891 | #ToDo: Replace this with a bisection search, allowing user
892 | #to specify the maximum number of distinct guesses that
893 | #need to be found.
894 | guessList = []
895 | dx = (interval[1]-interval[0])/(n-1)
896 | flast = self.f(interval[0],self.params)
897 | for x in [interval[0]+ i*dx for i in range(1,n)]:
898 | fnow = self.f(x,self.params)
899 | if ((fnow >= 0.)&(flast <=0.)) or ((fnow <= 0.)&(flast >=0.)):
900 | guessList.append(x)
901 | flast = fnow
902 | return guessList
903 |
904 |
905 |
906 |
907 |
908 |
909 |
910 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/ClimateUtilities.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CommonClimate/notebooks/576108231d5bbca8cbe6636752317f823b59429c/GEOL351/CoursewareModules/ClimateUtilities.pyc
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/DummyGraphics.py:
--------------------------------------------------------------------------------
1 | #----------Section 3: Plotting utilities-------------------------------
2 | #This is a dummy graphics routine, to import if a graphics driver
3 | #is not found. It is the fallback import if the import of ClimateGraphics
4 | #fails in ClimateUtilities. This dummy routine allows courseware
5 | #scripts to be run without the user needing to comment out plot commands.
6 | #It also can be used as a template for customizing the plotter interface
7 | #to work with you locally favored Python graphics driver (e.g. MatPlotLib).
8 | #----------------------------------------------------------------------=
9 |
10 | #A dummy class useful for passing parameters.
11 | #Just make an instance, then add new members
12 | #at will. Not sure why this also has to be defined
13 | #here, since it's defined in ClimateUtilities, but
14 | #it seems to be necessary
15 | class Dummy:
16 | pass
17 |
18 | # A little class to make resources for Ngl plot options
19 | class resource:
20 | def __init__(self):
21 | self.trYReverse = False
22 | self.trXReverse = False
23 | self.trXLog = False
24 | self.trYLog = False
25 | self.nglFrame = False
26 | self.nglDraw = False
27 | #ToDo: Missing data code resource, line styles, line colors
28 |
29 | # A little class for use as a return object from plot(), so
30 | # the user has an easy way to delete a window or save a plot
31 | # to an eps file.
32 | class plotObj:
33 | def __init__(self,workstation,plot,WorkstationResources = None):
34 | self.workstation = workstation
35 | self.plot = plot
36 | self.WorkstationResources = WorkstationResources
37 | #Deletes a plot window
38 | def delete(self):
39 | #Deletes a plot window, cleans up
40 | pass
41 | def save(self,filename = 'plot'):
42 | #Saves a plot to a file
43 | pass
44 |
45 | #Makes a line pot from a Curve object
46 | def plot(c):
47 | print "Plotting not implemented"
48 | #Set axis options according to information
49 | #in the curve object c.
50 | #
51 | #c.reverseY:
52 | # if True, reverse the Y axis
53 | #c.reverseX:
54 | # if True, reverse the X axis
55 | #c.XlogAxis:
56 | # if True, use a logarithmic X axis
57 | #c.YlogAxis:
58 | # if True, use a logarithmic Y axis
59 | #
60 | #Customize Line styles and colors here
61 | #
62 | #Set thePlot title
63 | #c.PlotTitle:
64 | # String containing the plot title
65 | #Axis labels
66 | #X and Y axis labels
67 | #c.Xlabel:
68 | # String containing the X axis label
69 | #c.Ylabel:
70 | # String containing the Y axis label
71 | #
72 | #Interchange the X and Y axes
73 | if c.switchXY:
74 | pass
75 | #If True, exchang the axes
76 |
77 | # Legends, for multicurve plot
78 | legends = []
79 | for id in c.listVariables():
80 | if not id == c.Xid:
81 | if len(c.label[id]) > 0:
82 | legends.append(c.label[id])
83 | else:
84 | legends.append(id)
85 |
86 | #
87 | #Suppress line drawing and just plot symbol for scatter plot curves
88 | #Customize plotting symbols and marker sizes if desired
89 | for id in c.listVariables():
90 | if not id == c.Xid:
91 | if c.scatter[id]:
92 | #If True, treat this data column as a scatter
93 | #plot and don't draw lines
94 | pass
95 | else:
96 | #If False, draw lines (default)
97 | pass
98 | #
99 | #Initialize the plot window here, if necessary.
100 | #w is the handle to the plot window
101 | w = None
102 | if c.switchXY:
103 | #Put in the command for doing the line plot here
104 | #Do the plot with the X and Y axes in the usual
105 | #order
106 | pass
107 | else:
108 | #Put in the command for doing the line plot here,
109 | #but switch the order of the axes.
110 | pass
111 | #
112 | #Now draw the plot
113 | #Depending on your graphics software, the preceding
114 | #command may already have drawn the plot. In some graphics
115 | #packages, after the plot is created, another command needs
116 | #to be executed in orter to display it. Either way, the
117 | #variable plotHandle below is a handle referring to the plot.
118 | #It is used to build a plotObj that can be used for further
119 | #manipulation of the plot such as saving or deleting. In some
120 | #graphics packages, which give control over such things from
121 | #menus in the plot window, the use of a plotObj for this may
122 | #be unnecessary.
123 | plotHandle = None
124 | return plotObj(w,plotHandle) #So that user can delete window or save plot
125 |
126 | # A basic contour plotter, which will plot a contour plot
127 | # of a numpy array. The x and y scales can optionally
128 | # be specified using keyword arguments x and y. For example,
129 | # if we want the x scale to be the array (or list) lat, and
130 | # the y scale to be the array (or list) lon, we would call
131 | # contour as contour(A,x=lat,y=lon).
132 | def contour(A,**kwargs):
133 | print "Plotting not implemented"
134 | #The following allows an expert user to pass
135 | # options directly to the plotter.
136 | if 'resource' in kwargs.keys():
137 | r = kwargs['resource']
138 | else:
139 | r = Dummy()
140 | #
141 | r.cnFillOn = True #Use color fill
142 |
143 | if 'x' in kwargs.keys():
144 | #Set the X array for the contour plot
145 | XArray = kwargs['x']
146 | if 'y' in kwargs.keys():
147 | #Set the Y array for the contour plot
148 | YArray = kwargs['y']
149 | #
150 | # Now create the plot
151 | rw = Dummy()
152 | #Set the color map
153 | if 'colors' in kwargs.keys():
154 | if (kwargs['colors'] == 'gray') or (kwargs['colors'] == 'grey') :
155 | #Set the default greyscale
156 | #(Substitute the appropriate command for your driver)
157 | rw.wkColorMap = 'gsdtol'
158 | else:
159 | rw.wkColorMap = kwargs['colors']
160 | else:
161 | #Default rainbow color table
162 | rw.wkColorMap = "temp1"
163 |
164 | #Open/initialize a plot window
165 | w = None
166 | #Make the plot. plotHandle is the handle returned
167 | #by the plotter, used for further manipulation of the
168 | #plot. (Redundant for some kinds of plotting packages)
169 | plotHandle = None
170 | #Now draw the plot, if your driver needs this as a separate step
171 | #(Insert command for drawing the plot here, e.g.
172 | #ShowPlot(plotHandle).
173 | #
174 | #Return a plotObj with the necessary data for further
175 | #manipulation.
176 | return plotObj(w,plotHandle,rw) #So user can delete or save plot
177 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/DummyGraphics.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CommonClimate/notebooks/576108231d5bbca8cbe6636752317f823b59429c/GEOL351/CoursewareModules/DummyGraphics.pyc
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/_notes/dwsync.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/phys.py:
--------------------------------------------------------------------------------
1 | import math
2 | from ClimateUtilities import * #To get the math methods routines
3 | #
4 | #All units are mks units
5 | #
6 | #ToDo:
7 | # * Add conversion factors (calories to joule,Megatons, etc.)
8 | # * Unit conversion calculating object
9 | # * Database of interesting constants (e.g energy and C
10 | # content of coal)
11 | # * Find a better way to organize database of contants,
12 | # allowing indexing,unit information and description
13 | # *Add the rest of the gases to the database, and find a
14 | # better way to index them and provide help. Note that
15 | # a printed table is more "transparent" in terms of what
16 | # data is available. Find a way of providing a similar
17 | # tabular summary of available data, and perhaps values
18 | # This applies to the planet data table as well, and perhaps
19 | # even to the table of physical constants.
20 | #
21 | # *Finish putting data into the gas database,
22 | # perhaps including Van der Waals, Shomate and Antoine
23 | # coefficients, at least in selected cases
24 | #
25 | #
26 |
27 | #-------------Basic physical constants-------------------
28 | #
29 | #The following five are the most accurate 1986 values
30 | #
31 | h = 6.626075540e-34 #Planck's constant
32 | c = 2.99792458e8 #Speed of light
33 | k =1.38065812e-23 #Boltzman thermodynamic constant
34 | sigma = 5.67051196e-8 #Stefan-Boltzman constant
35 | G = 6.67428e-11 #Gravitational constant (2006 measurements)
36 | #
37 |
38 | #-----------Thermodynamic constants------------
39 | #Following will come out in J/(deg kmol), so
40 | #that dividing Rstar by molecular weight gives
41 | #gas constant appropriate for mks units
42 | N_avogadro = 6.022136736e23 #Avogadro's number
43 | Rstar = 1000.*k*N_avogadro #Universal gas constant
44 | #
45 |
46 |
47 | #----------Properties of gases-----------------
48 | #The following are approximate mean values
49 | #for "normal" temperatures and pressures, suitable only
50 | #for rough calculations.
51 | #
52 |
53 | #This class allows convenient access
54 | #to the basic thermodynamic properties of
55 | #a gas, and selected properties of its
56 | #solid and liquid phases. The properties
57 | #do not need to be specified in the __init__
58 | #method, since they can be created dynamically.
59 | #We specify them anyway, and set them to None,
60 | #as a guide to the naming conventions for those
61 | #creating their own gas objects. A utility is also
62 | #available which will turn a LaTeX formatted thermodynamic table
63 | #into a Python script defining new gas objects.
64 | #
65 | #
66 | #
67 | #ToDo: *Add more documentation and help features
68 | #
69 | # *provide dictionary of properties and units.
70 | # *Add some methods or lists that make it easier
71 | # for the user to create new gas objects and insert
72 | # the data
73 | class gas:
74 | '''
75 | A gas object stores thermodynamic data for
76 | a gas, and selected properties of its condensed
77 | phases. You can create a gas object for gas
78 | G by executing:
79 | G = gas()
80 | and then setting the attributes individually (see
81 | below for explanation of names). A collection
82 | of gas objects for common gases is provided as part
83 | of the phys module. These gas objects were not actually
84 | created "by hand" but rather using a utility script that
85 | automatically translates a LaTeX formatted table into a
86 | Python script defining the objects.
87 |
88 |
89 | Attributes of a gas object:
90 | CriticalPointT: Critical point temperature (K)
91 | CriticalPointP: Critical point pressure (Pa)
92 | TriplePointT: Triple point temperature (K)
93 | TriplePointP: Triple point pressure (Pa)
94 | L_vaporization_BoilingPoint: Latent heat of vaporization (J/kg)
95 | at boiling point. The so-called "boiling point" is
96 | the temperature at which the saturation vapor pressure
97 | equals 1 atmosphere (1.013 bar). For CO2, the "boiling point"
98 | occurs below the triple point temperature, so the condensed
99 | phase would not be a liquid. Hence, for CO2 the
100 | latent heat is given at the arbitrary reference point
101 | of 253K and 29Pa.
102 | L_vaporization_TriplePoint: Latent heat of vaporization (J/kg)
103 | at the triple point
104 | L_fusion: Latent heat of fusion (J/kg) at the triple point
105 | L_sublimation: Latent heat of sublimation (J/kg) at triple point
106 | rho_liquid_BoilingPoint Liquid phase density (kg/m**3)
107 | at the boiling point
108 | rho_liquid_TriplePoint: Liquid phase density (kg/m**3)
109 | at the triple point
110 | rho_solid: Solid phase density (kg/m**3) at (or sometimes near)
111 | the triple point
112 | cp: Gas phase specific heat (J/(kg K)), at 298K and 1 bar
113 | gamma: ratio of specific heat at constant pressure
114 | to specific heat at constant volume. (Generally
115 | stated at 298K and 1bar)
116 | MolecularWeight: Molecular weight of the dominant isotope
117 | name: Name of the gas
118 | formula: Chemical formula (e.g. 'CH4')
119 |
120 | L_vaporization: Default value to use for latent heat of
121 | vaporization. Set to triple point value, if available,
122 | else to boiling point value
123 | rho_liquid: Default value to use for liquid phase density.
124 | Set to triple point value if available otherwise
125 | set to boiling point value
126 |
127 | R: Gas constant for the individual gas. Computed from
128 | other data as Rstar/MolecularWeight, when the update()
129 | method is called
130 | Rcp: The adiabatic exponent R/cp. Computed from other
131 | data when the update() method is called.
132 |
133 | '''
134 |
135 | #The __repr__ method allows us to print out
136 | #a help string when the user types the name
137 | #of a gas object
138 | def __repr__(self):
139 | firstline =\
140 | 'This gas object contains thermodynamic data on %s\n'%self.formula
141 | secondline = \
142 | 'Type \"help(gas)\" for more information\n'
143 | return firstline+secondline
144 | def __init__(self):
145 | self.CriticalPointT = None
146 | self.CriticalPointP = None
147 | self.TriplePointT = None
148 | self.TriplePointP = None
149 | self.L_vaporization_BoilingPoint = None
150 | self.L_vaporization_TriplePoint = None
151 | self.L_fusion = None
152 | self.L_sublimation = None
153 | self.rho_liquid_BoilingPoint = None
154 | self.rho_liquid_TriplePoint = None
155 | self.rho_solid = None
156 | self.cp = None
157 | self.gamma = None
158 | self.MolecularWeight = None
159 | self.name = None
160 | self.formula = None
161 | #
162 | #Default values for latent heat and liquid
163 | #density. The triple point value is set
164 | #as the default if it is available, otherwise
165 | #the boiling point values are used.
166 | self.L_vaporization= None
167 | self.rho_liquid= None
168 | #
169 | #Computed quantities
170 | #
171 | self.R = None
172 | self.Rcp = None
173 | #Function to compute derived properties
174 | def update(self):
175 | self.R = Rstar/self.MolecularWeight
176 | self.Rcp = self.R/self.cp
177 |
178 | #Set properties of individual gases
179 | #
180 | #Thermodynamic properties of dry Earth air
181 | air = gas()
182 | air.name = 'Earth Air'
183 | air.cp = 1004.
184 | air.MolecularWeight = 28.97
185 | air.gamma = 1.4003
186 |
187 | #------------------------
188 | H2O = gas()
189 | H2O.CriticalPointT = 6.471000e+02
190 | H2O.CriticalPointP = 2.210000e+07
191 | H2O.TriplePointT = 2.731500e+02
192 | H2O.TriplePointP = 6.110000e+02
193 | H2O.L_vaporization_BoilingPoint = 2.255000e+06
194 | H2O.L_vaporization_TriplePoint = 2.493000e+06
195 | H2O.L_fusion = 3.340000e+05
196 | H2O.L_sublimation = 2.840000e+06
197 | H2O.rho_liquid_BoilingPoint = 9.584000e+02
198 | H2O.rho_liquid_TriplePoint = 9.998700e+02
199 | H2O.rho_solid = 9.170000e+02
200 | H2O.cp = 1.847000e+03
201 | H2O.gamma = 1.331000e+00
202 | H2O.MolecularWeight = 1.800000e+01
203 | H2O.name = 'Water'
204 | H2O.formula = 'H2O'
205 | H2O.L_vaporization=2.493000e+06
206 | H2O.rho_liquid=9.998700e+02
207 | #------------------------
208 | CH4 = gas()
209 | CH4.CriticalPointT = 1.904400e+02
210 | CH4.CriticalPointP = 4.596000e+06
211 | CH4.TriplePointT = 9.067000e+01
212 | CH4.TriplePointP = 1.170000e+04
213 | CH4.L_vaporization_BoilingPoint = 5.100000e+05
214 | CH4.L_vaporization_TriplePoint = 5.360000e+05
215 | CH4.L_fusion = 5.868000e+04
216 | CH4.L_sublimation = 5.950000e+05
217 | CH4.rho_liquid_BoilingPoint = 4.502000e+02
218 | CH4.rho_liquid_TriplePoint = None
219 | CH4.rho_solid = 5.093000e+02
220 | CH4.cp = 2.195000e+03
221 | CH4.gamma = 1.305000e+00
222 | CH4.MolecularWeight = 1.600000e+01
223 | CH4.name = 'Methane'
224 | CH4.formula = 'CH4'
225 | CH4.L_vaporization=5.360000e+05
226 | CH4.rho_liquid=4.502000e+02
227 | #------------------------
228 | CO2 = gas()
229 | CO2.CriticalPointT = 3.042000e+02
230 | CO2.CriticalPointP = 7.382500e+06
231 | CO2.TriplePointT = 2.165400e+02
232 | CO2.TriplePointP = 5.185000e+05
233 | CO2.L_vaporization_BoilingPoint = None
234 | CO2.L_vaporization_TriplePoint = 3.970000e+05
235 | CO2.L_fusion = 1.960000e+05
236 | CO2.L_sublimation = 5.930000e+05
237 | CO2.rho_liquid_BoilingPoint = 1.032000e+03
238 | CO2.rho_liquid_TriplePoint = 1.110000e+03
239 | CO2.rho_solid = 1.562000e+03
240 | CO2.cp = 8.200000e+02
241 | CO2.gamma = 1.294000e+00
242 | CO2.MolecularWeight = 4.400000e+01
243 | CO2.name = 'Carbon Dioxide'
244 | CO2.formula = 'CO2'
245 | CO2.L_vaporization=3.970000e+05
246 | CO2.rho_liquid=1.110000e+03
247 | #------------------------
248 | N2 = gas()
249 | N2.CriticalPointT = 1.262000e+02
250 | N2.CriticalPointP = 3.400000e+06
251 | N2.TriplePointT = 6.314000e+01
252 | N2.TriplePointP = 1.253000e+04
253 | N2.L_vaporization_BoilingPoint = 1.980000e+05
254 | N2.L_vaporization_TriplePoint = 2.180000e+05
255 | N2.L_fusion = 2.573000e+04
256 | N2.L_sublimation = 2.437000e+05
257 | N2.rho_liquid_BoilingPoint = 8.086000e+02
258 | N2.rho_liquid_TriplePoint = None
259 | N2.rho_solid = 1.026000e+03
260 | N2.cp = 1.037000e+03
261 | N2.gamma = 1.403000e+00
262 | N2.MolecularWeight = 2.800000e+01
263 | N2.name = 'Nitrogen'
264 | N2.formula = 'N2'
265 | N2.L_vaporization=2.180000e+05
266 | N2.rho_liquid=8.086000e+02
267 | #------------------------
268 | O2 = gas()
269 | O2.CriticalPointT = 1.545400e+02
270 | O2.CriticalPointP = 5.043000e+06
271 | O2.TriplePointT = 5.430000e+01
272 | O2.TriplePointP = 1.500000e+02
273 | O2.L_vaporization_BoilingPoint = 2.130000e+05
274 | O2.L_vaporization_TriplePoint = 2.420000e+05
275 | O2.L_fusion = 1.390000e+04
276 | O2.L_sublimation = 2.560000e+05
277 | O2.rho_liquid_BoilingPoint = 1.141000e+03
278 | O2.rho_liquid_TriplePoint = 1.307000e+03
279 | O2.rho_solid = 1.351000e+03
280 | O2.cp = 9.160000e+02
281 | O2.gamma = 1.393000e+00
282 | O2.MolecularWeight = 3.200000e+01
283 | O2.name = 'Oxygen'
284 | O2.formula = 'O2'
285 | O2.L_vaporization=2.420000e+05
286 | O2.rho_liquid=1.307000e+03
287 | #------------------------
288 | H2 = gas()
289 | H2.CriticalPointT = 3.320000e+01
290 | H2.CriticalPointP = 1.298000e+06
291 | H2.TriplePointT = 1.395000e+01
292 | H2.TriplePointP = 7.200000e+03
293 | H2.L_vaporization_BoilingPoint = 4.540000e+05
294 | H2.L_vaporization_TriplePoint = None
295 | H2.L_fusion = 5.820000e+04
296 | H2.L_sublimation = None
297 | H2.rho_liquid_BoilingPoint = 7.097000e+01
298 | H2.rho_liquid_TriplePoint = None
299 | H2.rho_solid = 8.800000e+01
300 | H2.cp = 1.423000e+04
301 | H2.gamma = 1.384000e+00
302 | H2.MolecularWeight = 2.000000e+00
303 | H2.name = 'Hydrogen'
304 | H2.formula = 'H2'
305 | H2.L_vaporization=4.540000e+05
306 | H2.rho_liquid=7.097000e+01
307 | #------------------------
308 | He = gas()
309 | He.CriticalPointT = 5.100000e+00
310 | He.CriticalPointP = 2.280000e+05
311 | He.TriplePointT = 2.170000e+00
312 | He.TriplePointP = 5.070000e+03
313 | He.L_vaporization_BoilingPoint = 2.030000e+04
314 | He.L_vaporization_TriplePoint = None
315 | He.L_fusion = None
316 | He.L_sublimation = None
317 | He.rho_liquid_BoilingPoint = 1.249600e+02
318 | He.rho_liquid_TriplePoint = None
319 | He.rho_solid = 2.000000e+02
320 | He.cp = 5.196000e+03
321 | He.gamma = 1.664000e+00
322 | He.MolecularWeight = 4.000000e+00
323 | He.name = 'Helium'
324 | He.formula = 'He'
325 | He.L_vaporization=2.030000e+04
326 | He.rho_liquid=1.249600e+02
327 | #------------------------
328 | NH3 = gas()
329 | NH3.CriticalPointT = 4.055000e+02
330 | NH3.CriticalPointP = 1.128000e+07
331 | NH3.TriplePointT = 1.954000e+02
332 | NH3.TriplePointP = 6.100000e+03
333 | NH3.L_vaporization_BoilingPoint = 1.371000e+06
334 | NH3.L_vaporization_TriplePoint = 1.658000e+06
335 | NH3.L_fusion = 3.314000e+05
336 | NH3.L_sublimation = 1.989000e+06
337 | NH3.rho_liquid_BoilingPoint = 6.820000e+02
338 | NH3.rho_liquid_TriplePoint = 7.342000e+02
339 | NH3.rho_solid = 8.226000e+02
340 | NH3.cp = 2.060000e+03
341 | NH3.gamma = 1.309000e+00
342 | NH3.MolecularWeight = 1.700000e+01
343 | NH3.name = 'Ammonia'
344 | NH3.formula = 'NH3'
345 | NH3.L_vaporization=1.658000e+06
346 | NH3.rho_liquid=7.342000e+02
347 | #------------------------
348 |
349 | #------------------------
350 | #Synonym for H2O
351 | water = H2O
352 | #Make a list of all the gases
353 | #
354 | #This clever little fragment uses the fact that
355 | #Python can execute any string as a Python statement,
356 | #in order to find all the gases and build a list of them.
357 | #I don't know if there is a more straightforward way to
358 | #get a list of all the objects of a certain type, but this
359 | #works. Some of the trickery below is needed because
360 | #the dir() command returns a list of strings, which
361 | #give the names of the objects. It doesn't give the objects
362 | #themselves.
363 | gases = []
364 | for ob in dir():
365 | exec('isGas=isinstance('+ob+',gas)')
366 | if isGas:
367 | exec('gases.append('+ob+')')
368 |
369 | #Update all the gases
370 | for gas1 in gases:
371 | gas1.update()
372 | #
373 | #
374 | #----------------Radiation related functions-------------
375 |
376 | #Planck function (of frequency)
377 | def B(nu,T):
378 | u = min(h*nu/(k*T),500.) #To prevent overflow
379 | return (2.*h*nu**3/c**2)/(math.exp(u)-1.)
380 | #
381 | #
382 |
383 |
384 | #----------Saturation Vapor Pressure functions---------------
385 | #
386 |
387 | #Saturation vapor pressure over ice (Smithsonian formula)
388 | # Input: Kelvin. Output: Pascal
389 | def satvpi(T):
390 | #
391 | # Compute es over ice (valid between -153 c and 0 c)
392 | # see smithsonian meteorological tables page 350
393 | #
394 | # Original source: GFDL climate model, circa 1995
395 | esbasi = 6107.1
396 | tbasi = 273.16
397 | #
398 | aa = -9.09718 *(tbasi/T-1.0)
399 | b = -3.56654 *math.log10(tbasi/T)
400 | c = 0.876793*(1.0-T/tbasi)
401 | e = math.log10(esbasi)
402 | esice = 10.**(aa+b+c+e)
403 | return .1*esice #Convert to Pascals
404 |
405 | #Saturation vapor pressure over liquid water (Smithsonian formula)
406 | # Input: Kelvin. Output: Pascal
407 | def satvpw(T):
408 | # compute es over liquid water between -20c and freezing.
409 | # see smithsonian meteorological tables page 350.
410 | #
411 | # Original source: GFDL climate model, circa 1995
412 | esbasw = 1013246.0
413 | tbasw = 373.16
414 | #
415 | aa = -7.90298*(tbasw/T-1)
416 | b = 5.02808*math.log10(tbasw/T)
417 | c = -1.3816e-07*( 10.**( ((1-T/tbasw)*11.344)-1 ) )
418 | d = 8.1328e-03*( 10.**( ((tbasw/T-1)*(-3.49149))-1) )
419 | e = math.log10(esbasw)
420 | esh2O = 10.**(aa+b+c+d+e)
421 | return .1*esh2O #Convert to Pascals
422 |
423 | # An alternate formula for saturation vapor pressure over liquid water
424 | def satvpw_Heymsfield(T):
425 | ts=373.16
426 | sr=3.0057166
427 | # Vapor pressure over water. Heymsfield formula
428 | ar = ts/T
429 | br = 7.90298*(ar-1.)
430 | cr = 5.02808*math.log10(ar);
431 | dw = (1.3816E-07)*(10.**(11.344*(1.-1./ar))-1.)
432 | er = 8.1328E-03*((10.**(-(3.49149*(ar-1.))) )-1.)
433 | vp = 10.**(cr-dw+er+sr-br)
434 | vp=vp*1.0e02
435 | return(vp)
436 |
437 | def satvpg(T):
438 | #This is the saturation vapor pressure computation used in the
439 | #GFDL climate model. It blends over from water saturation to
440 | #ice saturation as the temperature falls below 0C.
441 | if ((T-273.16) < -20.):
442 | return satvpi(T)
443 | if ( ((T-273.16) >= -20.)&((T-273.16)<=0.)):
444 | return 0.05*(273.16-T)*satvpi(T) + 0.05*(T-253.16)*satvpw(T)
445 | if ((T-273.16)>0.):
446 | return satvpw(T)
447 |
448 | #Saturation vapor pressure for any substance, computed using
449 | #the simplified form of Clausius-Clapeyron assuming the perfect
450 | #gas law and constant latent heat
451 | def satvps(T,T0,e0,MolecularWeight,LatentHeat):
452 | Rv=Rstar/MolecularWeight
453 | return e0*math.exp(-(LatentHeat/Rv)*(1./T - 1./T0))
454 |
455 | #This example shows how to simplify the use of the simplified
456 | #saturation vapor pressure function, by setting up an object
457 | #that stores the thermodynamic data needed, so it doesn't have
458 | #to be re-entered each time. Because of the __call__ method,
459 | #once the object is created, it can be invoked like a regular
460 | #function.
461 | #
462 | #Usage example:
463 | # To set up a function e(T) that approximates the saturation
464 | # vapor presure for a substance which has a latent heat of
465 | # 2.5e6 J/kg, a molecular weight of 18 and has vapor pressure
466 | # 3589. Pa at a temperature of 300K, create the function using:
467 | #
468 | # e = satvps_function(300.,3589.,18.,2.5e6)
469 | #
470 | # and afterward you can invoke it simply as e(T), where T
471 | # is whatever temperature you want to evaluate it for.
472 | #
473 | #Alternately, satvps_function can be called with a gas object
474 | #as the first argument, e.g.
475 | # e = satvps_function(phys.CO2)
476 | #
477 | #If no other arguments are given, the latent heat of sublimation
478 | #will be used when e(T) is called for temperatures below the triple
479 | #point, and the latent heat of vaporization will be used for
480 | #temperatures above the triple point. To allow you to force
481 | #one or the other latent heats to be used, satvps_function takes
482 | #an optional second argument when the first argument is a gas
483 | #object. Thus,
484 | # e = satvps_function(phys.CO2,'ice')
485 | #will always use the latent heat of sublimation, regardless of T,
486 | #while e = satvps_function(phys.CO2,'liquid') will always use
487 | #the latent heat of vaporization.
488 | class satvps_function:
489 | def __init__(self,Gas_or_T0,e0_or_iceFlag=None,MolecularWeight=None,LatentHeat=None):
490 | #Check if the first argument is a gas object. If not, assume
491 | #that the arguments give T0, e0, etc. as numbers
492 | self.iceFlag = e0_or_iceFlag
493 | if isinstance(Gas_or_T0,gas):
494 | self.gas = Gas_or_T0
495 | self.M = Gas_or_T0.MolecularWeight
496 | self.T0 = Gas_or_T0.TriplePointT
497 | self.e0 = Gas_or_T0.TriplePointP
498 | if self.iceFlag == 'ice':
499 | self.L = Gas_or_T0.L_sublimation
500 | elif self.iceFlag == 'liquid':
501 | self.L = Gas_or_T0.L_vaporization
502 | else:
503 | self.iceFlag = 'switch'
504 | self.M = Gas_or_T0.MolecularWeight
505 | else:
506 | self.L = LatentHeat
507 | self.M = MolecularWeight
508 | self.T0 = Gas_or_T0
509 | self.e0 = e0_or_iceFlag
510 | def __call__(self,T):
511 | #Decide which latent heat to use
512 | if self.iceFlag == 'switch':
513 | if T ptop:
617 | ans = ad.next()
618 | pa = math.exp(ans[0])
619 | T = math.exp(ans[1])
620 | p = pa+self.satvp(T)
621 | pL.append(p)
622 | molarConL.append(self.satvp(T)/p)
623 | TL.append(T)
624 | #Numeric.array turns lists into arrays that one
625 | #can do arithmetic on.
626 | pL = Numeric.array(pL)
627 | TL = Numeric.array(TL)
628 | molarConL = Numeric.array(molarConL)
629 | #Now compute mass specific concentration
630 | Mc = self.condensible.MolecularWeight
631 | Mnc = self.noncon.MolecularWeight
632 | Mbar = molarConL*Mc +(1.-molarConL)*Mnc
633 | qL = (Mc/Mbar)*molarConL
634 | #
635 | #The else clause below interpolates to a
636 | #specified pressure array pgrid, if desired.
637 | # interp is a class defined in ClimateUtilities
638 | #which creates a callable object which acts like
639 | #an interpolation function for the listed data give
640 | #as arguments.
641 | if pgrid == None:
642 | return pL,TL,molarConL,qL
643 | else:
644 | T1 = interp(pL,TL)
645 | mc1 = interp(pL,molarConL)
646 | q1 = interp(pL,qL)
647 | T = Numeric.array([T1(pp) for pp in pgrid])
648 | mc = Numeric.array([mc1(pp) for pp in pgrid])
649 | q = Numeric.array([q1(pp) for pp in pgrid])
650 | return Numeric.array(pgrid),T, mc, q
651 |
652 |
653 |
654 |
655 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/phys.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CommonClimate/notebooks/576108231d5bbca8cbe6636752317f823b59429c/GEOL351/CoursewareModules/phys.pyc
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/planets.py:
--------------------------------------------------------------------------------
1 | #Planetary database
2 | #Source for planetary data, and some of the data on
3 | #the moons, is http://nssdc.gsfc.nasa.gov/planetary/factsheet/
4 |
5 | class Planet:
6 | '''
7 | A Planet object contains basic planetary data.
8 | If P is a Planet object, the data are:
9 | P.name = Name of the planet
10 | P.a = Mean radius of planet (m)
11 | P.g = Surface gravitational acceleration (m/s**2)
12 | P.L = Annual mean solar constant (current) (W/m**2)
13 | P.albedo Bond albedo (fraction)
14 |
15 | P.rsm = Semi-major axis of orbit about Sun (m)
16 | P.year = Sidereal length of year (s)
17 | P.eccentricity = Eccentricity (unitless)
18 | P.day = Mean tropical length of day (s)
19 | P.obliquity = Obliquity to orbit (degrees)
20 | P.Lequinox = Longitude of equinox (degrees)
21 |
22 | P.Tsbar = Mean surface temperature (K)
23 | P.Tsmax = Maximum surface temperature (K)
24 |
25 | For gas giants, "surface" quantities are given at the 1 bar level
26 | '''
27 |
28 | #__repr__ object prints out a help string when help is
29 | #invoked on the planet object or the planet name is typed
30 | def __repr__(self):
31 | line1 =\
32 | 'This planet object contains information on %s\n'%self.name
33 | line2 = 'Type \"help(Planet)\" for more information\n'
34 | return line1+line2
35 | def __init__(self):
36 | self.name = None #Name of the planet
37 | self.a = None #Mean radius of planet
38 | self.g = None #Surface gravitational acceleration
39 | self.L = None #Annual mean solar constant (current)
40 | self.albedo = None #Bond albedo
41 |
42 | self.rsm = None #Semi-major axis
43 | self.year = None #Sidereal length of year
44 | self.eccentricity = None # Eccentricity
45 | self.day = None #Mean tropical length of day
46 | self.obliquity = None #Obliquity to orbit
47 | self.Lequinox = None #Longitude of equinox
48 |
49 | self.Tsbar = None #Mean surface temperature
50 | self.Tsmax = None #Maximum surface temperature
51 |
52 | #----------------------------------------------------
53 | Mercury = Planet()
54 | Mercury.name = 'Mercury' #Name of the planet
55 | Mercury.a = 2.4397e6 #Mean radius of planet
56 | Mercury.g = 3.70 #Surface gravitational acceleration
57 | Mercury.albedo = .119 #Bond albedo
58 | Mercury.L = 9126.6 #Annual mean solar constant (current)
59 | #
60 | Mercury.rsm = 57.91e9 #Semi-major axis
61 | Mercury.year = 87.969*24.*3600. #Sidereal length of year
62 | Mercury.eccentricity = .2056 # Eccentricity
63 | Mercury.day = 4222.6*3600. #Mean tropical length of day
64 | Mercury.obliquity = .01 #Obliquity to orbit (deg)
65 | Mercury.Lequinox = None #Longitude of equinox (deg)
66 | #
67 | Mercury.Tsbar = 440. #Mean surface temperature
68 | Mercury.Tsmax = 725. #Maximum surface temperature
69 |
70 | #----------------------------------------------------
71 | Venus = Planet()
72 | Venus.name = 'Venus' #Name of the planet
73 | Venus.a = 6.0518e6 #Mean radius of planet
74 | Venus.g = 8.87 #Surface gravitational acceleration
75 | Venus.albedo = .750 #Bond albedo
76 | Venus.L = 2613.9 #Annual mean solar constant (current)
77 | #
78 | Venus.rsm = 108.21e9 #Semi-major axis
79 | Venus.year = 224.701*24.*3600. #Sidereal length of year
80 | Venus.eccentricity = .0067 # Eccentricity
81 | Venus.day = 2802.*3600. #Mean tropical length of day
82 | Venus.obliquity = 177.36 #Obliquity to orbit (deg)
83 | Venus.Lequinox = None #Longitude of equinox (deg)
84 | #
85 | Venus.Tsbar = 737. #Mean surface temperature
86 | Venus.Tsmax = 737. #Maximum surface temperature
87 |
88 | #----------------------------------------------------
89 | Earth = Planet()
90 | Earth.name = 'Earth' #Name of the planet
91 | Earth.a = 6.371e6 #Mean radius of planet
92 | Earth.g = 9.798 #Surface gravitational acceleration
93 | Earth.albedo = .306 #Bond albedo
94 | Earth.L = 1367.6 #Annual mean solar constant (current)
95 | #
96 | Earth.rsm = 149.60e9 #Semi-major axis
97 | Earth.year = 365.256*24.*3600. #Sidereal length of year
98 | Earth.eccentricity = .0167 # Eccentricity
99 | Earth.day = 24.000*3600. #Mean tropical length of day
100 | Earth.obliquity = 23.45 #Obliquity to orbit (deg)
101 | Earth.Lequinox = None #Longitude of equinox (deg)
102 | #
103 | Earth.Tsbar = 288. #Mean surface temperature
104 | Earth.Tsmax = None #Maximum surface temperature
105 |
106 | #----------------------------------------------------
107 | Mars = Planet()
108 | Mars.name = 'Mars' #Name of the planet
109 | Mars.a = 3.390e6 #Mean radius of planet
110 | Mars.g = 3.71 #Surface gravitational acceleration
111 | Mars.albedo = .250 #Bond albedo
112 | Mars.L = 589.2 #Annual mean solar constant (current)
113 | #
114 | Mars.rsm = 227.92e9 #Semi-major axis
115 | Mars.year = 686.98*24.*3600. #Sidereal length of year
116 | Mars.eccentricity = .0935 # Eccentricity
117 | Mars.day = 24.6597*3600. #Mean tropical length of day
118 | Mars.obliquity = 25.19 #Obliquity to orbit (deg)
119 | Mars.Lequinox = None #Longitude of equinox (deg)
120 | #
121 | Mars.Tsbar = 210. #Mean surface temperature
122 | Mars.Tsmax = 295. #Maximum surface temperature
123 |
124 | #----------------------------------------------------
125 | Jupiter = Planet()
126 | Jupiter.name = 'Jupiter' #Name of the planet
127 | Jupiter.a = 69.911e6 #Mean radius of planet
128 | Jupiter.g = 24.79 #Surface gravitational acceleration
129 | Jupiter.albedo = .343 #Bond albedo
130 | Jupiter.L = 50.5 #Annual mean solar constant (current)
131 | #
132 | Jupiter.rsm = 778.57e9 #Semi-major axis
133 | Jupiter.year = 4332.*24.*3600. #Sidereal length of year
134 | Jupiter.eccentricity = .0489 # Eccentricity
135 | Jupiter.day = 9.9259*3600. #Mean tropical length of day
136 | Jupiter.obliquity = 3.13 #Obliquity to orbit (deg)
137 | Jupiter.Lequinox = None #Longitude of equinox (deg)
138 | #
139 | Jupiter.Tsbar = 165. #Mean surface temperature
140 | Jupiter.Tsmax = None #Maximum surface temperature
141 |
142 | #----------------------------------------------------
143 | Saturn = Planet()
144 | Saturn.name = 'Saturn' #Name of the planet
145 | Saturn.a = 58.232e6 #Mean radius of planet
146 | Saturn.g = 10.44 #Surface gravitational acceleration
147 | Saturn.albedo = .342 #Bond albedo
148 | Saturn.L = 14.90 #Annual mean solar constant (current)
149 | #
150 | Saturn.rsm = 1433.e9 #Semi-major axis
151 | Saturn.year = 10759.*24.*3600. #Sidereal length of year
152 | Saturn.eccentricity = .0565 # Eccentricity
153 | Saturn.day = 10.656*3600. #Mean tropical length of day
154 | Saturn.obliquity = 26.73 #Obliquity to orbit (deg)
155 | Saturn.Lequinox = None #Longitude of equinox (deg)
156 | #
157 | Saturn.Tsbar = 134. #Mean surface temperature
158 | Saturn.Tsmax = None #Maximum surface temperature
159 |
160 | #----------------------------------------------------
161 | Uranus = Planet()
162 | Uranus.name = 'Uranus' #Name of the planet
163 | Uranus.a = 25.362e6 #Mean radius of planet
164 | Uranus.g = 8.87 #Surface gravitational acceleration
165 | Uranus.albedo = .300 #Bond albedo
166 | Uranus.L = 3.71 #Annual mean solar constant (current)
167 | #
168 | Uranus.rsm = 2872.46e9 #Semi-major axis
169 | Uranus.year = 30685.4*24.*3600. #Sidereal length of year
170 | Uranus.eccentricity = .0457 # Eccentricity
171 | Uranus.day = 17.24*3600. #Mean tropical length of day
172 | Uranus.obliquity = 97.77 #Obliquity to orbit (deg)
173 | Uranus.Lequinox = None #Longitude of equinox (deg)
174 | #
175 | Uranus.Tsbar = 76. #Mean surface temperature
176 | Uranus.Tsmax = None #Maximum surface temperature
177 |
178 |
179 | #----------------------------------------------------
180 | Neptune = Planet()
181 | Neptune.name = 'Neptune' #Name of the planet
182 | Neptune.a = 26.624e6 #Mean radius of planet
183 | Neptune.g = 11.15 #Surface gravitational acceleration
184 | Neptune.albedo = .290 #Bond albedo
185 | Neptune.L = 1.51 #Annual mean solar constant (current)
186 | #
187 | Neptune.rsm = 4495.06e9 #Semi-major axis
188 | Neptune.year = 60189.0*24.*3600. #Sidereal length of year
189 | Neptune.eccentricity = .0113 # Eccentricity
190 | Neptune.day = 16.11*3600. #Mean tropical length of day
191 | Neptune.obliquity = 28.32 #Obliquity to orbit (deg)
192 | Neptune.Lequinox = None #Longitude of equinox (deg)
193 | #
194 | Neptune.Tsbar = 72. #Mean surface temperature
195 | Neptune.Tsmax = None #Maximum surface temperature
196 |
197 | #----------------------------------------------------
198 | Pluto = Planet()
199 | Pluto.name = 'Pluto' #Name of the planet
200 | Pluto.a = 1.195e6 #Mean radius of planet
201 | Pluto.g = .58 #Surface gravitational acceleration
202 | Pluto.albedo = .5 #Bond albedo
203 | Pluto.L = .89 #Annual mean solar constant (current)
204 | #
205 | Pluto.rsm = 5906.e9 #Semi-major axis
206 | Pluto.year = 90465.*24.*3600. #Sidereal length of year
207 | Pluto.eccentricity = .2488 # Eccentricity
208 | Pluto.day = 153.2820*3600. #Mean tropical length of day
209 | Pluto.obliquity = 122.53 #Obliquity to orbit (deg)
210 | Pluto.Lequinox = None #Longitude of equinox (deg)
211 | #
212 | Pluto.Tsbar = 50. #Mean surface temperature
213 | Pluto.Tsmax = None #Maximum surface temperature
214 |
215 |
216 |
217 | #Selected moons
218 |
219 | #----------------------------------------------------
220 | Moon = Planet()
221 | Moon.name = 'Moon' #Name of the planet
222 | Moon.a = 1.737e6 #Mean radius of planet
223 | Moon.g = 1.62 #Surface gravitational acceleration
224 | Moon.albedo = .11 #Bond albedo
225 | Moon.L = 1367.6 #Annual mean solar constant (current)
226 | #
227 | Moon.rsm = Earth.rsm #Semi-major axis
228 | Moon.year = Earth.year #Sidereal length of year
229 | Moon.eccentricity = None # Eccentricity
230 | Moon.day = 28.*24.*3600. #Mean tropical length of day (approx)
231 | Moon.obliquity = None #Obliquity to orbit (deg)
232 | Moon.Lequinox = None #Longitude of equinox (deg)
233 | #
234 | Moon.Tsbar = None #Mean surface temperature
235 | Moon.Tsmax = 400. #Maximum surface temperature
236 | Moon.Tsmin = 100. #Minimum surface temperature
237 |
238 | Titan = Planet()
239 | Titan.name = 'Titan' #Name of the planet
240 | Titan.a = 2.575e6 #Mean radius of planet
241 | Titan.g = 1.35 #Surface gravitational acceleration
242 | Titan.L = Saturn.L #Annual mean solar constant (current)
243 | Titan.albedo = .21 #Bond albedo (Not yet updated from Cassini)
244 | #
245 | Titan.rsm = None #Semi-major axis
246 | Titan.year = Saturn.year #Sidereal length of year
247 | Titan.eccentricity = Saturn.eccentricity # Eccentricity ABOUT SUN
248 | Titan.day = 15.9452*24.*3600. #Mean tropical length of day
249 | Titan.obliquity = Saturn.obliquity #Obliquity to plane of Ecliptic
250 | #(Titan's rotation axis approx parallel
251 | # to Saturn's
252 | Titan.Lequinox = Saturn.Lequinox #Longitude of equinox
253 | #
254 | Titan.Tsbar = 95. #Mean surface temperature
255 | Titan.Tsmax = None #Maximum surface temperature
256 |
257 | Europa = Planet()
258 | Europa.name = 'Europa' #Name of the planet
259 | Europa.a = 1.560e6 #Mean radius of planet
260 | Europa.g = 1.31 #Surface gravitational acceleration
261 | Europa.L = Jupiter.L #Annual mean solar constant (current)
262 | Europa.albedo = .67 #Bond albedo
263 | #
264 | Europa.rsm = Jupiter.rsm #Semi-major axis
265 | Europa.year = Jupiter.year #Sidereal length of year
266 | Europa.eccentricity = Jupiter.eccentricity # Eccentricity
267 | Europa.day = 3.551*24.*3600. #Mean tropical length of day
268 | Europa.obliquity = Jupiter.obliquity #Obliquity to plane of ecliptic
269 | Europa.Lequinox = None #Longitude of equinox
270 | #
271 | Europa.Tsbar = 103. #Mean surface temperature
272 | Europa.Tsmax = 125. #Maximum surface temperature
273 |
274 | Triton = Planet()
275 | Triton.name = 'Triton' #Name of the planet
276 | Triton.a = 2.7068e6/2. #Mean radius of planet
277 | Triton.g = .78 #Surface gravitational acceleration
278 | Triton.L = Neptune.L #Annual mean solar constant (current)
279 | Triton.albedo = .76 #Bond albedo
280 | #
281 | Triton.rsm = Neptune.rsm #Semi-major axis
282 | Triton.year = Neptune.year #Sidereal length of year
283 | Triton.eccentricity = Neptune.eccentricity # Eccentricity about Sun
284 | Triton.day = 5.877*24.*3600. #Mean tropical length of day
285 | #Triton's rotation is retrograde
286 | Triton.obliquity = 156. #Obliquity to ecliptic **ToDo: Check this.
287 | #Note: Seasons are influenced by the inclination
288 | #of Triton's orbit? (About 20 degrees to
289 | #Neptune's equator
290 | Triton.Lequinox = None #Longitude of equinox
291 | #
292 | Triton.Tsbar = 34.5 #Mean surface temperature
293 | #This is probably a computed blackbody
294 | #temperature, rather than an observation
295 | Triton.Tsmax = None #Maximum surface temperature
296 |
297 |
298 |
--------------------------------------------------------------------------------
/GEOL351/CoursewareModules/setpath.py:
--------------------------------------------------------------------------------
1 | #----------------------------------------------------------------------
2 | #This utility sets up the python configuration files so as to
3 | #allow Python to find files in a specified directory, regardless
4 | #of what directory the user is working from. This is typically
5 | #used to create a directory where the user will put resources shared
6 | #by many Python scripts, such as courseware modules
7 | #
8 | #----------------------------------------------------------------------
9 | #Usage:
10 | # (1) Put a copy of this file (setpath.py) in the directory
11 | # you want to share
12 | #
13 | # (2) Execute setpath.py, either by opening it and running it
14 | # in Canopy, or from the command line by changing director
15 | # to the directory you want to share and then typing
16 | # python setup.py
17 | # If you run it by opening it in the Canopy editor you need to
18 | # select the directory popup menu item that tells Canopy to
19 | # change the working directory to the Editor directory.
20 | # in Canopy, the working directory always appears at the upper
21 | # right corner of the Python interpreter window.
22 | #
23 | #----------------------------------------------------------------------
24 | #Notes:
25 | #
26 | # This will create a startup file which will properly
27 | # initialize ipython (whether used directly or via Enthought
28 | # Canopy) to find your files, and will do that regardless
29 | # of your operating system.
30 | #
31 | # If you are using a Linux or Mac OSX operating system, it
32 | # will also edit your .cshrc and .bash_profile shell startup
33 | # scripts to set the environment variable PYTHONPATH so that
34 | # any version of the python interperter started from the
35 | # command line (i.e. whether ipython or python) will find
36 | # the shared files. This feature will not work on
37 | # Windows operating systems, so Windows users should start
38 | # either start up python by clicking on the Canopy app, or
39 | # by starting ipython from the command line. It is possible
40 | # to set the PYTHONPATH environment variable in Windows,
41 | # but this script does not yet implement that feature.
42 | #
43 | # Note that it is also possible to manually set up a temporary
44 | # shared path (for example /home/MyModules) in a given script
45 | # by executing the lines:
46 | #
47 | # import sys
48 | # sys.path.append('home/MyModules')
49 | #
50 | # where you would replace '/home/MyModules') with the
51 | # actual full path to the directory you want on your own
52 | # system
53 | #----------------------------------------------------------------------
54 | import os,glob,platform
55 |
56 | #Utility function to return an acceptable filename for the
57 | #startup file
58 | def makeFileName(startupDir):
59 | files = glob.glob(os.path.join(startupDir,'*.py'))
60 | #Make a startup filename that doesn't already exist
61 | for i in range(10000):
62 | if i<100:
63 | fname = '%02d-startup.py'%i
64 | else:
65 | fname ='%04d-startup.py'%i
66 | fname = os.path.join(startupDir,fname)
67 | if not fname in files: break
68 | return fname
69 | #
70 | #--------Main program starts here
71 | #
72 | #Get current path
73 | curPath = os.getcwd()
74 | #Get home directory
75 | home = os.path.expanduser('~')
76 | #
77 | #If this is a Linux or Mac OS X system, edit the
78 | #shell initialization files to set the PYTHONPATH environment
79 | #variable
80 | if ( (platform.system()=='Darwin') or ('inux' in platform.system())):
81 | #We are on a Linux or Mac system. Edit Shell startup files
82 | print 'This is a Linux or Mac system. Adding path to shell startup scripts'
83 | #
84 | #csh script: (Note, should also do this for .tcshrc if it exists)
85 | cshFile = os.path.join(home,'.cshrc')
86 | print 'csh family -- Editing '+cshFile
87 | #Make backup copy of file
88 | os.system('cp %s %s'%(cshFile,cshFile+'.setPathBackup'))
89 | #Append line to set PYTHONPATH
90 | outfile = open(cshFile,'a')
91 | outfile.write('#Line added by setPath.py. Original in %s\n'%(cshFile+'.setPathBackup'))
92 | #Note: the double quotes allow paths to contain spaces
93 | outfile.write('setenv PYTHONPATH \"%s:$PYTHONPATH\"\n'%curPath)
94 | outfile.close()
95 | #
96 | #bash script (ToDo: also edit .profile, for sh users)
97 | bashFile = os.path.join(home,'.bash_profile')
98 | print 'sh family -- Editing '+bashFile
99 | #Make backup copy of file
100 | os.system('cp %s %s'%(bashFile,bashFile+'.setPathBackup'))
101 | #Append line to set PYTHONPATH
102 | outfile = open(bashFile,'a')
103 | outfile.write('#Line added by setPath.py. Original in %s\n'%(bashFile+'.setPathBackup'))
104 | #Note: the double quotes allow paths to contain spaces
105 | outfile.write('export PYTHONPATH=\"%s:$PYTHONPATH\"\n'%curPath)
106 | outfile.close()
107 |
108 | #
109 | #
110 | #Set paths for ipython startup. This takes care of starting up ipython from
111 | #double-clicking the Canopy app on any operating system
112 | #
113 | profilepath = os.path.join(home,'.ipython/profile_default/startup')
114 | if os.path.isdir(profilepath):
115 | fname = makeFileName(profilepath)
116 | else:
117 | print "Could not find .ipython startup directory. Exiting."
118 | exit(1)
119 | #
120 | #Write the startup file
121 | contents = 'import sys \nsys.path.append(\'%s\')\n'%curPath
122 | outfile = open(fname,'w')
123 | outfile.write(contents)
124 | outfile.close()
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 El Nino
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Teaching Notebooks
2 |
3 | I am relatively new to Jupyter notebooks, but I have found them to be an absolutely incredible tool to lower barriers to entry in the quantitative modeling of earth systems. That is because they enable such a seamless integration of text, code and figures, and allow users to tinker with the code without having to know how to write it from scratch.
4 |
5 | This repository is work in progress, documenting my own journey in Python and Jupyter.
6 |
7 | In this initial version, you will find 3 labs:
8 |
9 | 1. [ZeroD.ipynb](ZeroD.ipynb) implements a zero-dimensional model of Earth's climate, closely following [Pierrehumbert et al, 2011](http://www.annualreviews.org/doi/abs/10.1146/annurev-earth-040809-152447)
10 |
11 | 2. [lorenz_climatechange.ipynb](lorenz_climatechange.ipynb) provides a nonlinear dynamical perspective on climate change, emulating much of the analysis of [Palmer (1999}](https://www.researchgate.net/publication/235703704_A_Nonlinear_Dynamical_Perspective_on_Climate_Prediction). The ODE solver for the Lorenz system was gratefully borrowed from the amazing [jakevdp](https://jakevdp.github.io/blog/2013/02/16/animating-the-lorentz-system-in-3d/).
12 |
13 | 3. [ENSO_recharge.ipynb](ENSO_recharge.ipynb) implements [Jin (1997)](http://yly-mac.gps.caltech.edu/AGU/AGU_2008/Zz_Others/Li_agu08/Jin1997a.pdf)'s "recharge oscillator" model for El Niño-Southern Oscillation.
14 |
15 | All labs were geared for Earth Science undergraduates with little to no mathematical background, so they skirt around the analysis and go straight to the "tinkering" part. I will not advocate it as a desirable way to teach Earth Science as a whole, but I have found it useful in generating student interest towards more math-heavy classes so they can understand these concepts at a deeper level. Give it a spin and see if you like it.
16 |
--------------------------------------------------------------------------------