meclib 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meclib-0.1.1/PKG-INFO +11 -0
- meclib-0.1.1/examples/CambioComparisons.py +112 -0
- meclib-0.1.1/examples/MakeEmissionScenario.py +107 -0
- meclib-0.1.1/meclib/__init__.py +1 -0
- meclib-0.1.1/meclib/cl.py +733 -0
- meclib-0.1.1/meclib.egg-info/PKG-INFO +11 -0
- meclib-0.1.1/meclib.egg-info/SOURCES.txt +10 -0
- meclib-0.1.1/meclib.egg-info/dependency_links.txt +1 -0
- meclib-0.1.1/meclib.egg-info/requires.txt +4 -0
- meclib-0.1.1/meclib.egg-info/top_level.txt +3 -0
- meclib-0.1.1/pyproject.toml +15 -0
- meclib-0.1.1/setup.cfg +4 -0
meclib-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meclib
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Code for Modeling Earth's Climate course
|
|
5
|
+
Author-email: Steven Neshyba <sneshyba@gmail.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Requires-Dist: matplotlib
|
|
10
|
+
Requires-Dist: pandas
|
|
11
|
+
Requires-Dist: pint
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
|
|
4
|
+
# ### Computational Guided Inquiry for Modeling Earth's Climate (Neshyba, 2025)
|
|
5
|
+
#
|
|
6
|
+
# # Cambio Comparisons
|
|
7
|
+
# This code takes as input a user-generated emission scenario and runs four versions of Cambio:
|
|
8
|
+
#
|
|
9
|
+
# - Cambio 1.0 has the five basic equations of motion, with $CO_2$ fertilization but no other feedbacks
|
|
10
|
+
# - Cambio 2.0 adds in Henry's Law feedbacks
|
|
11
|
+
# - Cambio 3.0 adds in ice-albedo feedback
|
|
12
|
+
# - Cambio 4.0 adds in terrestrial sequestion feedback that reduces $CO_2$ fertilization
|
|
13
|
+
#
|
|
14
|
+
# The code then runs the four simulations, and compares the output of a selected two of them.
|
|
15
|
+
|
|
16
|
+
# In[2]:
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
import matplotlib.pyplot as plt
|
|
21
|
+
import meclib as CL
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# In[3]:
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# get_ipython().run_line_magic('matplotlib', 'inline')
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ### Loading your emission scenario
|
|
31
|
+
# In the cell below, load in your scheduled flows file. It'll be most convenient if you use the following naming convention:
|
|
32
|
+
#
|
|
33
|
+
# time, eps, epsdictionary_from_file = CL.LoadMyScenario('...')
|
|
34
|
+
#
|
|
35
|
+
# (but of course supplying the name of a particular emission scenario).
|
|
36
|
+
|
|
37
|
+
# In[5]:
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
filename = "Peaks_in_2040_LTE_default.pkl"
|
|
41
|
+
time, eps, epsdictionary_from_file = CL.LoadMyScenario(filename, verbose=True)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ### Creating a dictionary for climate parameters consistent with this emission scenario
|
|
45
|
+
# In the cell below, we use the CreateClimateParams function to create a dictionary of climate parameters.
|
|
46
|
+
|
|
47
|
+
# In[7]:
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
ClimateParams = CL.CreateClimateParams(epsdictionary_from_file)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ### Running Cambio
|
|
54
|
+
# The cell below runs Cambio versions 1-4.
|
|
55
|
+
|
|
56
|
+
# In[9]:
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
CS_Cambio1_list = CL.run_Cambio(
|
|
60
|
+
CL.PropagateClimateState_Cambio1, ClimateParams, time, eps
|
|
61
|
+
)
|
|
62
|
+
CS_Cambio2_list = CL.run_Cambio(
|
|
63
|
+
CL.PropagateClimateState_Cambio2, ClimateParams, time, eps
|
|
64
|
+
)
|
|
65
|
+
CS_Cambio3_list = CL.run_Cambio(
|
|
66
|
+
CL.PropagateClimateState_Cambio3, ClimateParams, time, eps
|
|
67
|
+
)
|
|
68
|
+
CS_Cambio4_list = CL.run_Cambio(
|
|
69
|
+
CL.PropagateClimateState_Cambio4, ClimateParams, time, eps
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ### Individual reports using display
|
|
74
|
+
|
|
75
|
+
# In[11]:
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
print("Starting state:")
|
|
79
|
+
print(CS_Cambio1_list[0])
|
|
80
|
+
print("Ending state:")
|
|
81
|
+
print(CS_Cambio1_list[-1])
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ### Individual reports using CS_list_plots
|
|
85
|
+
|
|
86
|
+
# In[13]:
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
items_to_plot = [
|
|
90
|
+
["C_atm", "C_ocean"],
|
|
91
|
+
["F_ha", "F_ocean_net", "F_land_net"],
|
|
92
|
+
"T_anomaly",
|
|
93
|
+
]
|
|
94
|
+
CL.CS_list_plots(CS_Cambio1_list, "Cambio1.0", items_to_plot)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ### Comparison plots using CS_list_compare
|
|
98
|
+
|
|
99
|
+
# In[15]:
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
items_to_plot = [
|
|
103
|
+
["C_atm", "C_ocean"],
|
|
104
|
+
["F_ha", "F_ocean_net", "F_land_net"],
|
|
105
|
+
"T_anomaly",
|
|
106
|
+
"OceanSurfacepH",
|
|
107
|
+
]
|
|
108
|
+
CL.CS_list_compare(
|
|
109
|
+
[CS_Cambio1_list, CS_Cambio4_list],
|
|
110
|
+
["Cambio1.0", "Cambio4.0"],
|
|
111
|
+
items_to_plot,
|
|
112
|
+
)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
|
|
4
|
+
# ### Computational Guided Inquiry for Modeling Earth's Climate (Neshyba, 2025)
|
|
5
|
+
#
|
|
6
|
+
# # Make an Emission Scenario
|
|
7
|
+
# The idea of this module is to create an emissions scenario -- a _schedule_ -- that describes how much carbon humans have released to the atmosphere in the past, and that makes projections about future emissions. We've done this before (ScheduledFlows), but this will allow us to specify long-term emissions.
|
|
8
|
+
|
|
9
|
+
# In[16]:
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import pandas as pd
|
|
14
|
+
import matplotlib.pyplot as plt
|
|
15
|
+
import meclib as CL
|
|
16
|
+
|
|
17
|
+
# In[17]:
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# get_ipython().run_line_magic('matplotlib', 'inline')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ### Using ScheduledFlowsWithLTE
|
|
24
|
+
# In the cell below, we calculate and plot an emissions scenario, $\epsilon (t)$, using MakeEmissionsScenarioLTE (which includes long-term emissions).
|
|
25
|
+
|
|
26
|
+
# In[19]:
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Parameters
|
|
30
|
+
t_start = 1750
|
|
31
|
+
t_stop = 2150
|
|
32
|
+
nsteps = 600
|
|
33
|
+
t = np.linspace(t_start, t_stop, nsteps)
|
|
34
|
+
t_peak = 2040
|
|
35
|
+
t_decarb = 15
|
|
36
|
+
k = 0.0166
|
|
37
|
+
t_0 = 2003
|
|
38
|
+
eps_0 = 9
|
|
39
|
+
epslongterm = 2
|
|
40
|
+
t_decarb_ppf_factor = 0.5
|
|
41
|
+
|
|
42
|
+
# Create the scenario
|
|
43
|
+
t, myeps = CL.MakeEmissionsScenarioLTE(
|
|
44
|
+
t_start,
|
|
45
|
+
t_stop,
|
|
46
|
+
nsteps,
|
|
47
|
+
k,
|
|
48
|
+
eps_0,
|
|
49
|
+
t_0,
|
|
50
|
+
t_peak,
|
|
51
|
+
t_decarb,
|
|
52
|
+
epslongterm=epslongterm,
|
|
53
|
+
t_decarb_ppf_factor=t_decarb_ppf_factor,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Plot the scenario
|
|
57
|
+
plt.figure()
|
|
58
|
+
plt.plot(t, myeps)
|
|
59
|
+
plt.grid(True)
|
|
60
|
+
plt.title("Emission scenario (GtC/year)")
|
|
61
|
+
plt.xlabel("year")
|
|
62
|
+
plt.ylabel("GtC/year")
|
|
63
|
+
plt.show()
|
|
64
|
+
|
|
65
|
+
# ### Saving your emissions scenario
|
|
66
|
+
# In the cell below we create the dictionary, etc., and save it to a file.
|
|
67
|
+
|
|
68
|
+
# In[21]:
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Create an empty dictionary
|
|
72
|
+
epsdictionary = dict()
|
|
73
|
+
|
|
74
|
+
# Create an empty dataframe
|
|
75
|
+
epsdf = pd.DataFrame()
|
|
76
|
+
|
|
77
|
+
# Insert the time and emissions columns into the dataframe
|
|
78
|
+
epsdf.insert(loc=0, column="time", value=t)
|
|
79
|
+
epsdf.insert(loc=1, column="emissions", value=myeps)
|
|
80
|
+
|
|
81
|
+
# Add the dataframe to the dictionary
|
|
82
|
+
epsdictionary["dataframe"] = epsdf
|
|
83
|
+
|
|
84
|
+
# Add metadata
|
|
85
|
+
epsdictionary["t_0"] = t_0
|
|
86
|
+
epsdictionary["eps_0"] = eps_0
|
|
87
|
+
epsdictionary["t_peak"] = t_peak
|
|
88
|
+
epsdictionary["t_decarb"] = t_decarb
|
|
89
|
+
epsdictionary["t_decarb"] = t_decarb_ppf_factor
|
|
90
|
+
epsdictionary["k"] = k
|
|
91
|
+
epsdictionary["description"] = (
|
|
92
|
+
"This is an emission scenario that peaks in the year 2040, with long-term emissions continuing after that"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Report the contents of the dictionary
|
|
96
|
+
print(epsdictionary)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ### Saving your emissions scenario
|
|
100
|
+
# Use the cell below to save your emissions scenario (the entire dictionary -- data and metadata) as a pickle file.
|
|
101
|
+
|
|
102
|
+
# In[23]:
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# Assign a name for the file, and save it
|
|
106
|
+
filename = "Peaks_in_2040_LTE_default.pkl"
|
|
107
|
+
CL.SaveMyScenario(epsdictionary, filename)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .cl import *
|
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
from copy import deepcopy as makeacopy
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import pickle
|
|
6
|
+
|
|
7
|
+
def SaveMyScenario(my_dict, filename):
|
|
8
|
+
"""Stores the stored scheduled flow dictionary"""
|
|
9
|
+
|
|
10
|
+
# Save dictionary to a file
|
|
11
|
+
with open(filename, 'wb') as file: pickle.dump(my_dict, file)
|
|
12
|
+
|
|
13
|
+
# Return
|
|
14
|
+
return
|
|
15
|
+
|
|
16
|
+
def LoadMyScenario(filename,verbose=False):
|
|
17
|
+
"""Loads the stored scheduled flow dictionary, with an option to plot"""
|
|
18
|
+
|
|
19
|
+
# Load dictionary from a file
|
|
20
|
+
with open(filename, 'rb') as file: my_dict_loaded = pickle.load(file)
|
|
21
|
+
|
|
22
|
+
# This extracts the dataframe from the dictionary
|
|
23
|
+
epsdf = my_dict_loaded['dataframe']
|
|
24
|
+
|
|
25
|
+
# This extracts the time and emissions from the dataframe
|
|
26
|
+
time = np.array(epsdf['time'])
|
|
27
|
+
eps = np.array(epsdf['emissions'])
|
|
28
|
+
|
|
29
|
+
# Graph the time series (if flagged)
|
|
30
|
+
if verbose:
|
|
31
|
+
linewidth = 3
|
|
32
|
+
print(my_dict_loaded)
|
|
33
|
+
plt.figure()
|
|
34
|
+
plt.plot(time,eps,'k',linewidth=linewidth)
|
|
35
|
+
plt.grid(True)
|
|
36
|
+
plt.title(filename)
|
|
37
|
+
plt.xlabel('year')
|
|
38
|
+
plt.ylabel('GtC/year')
|
|
39
|
+
|
|
40
|
+
# Return
|
|
41
|
+
return time, eps, my_dict_loaded
|
|
42
|
+
|
|
43
|
+
def CreateClimateParams(epsdictionary,k_la=120):
|
|
44
|
+
"""Returns a structured dictionary containing climate parameters"""
|
|
45
|
+
|
|
46
|
+
# Start with an empty dictionary
|
|
47
|
+
ClimateParams = {}
|
|
48
|
+
|
|
49
|
+
# Amount in the atmosphere (pre-industrial, and at the time of the NASA figure, 2003)
|
|
50
|
+
C_atm_preind = 590
|
|
51
|
+
C_atm_2003 = 780
|
|
52
|
+
|
|
53
|
+
# The starting time
|
|
54
|
+
epsdf = epsdictionary['dataframe']
|
|
55
|
+
time_preind = np.array(epsdf['time'])[0]
|
|
56
|
+
|
|
57
|
+
# Land-to-atmosphere flux constant and fluxes, according to NASA's figure
|
|
58
|
+
F_la_preind = k_la
|
|
59
|
+
F_la_2003 = F_la_preind
|
|
60
|
+
F_al_2003 = F_la_preind + 3
|
|
61
|
+
|
|
62
|
+
# Atm-to-land flux constants (Eq. 4 of Cambio1.0, using 2003 fluxes
|
|
63
|
+
# and reservoir amounts in pre-industrial time and 2003)
|
|
64
|
+
k_al1 = (F_al_2003-F_la_preind)/(C_atm_2003-C_atm_preind)
|
|
65
|
+
k_al0 = F_la_preind-k_al1*C_atm_preind
|
|
66
|
+
|
|
67
|
+
# Atmosphere-to-ocean (Eq. 6 of Cambio1.0, using 2003 flux and
|
|
68
|
+
# atmospheric reservoir amount)
|
|
69
|
+
F_ao_2003 = 92
|
|
70
|
+
k_ao = F_ao_2003/C_atm_2003
|
|
71
|
+
F_oa_2003 = 90
|
|
72
|
+
|
|
73
|
+
# NASA's observed ratio of net ocean sequestration to net land sequestration
|
|
74
|
+
r = 2/3
|
|
75
|
+
|
|
76
|
+
# Ocean-to-atmosphere flux constant based on Cambio1.0 equations
|
|
77
|
+
k = epsdictionary['k']
|
|
78
|
+
k_oa = k*(k_ao/(r*k_al1)-1) # constraining to satisfy the ratio r
|
|
79
|
+
|
|
80
|
+
# Atmosphere-to-land feedback parameters
|
|
81
|
+
k_al1_Tstar = 1.43
|
|
82
|
+
k_al1_deltaT = 0.471
|
|
83
|
+
fractional_k_al1_floor = 0.684
|
|
84
|
+
|
|
85
|
+
# Albedo parameters
|
|
86
|
+
preindustrial_albedo = 0.3
|
|
87
|
+
kappa = 0.61
|
|
88
|
+
S0 = 1367
|
|
89
|
+
sigma = 5.67e-8
|
|
90
|
+
AS_term1 = -1/(4*(1-preindustrial_albedo))
|
|
91
|
+
AS_term2 = (S0*(1-preindustrial_albedo)/(4*kappa*sigma))**.25
|
|
92
|
+
AS = AS_term1 * AS_term2; #print('Calculated AS = ', AS_test)
|
|
93
|
+
T_test = (S0/4*(1-preindustrial_albedo)/kappa/sigma)**.25; #print('Calculated T_test =', T_test)
|
|
94
|
+
#AS = -102.8
|
|
95
|
+
|
|
96
|
+
# Attach to the dictionary
|
|
97
|
+
ClimateParams['k_la'] = k_la
|
|
98
|
+
ClimateParams['k_al0'] = k_al0
|
|
99
|
+
ClimateParams['k_al1'] = k_al1
|
|
100
|
+
ClimateParams['k_oa'] = k_oa
|
|
101
|
+
ClimateParams['k_ao'] = k_ao
|
|
102
|
+
ClimateParams['k'] = k
|
|
103
|
+
ClimateParams['DC'] = 0.0321
|
|
104
|
+
ClimateParams['climate sensitivity'] = 3/C_atm_preind
|
|
105
|
+
ClimateParams['C_atm 2003'] = C_atm_2003
|
|
106
|
+
ClimateParams['F_ao 2003'] = F_ao_2003
|
|
107
|
+
ClimateParams['F_oa 2003'] = F_oa_2003
|
|
108
|
+
ClimateParams['preindustrial C_atm'] = C_atm_preind
|
|
109
|
+
ClimateParams['albedo sensitivity'] = AS
|
|
110
|
+
ClimateParams['preindustrial albedo'] = preindustrial_albedo
|
|
111
|
+
ClimateParams['preindustrial pH'] = 8.2
|
|
112
|
+
ClimateParams['starting time'] = time_preind
|
|
113
|
+
ClimateParams['k_al1_Tstar'] = k_al1_Tstar
|
|
114
|
+
ClimateParams['k_al1_deltaT'] = k_al1_deltaT
|
|
115
|
+
ClimateParams['fractional_k_al1_floor'] = fractional_k_al1_floor
|
|
116
|
+
|
|
117
|
+
# Albedo feedback parameters (see Wunderling et al for the origin of 0.43 here)
|
|
118
|
+
Delta_alpha_ASI = 0.43/AS; #print(Delta_alpha_ASI)
|
|
119
|
+
fractional_albedo_floor = (preindustrial_albedo+Delta_alpha_ASI)/preindustrial_albedo
|
|
120
|
+
ClimateParams['albedo_Tstar'] = 2
|
|
121
|
+
ClimateParams['albedo_delta_T'] = 1
|
|
122
|
+
ClimateParams['fractional_albedo_floor'] = fractional_albedo_floor
|
|
123
|
+
|
|
124
|
+
# Return
|
|
125
|
+
return ClimateParams
|
|
126
|
+
|
|
127
|
+
def CreateClimateState(ClimateParams):
|
|
128
|
+
"""Creates a new climate state with default values (preindustrial)"""
|
|
129
|
+
|
|
130
|
+
# Create an empty climate state
|
|
131
|
+
ClimateState = {}
|
|
132
|
+
|
|
133
|
+
# Fill in some default (preindustrial) values
|
|
134
|
+
ClimateState['C_atm'] = ClimateParams['preindustrial C_atm']
|
|
135
|
+
|
|
136
|
+
# Pre-industrial ocean carbon reservoir needed to satisfy flux ratios in 2003
|
|
137
|
+
F_ao_preind = ClimateParams['k_ao'] * ClimateParams['preindustrial C_atm']
|
|
138
|
+
F_oa_preind = F_ao_preind
|
|
139
|
+
ClimateState['C_ocean'] = F_oa_preind/ClimateParams['k_oa']
|
|
140
|
+
|
|
141
|
+
# Other useful parameters
|
|
142
|
+
ClimateState['albedo'] = ClimateParams['preindustrial albedo']
|
|
143
|
+
|
|
144
|
+
# The temperature anomaly in preindustrial times is defined to be zero
|
|
145
|
+
ClimateState['T_anomaly'] = 0
|
|
146
|
+
|
|
147
|
+
# And the pH
|
|
148
|
+
ClimateState['pH'] = ClimateParams['preindustrial pH']
|
|
149
|
+
|
|
150
|
+
# Starting year
|
|
151
|
+
ClimateState['time'] = ClimateParams['starting time']
|
|
152
|
+
|
|
153
|
+
# Return the climate
|
|
154
|
+
return ClimateState
|
|
155
|
+
|
|
156
|
+
def sigmafloor(x,x_trans=0,x_interval=1,sigma_floor_infinity=0):
|
|
157
|
+
"""Generates a sigmoid (smooth step-down) function with a floor"""
|
|
158
|
+
temp = 1 - 1/(1 + np.exp(-(x-x_trans)*3/x_interval))
|
|
159
|
+
return temp*(1-sigma_floor_infinity)+sigma_floor_infinity
|
|
160
|
+
|
|
161
|
+
def sigmoid(x,x_thresh=0,x_trans=1,sigma_x_infinity=0):
|
|
162
|
+
"""Generates a sigmoid (smooth step-down) function with a floor"""
|
|
163
|
+
return sigmafloor(x,x_thresh,x_trans,sigma_x_infinity)
|
|
164
|
+
|
|
165
|
+
def sigmaup(t,transitiontime,transitiontimeinterval):
|
|
166
|
+
"""Generates a sigmoid (smooth step-up) function"""
|
|
167
|
+
return 1 / (1 + np.exp(-(t-transitiontime)*3/transitiontimeinterval))
|
|
168
|
+
|
|
169
|
+
def sigmadown(t,transitiontime,transitiontimeinterval):
|
|
170
|
+
"""Generates a sigmoid (smooth step-down) function"""
|
|
171
|
+
return 1 - sigmaup(t,transitiontime,transitiontimeinterval)
|
|
172
|
+
|
|
173
|
+
def CollectClimateTimeSeries(ClimateState_list,whatIwant):
|
|
174
|
+
"""Collects elements from a list of dictionaries"""
|
|
175
|
+
array = np.empty(0)
|
|
176
|
+
for ClimateState in ClimateState_list:
|
|
177
|
+
array = np.append(array,ClimateState[whatIwant])
|
|
178
|
+
return array
|
|
179
|
+
|
|
180
|
+
def Diagnose_OceanSurfacepH(C_atm,ClimateParams):
|
|
181
|
+
"""Returns ocean pH from the carbon amount in the atmosphere"""
|
|
182
|
+
"""and the ClimateParams dictionary (preindust_pH and preindust_C_atm)"""
|
|
183
|
+
|
|
184
|
+
# Extract the preindustrial pH from ClimateParms
|
|
185
|
+
preindust_pH = ClimateParams['preindustrial pH']
|
|
186
|
+
|
|
187
|
+
# Extract the preindustrial carbon in the atmosphere from ClimateParams
|
|
188
|
+
preindust_C_atm = ClimateParams['preindustrial C_atm']
|
|
189
|
+
|
|
190
|
+
# Calculate the pH according to our algorithm
|
|
191
|
+
pH = -np.log10(C_atm/preindust_C_atm)+preindust_pH
|
|
192
|
+
|
|
193
|
+
# Return the diagnosed pH
|
|
194
|
+
return(pH)
|
|
195
|
+
|
|
196
|
+
def Diagnose_T_anomaly(C_atm, alpha, ClimateParams):
|
|
197
|
+
"""Returns a temperature anomaly based on the C_atm, Earth's albedo,"""
|
|
198
|
+
"""and ClimateParams parameters related to climate_sensitivity and albedo sensitivity"""
|
|
199
|
+
|
|
200
|
+
CS = ClimateParams['climate sensitivity']
|
|
201
|
+
preindust_C_atm = ClimateParams['preindustrial C_atm']
|
|
202
|
+
AS = ClimateParams['albedo sensitivity']
|
|
203
|
+
preindust_alpha = ClimateParams['preindustrial albedo']
|
|
204
|
+
|
|
205
|
+
# Calculate the temperature anomaly according to our algorithm
|
|
206
|
+
T_anomaly = CS*(C_atm-preindust_C_atm) + AS*(alpha-preindust_alpha)
|
|
207
|
+
|
|
208
|
+
# Return the temperature anomaly
|
|
209
|
+
return(T_anomaly)
|
|
210
|
+
|
|
211
|
+
def Diagnose_ppm(C_atm):
|
|
212
|
+
"""Computes atmospheric carbon amount in ppm, from GtC"""
|
|
213
|
+
C_atm_ppm = C_atm/2.12
|
|
214
|
+
return(C_atm_ppm)
|
|
215
|
+
|
|
216
|
+
def Diagnose_actual_temperature(T_anomaly):
|
|
217
|
+
"""Computes degrees C from a temperature anomaly"""
|
|
218
|
+
T_C = T_anomaly+14
|
|
219
|
+
return(T_C)
|
|
220
|
+
|
|
221
|
+
def Diagnose_degreesF(T_C):
|
|
222
|
+
"""Converts temperature from C to F"""
|
|
223
|
+
|
|
224
|
+
# Do the conversion to F
|
|
225
|
+
T_F = T_C*9/5+32### END SOLUTION
|
|
226
|
+
|
|
227
|
+
# Return the diagnosed temperature in F
|
|
228
|
+
return(T_F)
|
|
229
|
+
|
|
230
|
+
def Diagnose_albedo_with_constraint(T_anomaly, ClimateParams, previousalbedo=0, dtime=0):
|
|
231
|
+
"""
|
|
232
|
+
Returns the albedo as a function of temperature, constrained so the change can't
|
|
233
|
+
exceed a certain amount per year, if so flagged
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
# Find the albedo without constraint
|
|
237
|
+
albedo = Diagnose_albedo(T_anomaly, ClimateParams)
|
|
238
|
+
|
|
239
|
+
# Applying a constraint, if called for
|
|
240
|
+
if (previousalbedo !=0) & (dtime != 0):
|
|
241
|
+
albedo_change = albedo-previousalbedo
|
|
242
|
+
max_albedo_change = ClimateParams['max_albedo_change_rate']*dtime
|
|
243
|
+
if np.abs(albedo_change) > max_albedo_change:
|
|
244
|
+
this_albedo_change = np.sign(albedo_change)*max_albedo_change
|
|
245
|
+
albedo = previousalbedo + this_albedo_change
|
|
246
|
+
|
|
247
|
+
# Return the albedo
|
|
248
|
+
return albedo
|
|
249
|
+
|
|
250
|
+
def Diagnose_albedo(T_anomaly, ClimateParams):
|
|
251
|
+
"""
|
|
252
|
+
Returns the albedo as a function of temperature anomaly
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
# Extract the fractional albedo floor from ClimateParams
|
|
256
|
+
fractional_albedo_floor = ClimateParams['fractional_albedo_floor']
|
|
257
|
+
|
|
258
|
+
# Extract the transition temperature
|
|
259
|
+
transitionT = ClimateParams['albedo_Tstar']
|
|
260
|
+
|
|
261
|
+
# Extract the transition temperature interval
|
|
262
|
+
transitionTinterval = ClimateParams['albedo_delta_T']
|
|
263
|
+
|
|
264
|
+
# Extract the preindustrial albedo
|
|
265
|
+
preindust_albedo = ClimateParams['preindustrial albedo']
|
|
266
|
+
|
|
267
|
+
# Compute the albedo
|
|
268
|
+
albedo = sigmafloor(T_anomaly,transitionT,transitionTinterval,fractional_albedo_floor)*preindust_albedo
|
|
269
|
+
|
|
270
|
+
# Return the diagnosed albedo
|
|
271
|
+
return albedo
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def Diagnose_Delta_T_from_albedo(albedo,ClimateParams):
|
|
275
|
+
"""
|
|
276
|
+
Computes additional planetary temperature increase resulting from a lower albedo
|
|
277
|
+
Based on the idea of radiative balance, ASR = OLR
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
# Extract parameters we need and make the diagnosis
|
|
281
|
+
AS = ClimateParams['albedo_sensitivity']
|
|
282
|
+
preindust_albedo = ClimateParams['preindust_albedo']
|
|
283
|
+
Delta_T_from_albedo = (albedo-preindust_albedo)*AS
|
|
284
|
+
return Delta_T_from_albedo
|
|
285
|
+
|
|
286
|
+
def Diagnose_Stochastic_C_atm(C_atm,ClimateParams):
|
|
287
|
+
"""Returns a noisy version of T"""
|
|
288
|
+
|
|
289
|
+
# Extract parameters we need and make the diagnosis
|
|
290
|
+
Stochastic_C_atm_std_dev = ClimateParams['Stochastic_C_atm_std_dev']
|
|
291
|
+
C_atm_new = np.random.normal(C_atm, Stochastic_C_atm_std_dev)
|
|
292
|
+
return C_atm_new
|
|
293
|
+
|
|
294
|
+
def MakeEmissionsScenario(t_start,t_stop,nsteps,k,eps_0,t_0,t_peak,t_decarb):
|
|
295
|
+
"""Returns an emissions scenario"""
|
|
296
|
+
t = np.linspace(t_start,t_stop,nsteps)
|
|
297
|
+
tp1 = t_peak + (t_decarb/3)*np.log(3/(k*t_decarb)-1)
|
|
298
|
+
myeps = np.exp(k*t) * eps_0*np.exp(-k*t_0) * np.exp(3/t_decarb*(tp1-t)) / (1+np.exp(3/t_decarb*(tp1-t)))
|
|
299
|
+
return t, myeps
|
|
300
|
+
|
|
301
|
+
def MakeEmissionsScenarioLTE(t_start,t_stop,nsteps,k,eps_0,t_0,t_peak,t_decarb,epslongterm,t_decarb_ppf_factor=1):
|
|
302
|
+
"""Returns an emissions scenario that has a long-term, post-peak emission"""
|
|
303
|
+
t_decarb_ppf = t_decarb*t_decarb_ppf_factor
|
|
304
|
+
t = np.linspace(t_start,t_stop,nsteps)
|
|
305
|
+
time, eps = MakeEmissionsScenario(t_start,t_stop,nsteps,k,eps_0,t_0,t_peak,t_decarb)
|
|
306
|
+
neweps = PostPeakFlattener(time,eps,t_decarb_ppf,epslongterm)
|
|
307
|
+
return time, neweps
|
|
308
|
+
|
|
309
|
+
def MakeEmissionsScenario1(t_start,t_stop,nsteps,k,eps_0,t_0,t_trans,delta_t_trans):
|
|
310
|
+
"""Returns an emissions scenario"""
|
|
311
|
+
time = np.linspace(t_start,t_stop,nsteps)
|
|
312
|
+
myexp = np.exp(k*time)
|
|
313
|
+
myN = eps_0/(np.exp(k*t_0)*sigmadown(t_0,t_trans,delta_t_trans))
|
|
314
|
+
mysigmadown = sigmadown(time,t_trans,delta_t_trans)
|
|
315
|
+
eps = myN*myexp*mysigmadown
|
|
316
|
+
return time, eps
|
|
317
|
+
|
|
318
|
+
def MakeEmissionsScenario2(t_start,t_stop,nsteps,k,eps_0,t_0,t_peak,delta_t_trans):
|
|
319
|
+
"""Returns an emissions scenario parameterized by the year of peak emissions"""
|
|
320
|
+
t_trans = t_peak + delta_t_trans/3*np.log(3/(k*delta_t_trans)-1)
|
|
321
|
+
return MakeEmissionsScenario(t_start,t_stop,nsteps,k,eps_0,t_0,t_trans,delta_t_trans)
|
|
322
|
+
|
|
323
|
+
def PostPeakFlattener(time,eps,transitiontimeinterval,epslongterm):
|
|
324
|
+
ipeak = np.where(eps==np.max(eps))[0][0]; print('peak',eps[ipeak],ipeak)
|
|
325
|
+
b = eps[ipeak]
|
|
326
|
+
a = epslongterm
|
|
327
|
+
neweps = makeacopy(eps)
|
|
328
|
+
for i in range(ipeak,len(eps)):
|
|
329
|
+
ipostpeak = i-ipeak
|
|
330
|
+
neweps[i] = a + np.exp(-(time[i]-time[ipeak])**2/transitiontimeinterval**2)*(b-a)
|
|
331
|
+
return neweps
|
|
332
|
+
|
|
333
|
+
def MakeEmissionsScenario1LTE(t_start,t_stop,nsteps,k,eps_0,t_0,t_trans,delta_t_trans,epslongterm):
|
|
334
|
+
time, eps = MakeEmissionsScenario(t_start,t_stop,nsteps,k,eps_0,t_0,t_trans,delta_t_trans)
|
|
335
|
+
neweps = PostPeakFlattener(time,eps,delta_t_trans,epslongterm)
|
|
336
|
+
return time, neweps
|
|
337
|
+
|
|
338
|
+
def MakeEmissionsScenario2LTE(t_start,t_stop,nsteps,k,eps_0,t_0,t_peak,delta_t_trans,epslongterm):
|
|
339
|
+
time, eps = MakeEmissionsScenario2(t_start,t_stop,nsteps,k,eps_0,t_0,t_peak,delta_t_trans)
|
|
340
|
+
neweps = PostPeakFlattener(time,eps,delta_t_trans,epslongterm)
|
|
341
|
+
return time, neweps
|
|
342
|
+
|
|
343
|
+
def MakeHybridEmissionScenario(\
|
|
344
|
+
t_start,t_stop,EnROADS_time,EnROADS_eps,filename,k=0.0166, nsteps=1000,reportflag=False,plotflag=False):
|
|
345
|
+
|
|
346
|
+
# Extracting the first emission entry of the baseline scenario, for pegging the cambio scenario
|
|
347
|
+
eps_0=EnROADS_eps[0]; print(eps_0)
|
|
348
|
+
time_0=EnROADS_time[0]; print(time_0)
|
|
349
|
+
|
|
350
|
+
# Creating a new cambio scenario compatible pegged to the baseline scenario
|
|
351
|
+
delta_t_trans = 20
|
|
352
|
+
t_peak = time_0+1
|
|
353
|
+
epslongterm = 0
|
|
354
|
+
intermediate_filename = 'ignore_this_file.txt'
|
|
355
|
+
time_cambio, eps_cambio = MakeEmissionsScenario2LTE(t_start,t_stop,nsteps,k,eps_0,time_0,t_peak,delta_t_trans,epslongterm)
|
|
356
|
+
|
|
357
|
+
# Create a set of emissions equal to EnROADS, but interpolated to the cambio times
|
|
358
|
+
dt = time_cambio[1]-time_cambio[0]
|
|
359
|
+
times_to_interpolate = np.arange(time_0+dt,t_stop,dt)
|
|
360
|
+
eps_interpolated = np.interp(times_to_interpolate,EnROADS_time,EnROADS_eps)
|
|
361
|
+
|
|
362
|
+
# Create the hybrid emission scenario
|
|
363
|
+
i_pegged,val = min(enumerate(time_cambio), key=lambda x: abs(x[1]-(time_0+dt))); print(i_pegged,val)
|
|
364
|
+
eps_hybrid = np.append(eps_cambio[0:i_pegged],eps_interpolated); print(len(eps_hybrid))
|
|
365
|
+
|
|
366
|
+
# Create an empty dictionary
|
|
367
|
+
epsdictionary = dict()
|
|
368
|
+
|
|
369
|
+
# Create an empty dataframe
|
|
370
|
+
epsdf = pd.DataFrame()
|
|
371
|
+
|
|
372
|
+
# Insert the time and emissions columns into the dataframe
|
|
373
|
+
epsdf.insert(loc=0, column='time', value=time_cambio)
|
|
374
|
+
epsdf.insert(loc=1, column='emissions', value=eps_hybrid)
|
|
375
|
+
|
|
376
|
+
# Add the dataframe to the dictionary
|
|
377
|
+
epsdictionary['dataframe'] = epsdf
|
|
378
|
+
|
|
379
|
+
# This saves it, if we change the flag to True
|
|
380
|
+
h5io.write_hdf5(filename, epsdictionary, overwrite=True)
|
|
381
|
+
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
def run_Cambio(PropagateClimateState, ClimateParams, epstime, eps):
|
|
385
|
+
|
|
386
|
+
# Make the starting state the preindustrial
|
|
387
|
+
ClimateState = CreateClimateState(ClimateParams)
|
|
388
|
+
|
|
389
|
+
# Initialize our list of climate states
|
|
390
|
+
ClimateState_list = []
|
|
391
|
+
|
|
392
|
+
# The time interval
|
|
393
|
+
dt = epstime[1]-epstime[0]
|
|
394
|
+
|
|
395
|
+
# Loop over all the times after the initial one in the scheduled flow
|
|
396
|
+
for i in range(1,len(epstime)):
|
|
397
|
+
|
|
398
|
+
# Propagate
|
|
399
|
+
ClimateState = PropagateClimateState(ClimateState,ClimateParams,dt,eps[i])
|
|
400
|
+
|
|
401
|
+
# Add to our list of climate states
|
|
402
|
+
ClimateState_list.append(ClimateState)
|
|
403
|
+
|
|
404
|
+
return ClimateState_list
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def PropagateClimateState_Cambio4(previousClimateState, ClimateParams, dt, F_ha):
|
|
408
|
+
"""Propagates the state of the climate, with a specified anthropogenic carbon flux"""
|
|
409
|
+
"""Returns a new climate state"""
|
|
410
|
+
|
|
411
|
+
# Extract constants from ClimateParams
|
|
412
|
+
k_la = ClimateParams['k_la']
|
|
413
|
+
k_al0 = ClimateParams['k_al0']
|
|
414
|
+
k_al1 = ClimateParams['k_al1']
|
|
415
|
+
k_oa = ClimateParams['k_oa']
|
|
416
|
+
k_ao = ClimateParams['k_ao']
|
|
417
|
+
DC = ClimateParams['DC']
|
|
418
|
+
preindustrial_albedo = ClimateParams['preindustrial albedo']
|
|
419
|
+
fractional_albedo_floor = ClimateParams['fractional_albedo_floor']
|
|
420
|
+
albedo_Tstar = ClimateParams['albedo_Tstar']
|
|
421
|
+
albedo_delta_T = ClimateParams['albedo_delta_T']
|
|
422
|
+
k_al1_Tstar = ClimateParams['k_al1_Tstar']
|
|
423
|
+
k_al1_deltaT = ClimateParams['k_al1_deltaT']
|
|
424
|
+
fractional_k_al1_floor = ClimateParams['fractional_k_al1_floor']
|
|
425
|
+
|
|
426
|
+
# Extract concentrations, albedo, etc, from the previous climate state
|
|
427
|
+
C_atm = previousClimateState['C_atm']
|
|
428
|
+
C_ocean = previousClimateState['C_ocean']
|
|
429
|
+
albedo = previousClimateState['albedo']
|
|
430
|
+
time = previousClimateState['time']
|
|
431
|
+
|
|
432
|
+
# Get the temperature implied by the carbon in the atmosphere and the updated albedo, and ocean pH
|
|
433
|
+
T_anomaly = Diagnose_T_anomaly(C_atm, albedo, ClimateParams)
|
|
434
|
+
actual_temperature = Diagnose_actual_temperature(T_anomaly)
|
|
435
|
+
OceanSurfacepH = Diagnose_OceanSurfacepH(C_atm,ClimateParams)
|
|
436
|
+
|
|
437
|
+
# Get the albedo implied by the temperature anomaly
|
|
438
|
+
albedo = Diagnose_albedo(T_anomaly, ClimateParams)
|
|
439
|
+
|
|
440
|
+
# Get new fluxes (including the effect of temperature anomaly on the ocean-to-atmosphere flux)
|
|
441
|
+
F_la = k_la
|
|
442
|
+
F_al = k_al0 + k_al1*C_atm*sigmafloor(T_anomaly,k_al1_Tstar,k_al1_deltaT,fractional_k_al1_floor)
|
|
443
|
+
F_oa = k_oa*C_ocean*(1+DC*T_anomaly)
|
|
444
|
+
F_ao = k_ao*C_atm
|
|
445
|
+
|
|
446
|
+
# Get new concentrations of carbon that depend on the fluxes
|
|
447
|
+
C_atm += (F_la + F_oa - F_ao - F_al + F_ha)*dt
|
|
448
|
+
C_ocean += (F_ao - F_oa)*dt
|
|
449
|
+
time += dt
|
|
450
|
+
|
|
451
|
+
# Ocean surface diagnostic
|
|
452
|
+
OceanSurfacepH = Diagnose_OceanSurfacepH(C_atm,ClimateParams)
|
|
453
|
+
|
|
454
|
+
# Create a new climate state with these updates
|
|
455
|
+
ClimateState = makeacopy(previousClimateState)
|
|
456
|
+
ClimateState['C_atm'] = C_atm
|
|
457
|
+
ClimateState['C_ocean'] = C_ocean
|
|
458
|
+
ClimateState['F_al'] = F_al
|
|
459
|
+
ClimateState['F_la'] = F_la
|
|
460
|
+
ClimateState['F_ao'] = F_ao
|
|
461
|
+
ClimateState['F_oa'] = F_oa
|
|
462
|
+
ClimateState['F_ha'] = F_ha
|
|
463
|
+
ClimateState['F_ocean_net'] = F_oa-F_ao
|
|
464
|
+
ClimateState['F_land_net'] = F_la-F_al
|
|
465
|
+
ClimateState['time'] = time
|
|
466
|
+
ClimateState['T_anomaly'] = T_anomaly
|
|
467
|
+
ClimateState['actual temperature'] = actual_temperature
|
|
468
|
+
ClimateState['OceanSurfacepH'] = OceanSurfacepH
|
|
469
|
+
ClimateState['albedo'] = albedo
|
|
470
|
+
|
|
471
|
+
# Return the new climate state
|
|
472
|
+
return ClimateState
|
|
473
|
+
|
|
474
|
+
def PropagateClimateState_Cambio3(previousClimateState, ClimateParams, dt, F_ha):
|
|
475
|
+
"""Propagates the state of the climate including Henry feedback and ice-albedo feedback"""
|
|
476
|
+
|
|
477
|
+
# Extract constants from ClimateParams
|
|
478
|
+
k_la = ClimateParams['k_la']
|
|
479
|
+
k_al0 = ClimateParams['k_al0']
|
|
480
|
+
k_al1 = ClimateParams['k_al1']
|
|
481
|
+
k_oa = ClimateParams['k_oa']
|
|
482
|
+
k_ao = ClimateParams['k_ao']
|
|
483
|
+
DC = ClimateParams['DC']
|
|
484
|
+
preindustrial_albedo = ClimateParams['preindustrial albedo']
|
|
485
|
+
fractional_albedo_floor = ClimateParams['fractional_albedo_floor']
|
|
486
|
+
albedo_Tstar = ClimateParams['albedo_Tstar']
|
|
487
|
+
albedo_delta_T = ClimateParams['albedo_delta_T']
|
|
488
|
+
|
|
489
|
+
# Extract concentrations, albedo, etc, from the previous climate state
|
|
490
|
+
C_atm = previousClimateState['C_atm']
|
|
491
|
+
C_ocean = previousClimateState['C_ocean']
|
|
492
|
+
albedo = previousClimateState['albedo']
|
|
493
|
+
time = previousClimateState['time']
|
|
494
|
+
|
|
495
|
+
# Get the temperature implied by the carbon in the atmosphere and the updated albedo, and ocean pH
|
|
496
|
+
T_anomaly = Diagnose_T_anomaly(C_atm, albedo, ClimateParams)
|
|
497
|
+
actual_temperature = Diagnose_actual_temperature(T_anomaly)
|
|
498
|
+
OceanSurfacepH = Diagnose_OceanSurfacepH(C_atm,ClimateParams)
|
|
499
|
+
|
|
500
|
+
# Get the albedo implied by the temperature anomaly
|
|
501
|
+
albedo = Diagnose_albedo(T_anomaly, ClimateParams)
|
|
502
|
+
|
|
503
|
+
# Get new fluxes (including the effect of temperature anomaly on the ocean-to-atmosphere flux)
|
|
504
|
+
F_la = k_la
|
|
505
|
+
F_al = k_al0 + k_al1*C_atm
|
|
506
|
+
F_oa = k_oa*C_ocean*(1+DC*T_anomaly)
|
|
507
|
+
F_ao = k_ao*C_atm
|
|
508
|
+
|
|
509
|
+
# Get new concentrations of carbon that depend on the fluxes
|
|
510
|
+
C_atm += (F_la + F_oa - F_ao - F_al + F_ha)*dt
|
|
511
|
+
C_ocean += (F_ao - F_oa)*dt
|
|
512
|
+
time += dt
|
|
513
|
+
|
|
514
|
+
# Ocean surface diagnostic
|
|
515
|
+
OceanSurfacepH = Diagnose_OceanSurfacepH(C_atm,ClimateParams)
|
|
516
|
+
|
|
517
|
+
# Create a new climate state with these updates
|
|
518
|
+
ClimateState = makeacopy(previousClimateState)
|
|
519
|
+
ClimateState['C_atm'] = C_atm
|
|
520
|
+
ClimateState['C_ocean'] = C_ocean
|
|
521
|
+
ClimateState['F_al'] = F_al
|
|
522
|
+
ClimateState['F_la'] = F_la
|
|
523
|
+
ClimateState['F_ao'] = F_ao
|
|
524
|
+
ClimateState['F_oa'] = F_oa
|
|
525
|
+
ClimateState['F_ha'] = F_ha
|
|
526
|
+
ClimateState['F_ocean_net'] = F_oa-F_ao
|
|
527
|
+
ClimateState['F_land_net'] = F_la-F_al
|
|
528
|
+
ClimateState['time'] = time
|
|
529
|
+
ClimateState['T_anomaly'] = T_anomaly
|
|
530
|
+
ClimateState['actual temperature'] = actual_temperature
|
|
531
|
+
ClimateState['OceanSurfacepH'] = OceanSurfacepH
|
|
532
|
+
ClimateState['albedo'] = albedo
|
|
533
|
+
|
|
534
|
+
# Return the new climate state
|
|
535
|
+
return ClimateState
|
|
536
|
+
|
|
537
|
+
def PropagateClimateState_Cambio2(previousClimateState, ClimateParams, dt, F_ha):
|
|
538
|
+
"""Propagates the state of the climate including Henry feedback"""
|
|
539
|
+
|
|
540
|
+
# Extract constants from ClimateParams
|
|
541
|
+
k_la = ClimateParams['k_la']
|
|
542
|
+
k_al0 = ClimateParams['k_al0']
|
|
543
|
+
k_al1 = ClimateParams['k_al1']
|
|
544
|
+
k_oa = ClimateParams['k_oa']
|
|
545
|
+
k_ao = ClimateParams['k_ao']
|
|
546
|
+
DC = ClimateParams['DC']
|
|
547
|
+
preindustrial_albedo = ClimateParams['preindustrial albedo']
|
|
548
|
+
fractional_albedo_floor = ClimateParams['fractional_albedo_floor']
|
|
549
|
+
albedo_Tstar = ClimateParams['albedo_Tstar']
|
|
550
|
+
albedo_delta_T = ClimateParams['albedo_delta_T']
|
|
551
|
+
|
|
552
|
+
# Extract concentrations, albedo, etc, from the previous climate state
|
|
553
|
+
C_atm = previousClimateState['C_atm']
|
|
554
|
+
C_ocean = previousClimateState['C_ocean']
|
|
555
|
+
albedo = previousClimateState['albedo']
|
|
556
|
+
time = previousClimateState['time']
|
|
557
|
+
|
|
558
|
+
# Get the temperature anomaly implied by the carbon in the atmosphere and the preindustrial albedo
|
|
559
|
+
T_anomaly = Diagnose_T_anomaly(C_atm, preindustrial_albedo, ClimateParams)
|
|
560
|
+
actual_temperature = Diagnose_actual_temperature(T_anomaly)
|
|
561
|
+
OceanSurfacepH = Diagnose_OceanSurfacepH(C_atm,ClimateParams)
|
|
562
|
+
|
|
563
|
+
# Get new fluxes (including the effect of temperature anomaly on the ocean-to-atmosphere flux)
|
|
564
|
+
F_la = k_la
|
|
565
|
+
F_al = k_al0 + k_al1*C_atm
|
|
566
|
+
F_oa = k_oa*C_ocean*(1+DC*T_anomaly)
|
|
567
|
+
F_ao = k_ao*C_atm
|
|
568
|
+
|
|
569
|
+
# Get new concentrations of carbon that depend on the fluxes
|
|
570
|
+
C_atm += (F_la + F_oa - F_ao - F_al + F_ha)*dt
|
|
571
|
+
C_ocean += (F_ao - F_oa)*dt
|
|
572
|
+
time += dt
|
|
573
|
+
|
|
574
|
+
# Create a new climate state with these updates
|
|
575
|
+
ClimateState = makeacopy(previousClimateState)
|
|
576
|
+
ClimateState['C_atm'] = C_atm
|
|
577
|
+
ClimateState['C_ocean'] = C_ocean
|
|
578
|
+
ClimateState['F_al'] = F_al
|
|
579
|
+
ClimateState['F_la'] = F_la
|
|
580
|
+
ClimateState['F_ao'] = F_ao
|
|
581
|
+
ClimateState['F_oa'] = F_oa
|
|
582
|
+
ClimateState['F_ha'] = F_ha
|
|
583
|
+
ClimateState['F_ocean_net'] = F_oa-F_ao
|
|
584
|
+
ClimateState['F_land_net'] = F_la-F_al
|
|
585
|
+
ClimateState['time'] = time
|
|
586
|
+
ClimateState['T_anomaly'] = T_anomaly
|
|
587
|
+
ClimateState['actual temperature'] = actual_temperature
|
|
588
|
+
ClimateState['OceanSurfacepH'] = OceanSurfacepH
|
|
589
|
+
ClimateState['albedo'] = albedo
|
|
590
|
+
|
|
591
|
+
# Return the new climate state
|
|
592
|
+
return ClimateState
|
|
593
|
+
|
|
594
|
+
def PropagateClimateState_Cambio1(previousClimateState, ClimateParams, dt, F_ha):
|
|
595
|
+
"""Propagates the state of the climate with no feedback"""
|
|
596
|
+
|
|
597
|
+
# Extract constants from ClimateParams
|
|
598
|
+
k_la = ClimateParams['k_la']
|
|
599
|
+
k_al0 = ClimateParams['k_al0']
|
|
600
|
+
k_al1 = ClimateParams['k_al1']
|
|
601
|
+
k_oa = ClimateParams['k_oa']
|
|
602
|
+
k_ao = ClimateParams['k_ao']
|
|
603
|
+
DC = ClimateParams['DC']
|
|
604
|
+
|
|
605
|
+
# Extract concentrations, albedo, etc, from the previous climate state
|
|
606
|
+
C_atm = previousClimateState['C_atm']
|
|
607
|
+
C_ocean = previousClimateState['C_ocean']
|
|
608
|
+
albedo = previousClimateState['albedo']
|
|
609
|
+
time = previousClimateState['time']
|
|
610
|
+
|
|
611
|
+
# Get the temperature anomaly implied by the carbon in the atmosphere and the albedo
|
|
612
|
+
T_anomaly = Diagnose_T_anomaly(C_atm, albedo, ClimateParams)
|
|
613
|
+
actual_temperature = Diagnose_actual_temperature(T_anomaly)
|
|
614
|
+
OceanSurfacepH = Diagnose_OceanSurfacepH(C_atm,ClimateParams)
|
|
615
|
+
|
|
616
|
+
# Get new fluxes (including the effect of temperature anomaly on the ocean-to-atmosphere flux)
|
|
617
|
+
F_la = k_la
|
|
618
|
+
F_al = k_al0 + k_al1*C_atm
|
|
619
|
+
F_oa = k_oa*C_ocean
|
|
620
|
+
F_ao = k_ao*C_atm
|
|
621
|
+
|
|
622
|
+
# Get new concentrations of carbon that depend on the fluxes
|
|
623
|
+
C_atm += (F_la + F_oa - F_ao - F_al + F_ha)*dt
|
|
624
|
+
C_ocean += (F_ao - F_oa)*dt
|
|
625
|
+
time += dt
|
|
626
|
+
|
|
627
|
+
# Create a new climate state with these updates
|
|
628
|
+
ClimateState = makeacopy(previousClimateState)
|
|
629
|
+
ClimateState['C_atm'] = C_atm
|
|
630
|
+
ClimateState['C_ocean'] = C_ocean
|
|
631
|
+
ClimateState['F_al'] = F_al
|
|
632
|
+
ClimateState['F_la'] = F_la
|
|
633
|
+
ClimateState['F_ao'] = F_ao
|
|
634
|
+
ClimateState['F_oa'] = F_oa
|
|
635
|
+
ClimateState['F_ha'] = F_ha
|
|
636
|
+
ClimateState['F_ocean_net'] = F_oa-F_ao
|
|
637
|
+
ClimateState['F_land_net'] = F_la-F_al
|
|
638
|
+
ClimateState['time'] = time
|
|
639
|
+
ClimateState['T_anomaly'] = T_anomaly
|
|
640
|
+
ClimateState['actual temperature'] = actual_temperature
|
|
641
|
+
ClimateState['OceanSurfacepH'] = OceanSurfacepH
|
|
642
|
+
ClimateState['albedo'] = albedo
|
|
643
|
+
|
|
644
|
+
# Return the new climate state
|
|
645
|
+
return ClimateState
|
|
646
|
+
|
|
647
|
+
def printmaxmin(t,y,label=''):
|
|
648
|
+
imax = np.argmax(y)
|
|
649
|
+
print('Max of '+label+' = ',y[imax], 'at time ', t[imax])
|
|
650
|
+
imin = np.argmin(y)
|
|
651
|
+
print('Min of '+label+' = ',y[imin], 'at time ', t[imin])
|
|
652
|
+
|
|
653
|
+
def CS_list_plots(ClimateState_list,plot_title,items_to_plot,colorlist=['k','b','g','r']):
|
|
654
|
+
linewidth = 3
|
|
655
|
+
time_array = CollectClimateTimeSeries(ClimateState_list,'time')
|
|
656
|
+
for item in items_to_plot:
|
|
657
|
+
plt.figure()
|
|
658
|
+
plt.title(plot_title)
|
|
659
|
+
plt.grid(True)
|
|
660
|
+
plt.xlabel('time (years)')
|
|
661
|
+
|
|
662
|
+
if np.size(item) == 1:
|
|
663
|
+
item_array = CollectClimateTimeSeries(ClimateState_list,item)
|
|
664
|
+
if len(plot_title) != 0:
|
|
665
|
+
plotlabel = plot_title+' ('+item+')'
|
|
666
|
+
else:
|
|
667
|
+
plotlabel = item
|
|
668
|
+
printmaxmin(time_array,item_array,label=plotlabel)
|
|
669
|
+
plt.plot(time_array,item_array,label=item,color=colorlist[0],linewidth=linewidth)
|
|
670
|
+
else:
|
|
671
|
+
icolor = 0
|
|
672
|
+
for subitem in item:
|
|
673
|
+
subitem_array = CollectClimateTimeSeries(ClimateState_list,subitem)
|
|
674
|
+
printmaxmin(time_array,subitem_array,label=plot_title+' ('+subitem+')')
|
|
675
|
+
if len(plot_title) != 0:
|
|
676
|
+
plotlabel = plot_title+' ('+subitem+')'
|
|
677
|
+
else:
|
|
678
|
+
plotlabel = subitem
|
|
679
|
+
plt.plot(time_array,subitem_array,label=plotlabel,color=colorlist[icolor],linewidth=linewidth)
|
|
680
|
+
icolor += 1
|
|
681
|
+
plt.legend()
|
|
682
|
+
plt.show()
|
|
683
|
+
return
|
|
684
|
+
|
|
685
|
+
def CS_list_compare(CS_Cambio_lists,plot_titles,items_to_plot,colorlist=['k','b','g','r']):
|
|
686
|
+
linewidth = 3
|
|
687
|
+
alpha = 0.3
|
|
688
|
+
CS_CambioA_list, CS_CambioB_list = CS_Cambio_lists
|
|
689
|
+
plot_titleA, plot_titleB = plot_titles
|
|
690
|
+
plot_title = plot_titleA+' and '+plot_titleB
|
|
691
|
+
time_arrayA = CollectClimateTimeSeries(CS_CambioA_list,'time')
|
|
692
|
+
time_arrayB = CollectClimateTimeSeries(CS_CambioB_list,'time')
|
|
693
|
+
for item in items_to_plot:
|
|
694
|
+
plt.figure()
|
|
695
|
+
plt.title(plot_title)
|
|
696
|
+
plt.grid(True)
|
|
697
|
+
plt.xlabel('time (years)')
|
|
698
|
+
|
|
699
|
+
if np.size(item) == 1:
|
|
700
|
+
item_arrayA = CollectClimateTimeSeries(CS_CambioA_list,item)
|
|
701
|
+
item_arrayB = CollectClimateTimeSeries(CS_CambioB_list,item)
|
|
702
|
+
if len(plot_titleA) != 0:
|
|
703
|
+
plotlabelA = plot_titleA+' ('+item+')'
|
|
704
|
+
else:
|
|
705
|
+
plotlabelA = item
|
|
706
|
+
if len(plot_titleB) != 0:
|
|
707
|
+
plotlabelB = plot_titleB+' ('+item+')'
|
|
708
|
+
else:
|
|
709
|
+
plotlabelB = item
|
|
710
|
+
plt.plot(time_arrayA,item_arrayA,label=plotlabelA,color=colorlist[0],linewidth=linewidth,alpha=alpha)
|
|
711
|
+
plt.plot(time_arrayB,item_arrayB,label=plotlabelB,color=colorlist[0],linewidth=linewidth)
|
|
712
|
+
else:
|
|
713
|
+
icolor = 0
|
|
714
|
+
for subitem in item:
|
|
715
|
+
subitem_arrayA = CollectClimateTimeSeries(CS_CambioA_list,subitem)
|
|
716
|
+
subitem_arrayB = CollectClimateTimeSeries(CS_CambioB_list,subitem)
|
|
717
|
+
printmaxmin(time_arrayA,subitem_arrayA,label=plot_titleA+' ('+subitem+')')
|
|
718
|
+
printmaxmin(time_arrayB,subitem_arrayB,label=plot_titleB+' ('+subitem+')')
|
|
719
|
+
if len(plot_titleA) != 0:
|
|
720
|
+
plotlabelA = plot_titleA+' ('+subitem+')'
|
|
721
|
+
else:
|
|
722
|
+
plotlabelA = subitem
|
|
723
|
+
if len(plot_titleB) != 0:
|
|
724
|
+
plotlabelB = plot_titleB+' ('+subitem+')'
|
|
725
|
+
else:
|
|
726
|
+
plotlabelB = subitem
|
|
727
|
+
plt.plot(time_arrayA,subitem_arrayA,label=plotlabelA,color=colorlist[icolor],linewidth=linewidth,alpha=alpha)
|
|
728
|
+
plt.plot(time_arrayB,subitem_arrayB,label=plotlabelB,color=colorlist[icolor],linewidth=linewidth)
|
|
729
|
+
icolor += 1
|
|
730
|
+
plt.legend()
|
|
731
|
+
plt.show()
|
|
732
|
+
return
|
|
733
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meclib
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Code for Modeling Earth's Climate course
|
|
5
|
+
Author-email: Steven Neshyba <sneshyba@gmail.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Requires-Dist: matplotlib
|
|
10
|
+
Requires-Dist: pandas
|
|
11
|
+
Requires-Dist: pint
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
examples/CambioComparisons.py
|
|
3
|
+
examples/MakeEmissionScenario.py
|
|
4
|
+
meclib/__init__.py
|
|
5
|
+
meclib/cl.py
|
|
6
|
+
meclib.egg-info/PKG-INFO
|
|
7
|
+
meclib.egg-info/SOURCES.txt
|
|
8
|
+
meclib.egg-info/dependency_links.txt
|
|
9
|
+
meclib.egg-info/requires.txt
|
|
10
|
+
meclib.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "meclib"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Code for Modeling Earth's Climate course"
|
|
5
|
+
authors = [{ name = "Steven Neshyba", email = "sneshyba@gmail.com" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.8"
|
|
8
|
+
dependencies = ['numpy', 'matplotlib', 'pandas', 'pint']
|
|
9
|
+
|
|
10
|
+
[build-system]
|
|
11
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
12
|
+
build-backend = "setuptools.build_meta"
|
|
13
|
+
|
|
14
|
+
[tool.setuptools.packages.find]
|
|
15
|
+
where = ["."]
|
meclib-0.1.1/setup.cfg
ADDED