Skip to content

Commit c4d2b3d

Browse files
committed
Merge branch 'solve' into develop
# Conflicts: # beam_carbon/beam.py
2 parents ffe2aa3 + d655ab1 commit c4d2b3d

File tree

1 file changed

+92
-58
lines changed

1 file changed

+92
-58
lines changed

beam_carbon/beam.py

Lines changed: 92 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
33
from __future__ import division
4-
from math import floor, sqrt
4+
from math import floor
55
import numpy as np
66
import pandas as pd
7-
from sympy import symbols, solve, Eq
87
from beam_carbon.temperature import DICETemperature, LinearTemperature
98

109

11-
__version__ = '0.2'
10+
__version__ = '0.3'
1211

1312

1413
class BEAMCarbon(object):
@@ -18,11 +17,13 @@ def __init__(self, emissions=None, time_step=1, intervals=10):
1817
"""BEAMCarbon init
1918
2019
Args:
21-
:param emissions: Array of annual emissions in GtC, beginning in 2005
20+
:param emissions: Array of annual emissions in GtC, beginning
21+
in 2005
2222
:type emissions: list
2323
:param time_step: Time between emissions values in years
2424
:type time_step: float
25-
:param intervals: Nuber of times to calculate BEAM carbon per timestep
25+
:param intervals: Number of times to calculate BEAM carbon
26+
per timestep
2627
:type intervals: int
2728
"""
2829
self._temperature_dependent = True
@@ -40,6 +41,10 @@ def __init__(self, emissions=None, time_step=1, intervals=10):
4041
self._k_h = 1.23e3
4142
self._A = None
4243
self._B = None
44+
self._Alk = 767.
45+
46+
self._initial_carbon = np.array([808.9, 725., 35641.])
47+
self._carbon_mass = None
4348

4449
self._linear_temperature = False
4550

@@ -48,7 +53,21 @@ def initial_carbon(self):
4853
"""Values for initial carbon in atmosphere, upper and lower oceans
4954
in GtC. Default values are from 2005.
5055
"""
51-
return np.array([808.9, 725., 35641.])
56+
return self._initial_carbon
57+
58+
@initial_carbon.setter
59+
def initial_carbon(self, value):
60+
self._initial_carbon = value
61+
62+
@property
63+
def carbon_mass(self):
64+
if self._carbon_mass is None:
65+
self._carbon_mass = self.initial_carbon.copy()
66+
return self._carbon_mass
67+
68+
@carbon_mass.setter
69+
def carbon_mass(self, value):
70+
self._carbon_mass = value
5271

5372
@property
5473
def transfer_matrix(self):
@@ -121,6 +140,8 @@ def delta(self):
121140
def k_h(self):
122141
"""CO2 solubility.
123142
"""
143+
if self._k_h is None:
144+
self._k_h = self.get_kh(self.temperature.initial_temp[1])
124145
return self._k_h
125146

126147
@k_h.setter
@@ -129,8 +150,10 @@ def k_h(self, value):
129150

130151
@property
131152
def k_1(self):
132-
"""First dissacoiation constant.
153+
"""First dissociation constant.
133154
"""
155+
if self._k_1 is None:
156+
self._k_1 = self.get_k1(self.temperature.initial_temp[1])
134157
return self._k_1
135158

136159
@k_1.setter
@@ -139,8 +162,10 @@ def k_1(self, value):
139162

140163
@property
141164
def k_2(self):
142-
"""Second dissacoiation constant.
165+
"""Second dissociation constant.
143166
"""
167+
if self._k_2 is None:
168+
self._k_2 = self.get_k2(self.temperature.initial_temp[1])
144169
return self._k_2
145170

146171
@k_2.setter
@@ -163,7 +188,11 @@ def OM(self):
163188
def Alk(self):
164189
"""Alkalinity in GtC.
165190
"""
166-
return 767.
191+
return self._Alk
192+
193+
@Alk.setter
194+
def Alk(self, value):
195+
self._Alk = value
167196

168197
@property
169198
def A(self):
@@ -200,7 +229,7 @@ def salinity(self):
200229

201230
@property
202231
def temperature_dependent(self):
203-
"""Switch for calculating temperature-dependent paramters k_1,
232+
"""Switch for calculating temperature-dependent parameters k_1,
204233
k_2, and k_h.
205234
"""
206235
return self._temperature_dependent
@@ -212,9 +241,11 @@ def linear_temperature(self):
212241
@linear_temperature.setter
213242
def linear_temperature(self, value):
214243
if value:
215-
self.temperature = LinearTemperature(self.time_step, self.intervals, self.n)
244+
self.temperature = LinearTemperature(self.time_step,
245+
self.intervals, self.n)
216246
else:
217-
self.temperature = DICETemperature(self.time_step, self.intervals, self.n)
247+
self.temperature = DICETemperature(self.time_step,
248+
self.intervals, self.n)
218249
self._linear_temperature = value
219250

220251
@temperature_dependent.setter
@@ -251,7 +282,7 @@ def get_A(self):
251282
"""
252283
return self.k_h * self.AM / (self.OM / (self.delta + 1))
253284

254-
def get_H(self, mass_upper, re_solve=False):
285+
def get_H(self, mass_upper):
255286
"""Solve for H+, the concentration of hydrogen ions
256287
(the (pH) of seawater).
257288
@@ -260,21 +291,10 @@ def get_H(self, mass_upper, re_solve=False):
260291
:return: H
261292
:rtype: float
262293
"""
263-
if re_solve:
264-
h = symbols('h')
265-
a = mass_upper / self.Alk
266-
f = Eq(
267-
(h**2 + self.k_1 * h + self.k_1 * self.k_2) / self.k_1,
268-
a * (h + 2 * self.k_2)
269-
)
270-
return max(solve(f, h))
271-
return (
272-
-self.k_1 * (self.Alk - mass_upper) / (2 * self.Alk) +
273-
sqrt(self.k_1 * (self.Alk ** 2 * self.k_1 -
274-
4 * self.Alk ** 2 * self.k_2 -
275-
2 * self.Alk * self.k_1 * mass_upper +
276-
8 * self.Alk * self.k_2 * mass_upper +
277-
self.k_1 * mass_upper ** 2)) / (2 * self.Alk))
294+
p0 = 1
295+
p1 = (self.k_1 - mass_upper * self.k_1 / self.Alk)
296+
p2 = (1 - 2 * mass_upper / self.Alk) * self.k_1 * self.k_2
297+
return max(np.roots([p0, p1, p2]))
278298

279299
def get_kh(self, temp_ocean):
280300
"""Calculate temperature dependent k_h
@@ -293,6 +313,18 @@ def get_kh(self, temp_ocean):
293313
self.A = kh * self.AM / (self.OM / (self.delta + 1.))
294314
return kh
295315

316+
def get_pk1(self, t):
317+
return (
318+
-13.721 + 0.031334 * t + 3235.76 / t + 1.3e-5 * self.salinity * t -
319+
0.1031 * self.salinity ** 0.5)
320+
321+
def get_pk2(self, t):
322+
return (
323+
5371.96 + 1.671221 * t + 0.22913 * self.salinity +
324+
18.3802 * np.log10(self.salinity)) - (128375.28 / t +
325+
2194.30 * np.log10(t) + 8.0944e-4 * self.salinity * t +
326+
5617.11 * np.log10(self.salinity) / t) + 2.136 * self.salinity / t
327+
296328
def get_k1(self, temp_ocean):
297329
"""Calculate temperature dependent k_1
298330
@@ -301,11 +333,7 @@ def get_k1(self, temp_ocean):
301333
:return: k_1
302334
:rtype: float
303335
"""
304-
t = 283.15 + temp_ocean
305-
pk1 = (
306-
-13.721 + 0.031334 * t + 3235.76 / t + 1.3e-5 * self.salinity * t -
307-
0.1031 * self.salinity ** 0.5)
308-
return 10 ** -pk1
336+
return 10 ** -self.get_pk1(283.15 + temp_ocean)
309337

310338
def get_k2(self, temp_ocean):
311339
"""Calculate temperature dependent k_2
@@ -315,72 +343,71 @@ def get_k2(self, temp_ocean):
315343
:return: k_2
316344
:rtype: float
317345
"""
318-
t = 283.15 + temp_ocean
319-
pk2 = (
320-
5371.96 + 1.671221 * t + 0.22913 * self.salinity +
321-
18.3802 * np.log10(self.salinity)) - (128375.28 / t +
322-
2194.30 * np.log10(t) + 8.0944e-4 * self.salinity * t +
323-
5617.11 * np.log10(self.salinity) / t) + 2.136 * self.salinity / t
324-
return 10 ** -pk2
346+
return 10 ** -self.get_pk2(283.15 + temp_ocean)
325347

326348
def run(self):
327349
N = self.n * self.intervals
350+
self.carbon_mass = self.initial_carbon.copy()
351+
total_carbon = 0
352+
emissions = np.zeros(3)
353+
temp_atmosphere, temp_ocean = self.temperature.initial_temp
354+
328355
output = np.tile(np.concatenate((
329356
self.initial_carbon,
330357
self.temperature.initial_temp,
331358
np.array([
332359
self.transfer_matrix[0][1], self.transfer_matrix[1][1]]),
333360
np.zeros(3),
334-
)).reshape((10, 1)).copy(), self.n + 1)
361+
)).reshape((10, 1)).copy(), (self.n + 1, ))
335362
output = pd.DataFrame(
336363
output,
337364
index=['mass_atmosphere', 'mass_upper', 'mass_lower',
338365
'temp_atmosphere', 'temp_ocean', 'phi12', 'phi22',
339366
'cumulative', 'A', 'B'],
340367
columns=np.arange(self.n + 1) * self.time_step,
341368
)
342-
carbon_mass = self.initial_carbon.copy()
343-
total_carbon = 0
344-
emissions = np.zeros(3)
345-
temp_atmosphere, temp_ocean = self.temperature.initial_temp
346369

347370
for i in xrange(N):
348371

349372
_i = int(floor(i / self.intervals)) # time_step
350373

351-
if i % self.intervals == 0 and self.temperature_dependent: # First interval in time step
374+
if i % self.intervals == 0 and self.temperature_dependent:
352375
self.temp_calibrate(temp_ocean)
353376

354-
h = self.get_H(carbon_mass[1], re_solve=False)
377+
h = self.get_H(self.carbon_mass[1])
355378
self.B = self.get_B(h)
356379

357-
if i % self.intervals == 0: # First interval in time step
380+
emissions[0] = self.emissions[_i] * self.time_step / self.intervals
381+
total_carbon += emissions[0]
382+
383+
self.carbon_mass += (
384+
(self.transfer_matrix *
385+
np.divide(self.carbon_mass, self.intervals)).sum(axis=1) +
386+
emissions)
387+
388+
if (i + 1) % self.intervals == 0:
358389

359390
emissions[0] = self.emissions[_i] * self.time_step
360391
total_carbon += emissions[0]
361392

362393
ta = temp_atmosphere
363394
temp_atmosphere = self.temperature.temp_atmosphere(
364395
index=_i, temp_atmosphere=ta,
365-
temp_ocean=temp_ocean, mass_atmosphere=carbon_mass[0],
396+
temp_ocean=temp_ocean, mass_atmosphere=self.carbon_mass[0],
366397
carbon=total_carbon, initial_carbon=self.initial_carbon,
367398
phi11=self.transfer_matrix[0][0],
368399
phi21=self.transfer_matrix[1][0])
369400
temp_ocean = self.temperature.temp_ocean(
370401
ta, temp_ocean)
371402

372-
carbon_mass += (((self.transfer_matrix * carbon_mass) /
373-
self.intervals).sum(axis=1) +
374-
emissions / self.intervals)
375-
376-
if (i + 1) % self.intervals == 0:
377403
output.iloc[:, _i + 1] = (
378-
np.concatenate((carbon_mass.copy(),
404+
np.concatenate((self.carbon_mass.copy(),
379405
np.array([temp_atmosphere, temp_ocean]),
380406
np.array([self.transfer_matrix[0][1],
381407
self.transfer_matrix[1][1],
382408
total_carbon,
383409
self.A, self.B]))))
410+
384411
return output
385412

386413

@@ -441,9 +468,16 @@ def write_beam(output, csv=None):
441468
if __name__ == '__main__':
442469
b = BEAMCarbon()
443470
b.time_step = 10.
444-
b.intervals = 20
471+
b.intervals = 200
445472
N = 100
446-
b.emissions = np.concatenate((10. * np.exp(-np.arange(N) / 40), np.zeros(100-N)))
473+
b.emissions = np.array([
474+
# 7.10, 7.97,
475+
9.58, 12.25, 14.72, 16.07, 17.43, 19.16, 20.89, 23.22, 26.15, 29.09
476+
])
477+
# df = pd.DataFrame.from_csv('webDICE-CSV.csv', header=-1, index_col=0)
478+
# b.emissions = np.array(df.ix['emissions_total', :])
479+
# b.emissions = np.concatenate((10. * np.exp(-np.arange(N) / 40), np.zeros(100-N)))
447480
b.temperature_dependent = False
448481
b.linear_temperature = False
449-
r = b.run()
482+
r = b.run()
483+
print(r)

0 commit comments

Comments
 (0)