├── .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 | --------------------------------------------------------------------------------