dynamic-characterization 0.0.1.dev1__tar.gz → 0.0.2__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.
Files changed (16) hide show
  1. {dynamic_characterization-0.0.1.dev1/dynamic_characterization.egg-info → dynamic_characterization-0.0.2}/PKG-INFO +3 -1
  2. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/dynamic_characterization/__init__.py +1 -1
  3. dynamic_characterization-0.0.2/dynamic_characterization/temporalis/__init__.py +15 -0
  4. dynamic_characterization-0.0.2/dynamic_characterization/temporalis/radiative_forcing.py +139 -0
  5. dynamic_characterization-0.0.2/dynamic_characterization/timex/__init__.py +23 -0
  6. dynamic_characterization-0.0.2/dynamic_characterization/timex/radiative_forcing.py +482 -0
  7. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2/dynamic_characterization.egg-info}/PKG-INFO +3 -1
  8. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/dynamic_characterization.egg-info/SOURCES.txt +5 -1
  9. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/dynamic_characterization.egg-info/requires.txt +2 -0
  10. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/pyproject.toml +7 -1
  11. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/LICENSE +0 -0
  12. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/MANIFEST.in +0 -0
  13. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/README.md +0 -0
  14. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/dynamic_characterization.egg-info/dependency_links.txt +0 -0
  15. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/dynamic_characterization.egg-info/top_level.txt +0 -0
  16. {dynamic_characterization-0.0.1.dev1 → dynamic_characterization-0.0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dynamic_characterization
3
- Version: 0.0.1.dev1
3
+ Version: 0.0.2
4
4
  Summary: Collection of dynamic characterization functions for life cycle inventories with temporal information
5
5
  Author-email: Timo Diepers <timo.diepers@ltt.rwth-aachen.de>
6
6
  Maintainer-email: Timo Diepers <timo.diepers@ltt.rwth-aachen.de>
@@ -20,6 +20,8 @@ Classifier: Topic :: Scientific/Engineering
20
20
  Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
+ Requires-Dist: pandas
24
+ Requires-Dist: numpy
23
25
  Provides-Extra: testing
24
26
  Requires-Dist: dynamic_characterization; extra == "testing"
25
27
  Requires-Dist: pytest; extra == "testing"
@@ -9,7 +9,7 @@ __all__ = (
9
9
  # Add functions and variables you want exposed in `dynamic_characterization.` namespace here
10
10
  )
11
11
 
12
- __version__ = "0.0.1dev1"
12
+ __version__ = "0.0.2"
13
13
 
14
14
  from . import temporalis
15
15
  from . import timex
@@ -0,0 +1,15 @@
1
+ """
2
+ Dynamic characterization functions from the bw_temporalis package (https://github.com/brightway-lca/bw_temporalis).
3
+ """
4
+
5
+ __all__ = (
6
+ "__version__",
7
+ "characterize_co2",
8
+ "characterize_methane",
9
+ # Add functions and variables you want exposed in `dynamic_characterization.` namespace here
10
+ )
11
+
12
+ __version__ = "0.0.1"
13
+
14
+ from .radiative_forcing import characterize_co2
15
+ from .radiative_forcing import characterize_methane
@@ -0,0 +1,139 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+
5
+ def characterize_co2(
6
+ series,
7
+ period: int | None = 100,
8
+ cumulative: bool | None = False,
9
+ ) -> pd.DataFrame:
10
+ """
11
+ Calculate the cumulative or marginal radiative forcing (CRF) from CO2 for each year in a given period.
12
+
13
+ If `cumulative` is True, the cumulative CRF is calculated. If `cumulative` is False, the marginal CRF is calculated.
14
+ Takes a single row of the TimeSeries Pandas DataFrame (corresponding to a set of (`date`/`amount`/`flow`/`activity`).
15
+ For each year in the given period, the CRF is calculated.
16
+ Units are watts/square meter/kilogram of CO2.
17
+
18
+ Returns
19
+ -------
20
+ A TimeSeries dataframe with the following columns:
21
+ - date: datetime64[s]
22
+ - amount: float
23
+ - flow: str
24
+ - activity: str
25
+
26
+ Notes
27
+ -----
28
+ See also the relevant scientific publication on CRF: https://doi.org/10.5194/acp-13-2793-2013
29
+ See also the relevant scientific publication on the numerical calculation of CRF: http://pubs.acs.org/doi/abs/10.1021/acs.est.5b01118
30
+
31
+ See Also
32
+ --------
33
+ characterize_methane: The same function for CH4
34
+ """
35
+
36
+ # functional variables and units (from publications listed in docstring)
37
+ RE = 1.76e-15 # Radiative forcing (W/m2/kg)
38
+ alpha_0, alpha_1, alpha_2, alpha_3 = 0.2173, 0.2240, 0.2824, 0.2763
39
+ tau_1, tau_2, tau_3 = 394.4, 36.54, 4.304
40
+ decay_term = lambda year, alpha, tau: alpha * tau * (1 - np.exp(-year / tau))
41
+
42
+ date_beginning: np.datetime64 = series["date"].to_numpy()
43
+ date_characterized: np.ndarray = date_beginning + np.arange(
44
+ start=0, stop=period, dtype="timedelta64[Y]"
45
+ ).astype("timedelta64[s]")
46
+
47
+ decay_multipliers: np.ndarray = np.array(
48
+ [
49
+ RE
50
+ * (
51
+ alpha_0 * year
52
+ + decay_term(year, alpha_1, tau_1)
53
+ + decay_term(year, alpha_2, tau_2)
54
+ + decay_term(year, alpha_3, tau_3)
55
+ )
56
+ for year in range(period)
57
+ ]
58
+ )
59
+
60
+ forcing = pd.Series(data=series.amount * decay_multipliers, dtype="float64")
61
+ if not cumulative:
62
+ forcing = forcing.diff(periods=1).fillna(0)
63
+
64
+ return pd.DataFrame(
65
+ {
66
+ "date": pd.Series(data=date_characterized, dtype="datetime64[s]"),
67
+ "amount": forcing,
68
+ "flow": series.flow,
69
+ "activity": series.activity,
70
+ }
71
+ )
72
+
73
+
74
+ def characterize_methane(series, period: int = 100, cumulative=False) -> pd.DataFrame:
75
+ """
76
+ Calculate the cumulative or marginal radiative forcing (CRF) from CH4 for each year in a given period.
77
+
78
+ If `cumulative` is True, the cumulative CRF is calculated. If `cumulative` is False, the marginal CRF is calculated.
79
+ Takes a single row of the TimeSeries Pandas DataFrame (corresponding to a set of (`date`/`amount`/`flow`/`activity`).
80
+ For earch year in the given period, the CRF is calculated.
81
+ Units are watts/square meter/kilogram of CH4.
82
+
83
+ Parameters
84
+ ----------
85
+ series : array-like
86
+ A single row of the TimeSeries dataframe.
87
+ period : int, optional
88
+ Time period for calculation (number of years), by default 100
89
+ cumulative : bool, optional
90
+ Should the RF amounts be summed over time?
91
+
92
+ Returns
93
+ -------
94
+ A TimeSeries dataframe with the following columns:
95
+ - date: datetime64[s]
96
+ - amount: float
97
+ - flow: str
98
+ - activity: str
99
+
100
+ Notes
101
+ -----
102
+ See also the relevant scientific publication on CRF: https://doi.org/10.5194/acp-13-2793-2013
103
+ See also the relevant scientific publication on the numerical calculation of CRF: http://pubs.acs.org/doi/abs/10.1021/acs.est.5b01118
104
+
105
+ See Also
106
+ --------
107
+ characterize_co2: The same function for CO2
108
+ """
109
+
110
+ # functional variables and units (from publications listed in docstring)
111
+ f1 = 0.5 # Unitless
112
+ f2 = 0.15 # Unitless
113
+ alpha = 1.27e-13 # Radiative forcing (W/m2/kg)
114
+ tau = 12.4 # Lifetime (years)
115
+
116
+ date_beginning: np.datetime64 = series["date"].to_numpy()
117
+ date_characterized: np.ndarray = date_beginning + np.arange(
118
+ start=0, stop=period, dtype="timedelta64[Y]"
119
+ ).astype("timedelta64[s]")
120
+
121
+ decay_multipliers: list = np.array(
122
+ [
123
+ (1 + f1 + f2) * alpha * tau * (1 - np.exp(-year / tau))
124
+ for year in range(period)
125
+ ]
126
+ )
127
+
128
+ forcing = pd.Series(data=series.amount * decay_multipliers, dtype="float64")
129
+ if not cumulative:
130
+ forcing = forcing.diff(periods=1).fillna(0)
131
+
132
+ return pd.DataFrame(
133
+ {
134
+ "date": pd.Series(data=date_characterized, dtype="datetime64[s]"),
135
+ "amount": forcing,
136
+ "flow": series.flow,
137
+ "activity": series.activity,
138
+ }
139
+ )
@@ -0,0 +1,23 @@
1
+ """
2
+ Dynamic characterization functions from the bw_timex package (https://github.com/brightway-lca/bw_timex).
3
+ """
4
+
5
+ __all__ = (
6
+ "__version__",
7
+ "characterize_co2",
8
+ "characterize_co2_uptake",
9
+ "characterize_co",
10
+ "characterize_ch4",
11
+ "characterize_n2o",
12
+ "create_generic_characterization_function",
13
+ # Add functions and variables you want exposed in `dynamic_characterization.` namespace here
14
+ )
15
+
16
+ __version__ = "0.0.1"
17
+
18
+ from .radiative_forcing import characterize_co2
19
+ from .radiative_forcing import characterize_co2_uptake
20
+ from .radiative_forcing import characterize_co
21
+ from .radiative_forcing import characterize_ch4
22
+ from .radiative_forcing import characterize_n2o
23
+ from .radiative_forcing import create_generic_characterization_function
@@ -0,0 +1,482 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+
4
+
5
+ def IRF_co2(year) -> callable:
6
+ """
7
+ Impulse Resonse Function (IRF) of CO2
8
+
9
+ Parameters
10
+ ----------
11
+ year : int
12
+ The year after emission for which the IRF is calculated.
13
+
14
+ Returns
15
+ -------
16
+ float
17
+ The IRF value for the given year.
18
+
19
+ """
20
+ alpha_0, alpha_1, alpha_2, alpha_3 = 0.2173, 0.2240, 0.2824, 0.2763
21
+ tau_1, tau_2, tau_3 = 394.4, 36.54, 4.304
22
+ exponentials = lambda year, alpha, tau: alpha * tau * (1 - np.exp(-year / tau))
23
+ return (
24
+ alpha_0 * year
25
+ + exponentials(year, alpha_1, tau_1)
26
+ + exponentials(year, alpha_2, tau_2)
27
+ + exponentials(year, alpha_3, tau_3)
28
+ )
29
+
30
+
31
+ def characterize_co2(
32
+ series,
33
+ period: int | None = 100,
34
+ cumulative: bool | None = False,
35
+ ) -> pd.DataFrame:
36
+ """
37
+ Calculate the cumulative or marginal radiative forcing (CRF) from CO2 for each year in a given period.
38
+
39
+ Based on characterize_co2 from bw_temporalis, but updated numerical values from IPCC AR6 Ch7 & SM.
40
+
41
+ If `cumulative` is True, the cumulative CRF is calculated. If `cumulative` is False, the marginal CRF is calculated.
42
+ Takes a single row of the TimeSeries Pandas DataFrame (corresponding to a set of (`date`/`amount`/`flow`/`activity`).
43
+ For each year in the given period, the CRF is calculated.
44
+ Units are watts/square meter/kilogram of CO2.
45
+
46
+ Returns
47
+ -------
48
+ A TimeSeries dataframe with the following columns:
49
+ - date: datetime64[s]
50
+ - amount: float
51
+ - flow: str
52
+ - activity: str
53
+
54
+ See also
55
+ --------
56
+ Joos2013: Relevant scientific publication on CRF: https://doi.org/10.5194/acp-13-2793-2013
57
+ Schivley2015: Relevant scientific publication on the numerical calculation of CRF: https://doi.org/10.1021/acs.est.5b01118
58
+ Forster2023: Updated numerical values from IPCC AR6 Chapter 7 (Table 7.15): https://doi.org/10.1017/9781009157896.009
59
+ """
60
+
61
+ # functional variables and units (from publications listed in docstring)
62
+ radiative_efficiency_ppb = (
63
+ 1.33e-5 # W/m2/ppb; 2019 background co2 concentration; IPCC AR6 Table 7.15
64
+ )
65
+
66
+ # for conversion from ppb to kg-CO2
67
+ M_co2 = 44.01 # g/mol
68
+ M_air = 28.97 # g/mol, dry air
69
+ m_atmosphere = 5.135e18 # kg [Trenberth and Smith, 2005]
70
+
71
+ radiative_efficiency_kg = (
72
+ radiative_efficiency_ppb * M_air / M_co2 * 1e9 / m_atmosphere
73
+ ) # W/m2/kg-CO2
74
+
75
+ date_beginning: np.datetime64 = series["date"].to_numpy()
76
+ date_characterized: np.ndarray = date_beginning + np.arange(
77
+ start=0, stop=period, dtype="timedelta64[Y]"
78
+ ).astype("timedelta64[s]")
79
+
80
+ decay_multipliers: np.ndarray = np.array(
81
+ [radiative_efficiency_kg * IRF_co2(year) for year in range(period)]
82
+ )
83
+
84
+ forcing = pd.Series(data=series.amount * decay_multipliers, dtype="float64")
85
+
86
+ if not cumulative:
87
+ forcing = forcing.diff(periods=1).fillna(0)
88
+
89
+ return pd.DataFrame(
90
+ {
91
+ "date": pd.Series(data=date_characterized, dtype="datetime64[s]"),
92
+ "amount": forcing,
93
+ "flow": series.flow,
94
+ "activity": series.activity,
95
+ }
96
+ )
97
+
98
+
99
+ def characterize_co2_uptake(
100
+ series,
101
+ period: int | None = 100,
102
+ cumulative: bool | None = False,
103
+ ) -> pd.DataFrame:
104
+ """
105
+ The same as characterize_co2, but with a negative sign for uptake of CO2.
106
+
107
+ Based on characterize_co2 from bw_temporalis, but updated numerical values from IPCC AR6 Ch7 & SM.
108
+
109
+ Calculate the negative cumulative or marginal radiative forcing (CRF) from CO2-uptake for each year in a given period.
110
+
111
+ If `cumulative` is True, the cumulative CRF is calculated. If `cumulative` is False, the marginal CRF is calculated.
112
+ Takes a single row of the TimeSeries Pandas DataFrame (corresponding to a set of (`date`/`amount`/`flow`/`activity`).
113
+ For each year in the given period, the CRF is calculated.
114
+ Units are watts/square meter/kilogram of CO2.
115
+
116
+ Returns
117
+ -------
118
+ A TimeSeries dataframe with the following columns:
119
+ - date: datetime64[s]
120
+ - amount: float
121
+ - flow: str
122
+ - activity: str
123
+
124
+ See also
125
+ --------
126
+ Joos2013: Relevant scientific publication on CRF: https://doi.org/10.5194/acp-13-2793-2013
127
+ Schivley2015: Relevant scientific publication on the numerical calculation of CRF: https://doi.org/10.1021/acs.est.5b01118
128
+ Forster2023: Updated numerical values from IPCC AR6 Chapter 7 (Table 7.15): https://doi.org/10.1017/9781009157896.009
129
+ """
130
+
131
+ # functional variables and units (from publications listed in docstring)
132
+ radiative_efficiency_ppb = (
133
+ 1.33e-5 # W/m2/ppb; 2019 background co2 concentration; IPCC AR6 Table 7.15
134
+ )
135
+
136
+ # for conversion from ppb to kg-CO2
137
+ M_co2 = 44.01 # g/mol
138
+ M_air = 28.97 # g/mol, dry air
139
+ m_atmosphere = 5.135e18 # kg [Trenberth and Smith, 2005]
140
+
141
+ radiative_efficiency_kg = (
142
+ radiative_efficiency_ppb * M_air / M_co2 * 1e9 / m_atmosphere
143
+ ) # W/m2/kg-CO2
144
+
145
+ date_beginning: np.datetime64 = series["date"].to_numpy()
146
+ date_characterized: np.ndarray = date_beginning + np.arange(
147
+ start=0, stop=period, dtype="timedelta64[Y]"
148
+ ).astype("timedelta64[s]")
149
+
150
+ decay_multipliers: np.ndarray = np.array(
151
+ [radiative_efficiency_kg * IRF_co2(year) for year in range(period)]
152
+ )
153
+
154
+ forcing = pd.Series(data=series.amount * decay_multipliers, dtype="float64")
155
+
156
+ forcing = (
157
+ -forcing
158
+ ) # flip the sign of the characterization function for CO2 uptake and not release
159
+
160
+ if not cumulative:
161
+ forcing = forcing.diff(periods=1).fillna(0)
162
+
163
+ return pd.DataFrame(
164
+ {
165
+ "date": pd.Series(data=date_characterized, dtype="datetime64[s]"),
166
+ "amount": forcing,
167
+ "flow": series.flow,
168
+ "activity": series.activity,
169
+ }
170
+ )
171
+
172
+
173
+ def characterize_co(
174
+ series,
175
+ period: int | None = 100,
176
+ cumulative: bool | None = False,
177
+ ) -> pd.DataFrame:
178
+ """
179
+ Calculate the cumulative or marginal radiative forcing (CRF) from CO for each year in a given period.
180
+
181
+ This is exactly the same function as for CO2, it's just scaled by the ratio of molar masses of CO and CO2. This is because CO is very short-lived (lifetime ~2 months) and we assume that it completely reacts to CO2 within the first year.
182
+
183
+ Based on characterize_co2 from bw_temporalis, but updated numerical values from IPCC AR6 Ch7 & SM.
184
+
185
+ Calculate the cumulative or marginal radiative forcing (CRF) from CO2 for each year in a given period.
186
+
187
+ If `cumulative` is True, the cumulative CRF is calculated. If `cumulative` is False, the marginal CRF is calculated.
188
+ Takes a single row of the TimeSeries Pandas DataFrame (corresponding to a set of (`date`/`amount`/`flow`/`activity`).
189
+ For each year in the given period, the CRF is calculated.
190
+ Units are watts/square meter/kilogram of CO2.
191
+
192
+ Returns
193
+ -------
194
+ A TimeSeries dataframe with the following columns:
195
+ - date: datetime64[s]
196
+ - amount: float
197
+ - flow: str
198
+ - activity: str
199
+
200
+ See also
201
+ --------
202
+ Joos2013: Relevant scientific publication on CRF: https://doi.org/10.5194/acp-13-2793-2013
203
+ Schivley2015: Relevant scientific publication on the numerical calculation of CRF: https://doi.org/10.1021/acs.est.5b01118
204
+ Forster2023: Updated numerical values from IPCC AR6 Chapter 7 (Table 7.15): https://doi.org/10.1017/9781009157896.009
205
+ """
206
+
207
+ # functional variables and units (from publications listed in docstring)
208
+ radiative_efficiency_ppb = (
209
+ 1.33e-5 # W/m2/ppb; 2019 background co2 concentration; IPCC AR6 Table 7.15
210
+ )
211
+
212
+ # for conversion from ppb to kg-CO2
213
+ M_co2 = 44.01 # g/mol
214
+ M_co = 28.01 # g/mol
215
+ M_air = 28.97 # g/mol, dry air
216
+ m_atmosphere = 5.135e18 # kg [Trenberth and Smith, 2005]
217
+
218
+ radiative_efficiency_kg = (
219
+ radiative_efficiency_ppb * M_air / M_co2 * 1e9 / m_atmosphere
220
+ ) # W/m2/kg-CO2
221
+
222
+ date_beginning: np.datetime64 = series["date"].to_numpy()
223
+ date_characterized: np.ndarray = date_beginning + np.arange(
224
+ start=0, stop=period, dtype="timedelta64[Y]"
225
+ ).astype("timedelta64[s]")
226
+
227
+ decay_multipliers: np.ndarray = np.array(
228
+ [
229
+ M_co2 / M_co * radiative_efficiency_kg * IRF_co2(year)
230
+ for year in range(period)
231
+ ] # <-- Scaling from co2 to co is done here
232
+ )
233
+
234
+ forcing = pd.Series(data=series.amount * decay_multipliers, dtype="float64")
235
+
236
+ if not cumulative:
237
+ forcing = forcing.diff(periods=1).fillna(0)
238
+
239
+ return pd.DataFrame(
240
+ {
241
+ "date": pd.Series(data=date_characterized, dtype="datetime64[s]"),
242
+ "amount": forcing,
243
+ "flow": series.flow,
244
+ "activity": series.activity,
245
+ }
246
+ )
247
+
248
+
249
+ def characterize_ch4(
250
+ series,
251
+ period: int = 100,
252
+ cumulative=False,
253
+ ) -> pd.DataFrame:
254
+ """
255
+ Calculate the cumulative or marginal radiative forcing (CRF) from CH4 for each year in a given period.
256
+
257
+ Based on characterize_methane from bw_temporalis, but updated numerical values from IPCC AR6 Ch7 & SM.
258
+
259
+ This DOES include indirect effects of CH4 on ozone and water vapor, but DOES NOT include the decay to CO2.
260
+ For more info on that, see the deprecated version of bw_temporalis.
261
+
262
+ If `cumulative` is True, the cumulative CRF is calculated. If `cumulative` is False, the marginal CRF is calculated.
263
+ Takes a single row of the TimeSeries Pandas DataFrame (corresponding to a set of (`date`/`amount`/`flow`/`activity`).
264
+ For earch year in the given period, the CRF is calculated.
265
+ Units are watts/square meter/kilogram of CH4.
266
+
267
+ Parameters
268
+ ----------
269
+ series : array-like
270
+ A single row of the TimeSeries dataframe.
271
+ period : int, optional
272
+ Time period for calculation (number of years), by default 100
273
+ cumulative : bool, optional
274
+ Should the RF amounts be summed over time?
275
+
276
+ Returns
277
+ -------
278
+ A TimeSeries dataframe with the following columns:
279
+ - date: datetime64[s]
280
+ - amount: float
281
+ - flow: str
282
+ - activity: str
283
+
284
+ See also
285
+ --------
286
+ Joos2013: Relevant scientific publication on CRF: https://doi.org/10.5194/acp-13-2793-2013
287
+ Schivley2015: Relevant scientific publication on the numerical calculation of CRF: https://doi.org/10.1021/acs.est.5b01118
288
+ Forster2023: Updated numerical values from IPCC AR6 Chapter 7 (Table 7.15): https://doi.org/10.1017/9781009157896.009
289
+ """
290
+
291
+ # functional variables and units (from publications listed in docstring)
292
+ radiative_efficiency_ppb = 5.7e-4 # # W/m2/ppb; 2019 background cch4 concentration; IPCC AR6 Table 7.15. This number includes indirect effects.
293
+
294
+ # for conversion from ppb to kg-CH4
295
+ M_ch4 = 16.04 # g/mol
296
+ M_air = 28.97 # g/mol, dry air
297
+ m_atmosphere = 5.135e18 # kg [Trenberth and Smith, 2005]
298
+
299
+ radiative_efficiency_kg = (
300
+ radiative_efficiency_ppb * M_air / M_ch4 * 1e9 / m_atmosphere
301
+ ) # W/m2/kg-CH4
302
+
303
+ tau = 11.8 # Lifetime (years)
304
+
305
+ date_beginning: np.datetime64 = series["date"].to_numpy()
306
+ date_characterized: np.ndarray = date_beginning + np.arange(
307
+ start=0, stop=period, dtype="timedelta64[Y]"
308
+ ).astype("timedelta64[s]")
309
+
310
+ decay_multipliers: list = np.array(
311
+ [
312
+ radiative_efficiency_kg * tau * (1 - np.exp(-year / tau))
313
+ for year in range(period)
314
+ ]
315
+ )
316
+
317
+ forcing = pd.Series(data=series.amount * decay_multipliers, dtype="float64")
318
+
319
+ if not cumulative:
320
+ forcing = forcing.diff(periods=1).fillna(0)
321
+
322
+ return pd.DataFrame(
323
+ {
324
+ "date": pd.Series(data=date_characterized, dtype="datetime64[s]"),
325
+ "amount": forcing,
326
+ "flow": series.flow,
327
+ "activity": series.activity,
328
+ }
329
+ )
330
+
331
+
332
+ def characterize_n2o(
333
+ series,
334
+ period: int = 100,
335
+ cumulative=False,
336
+ ) -> pd.DataFrame:
337
+ """
338
+ Calculate the cumulative or marginal radiative forcing (CRF) from N2O for each year in a given period.
339
+
340
+ Based on characterize_methane from bw_temporalis, but updated numerical values from IPCC AR6 Ch7 & SM.
341
+
342
+ If `cumulative` is True, the cumulative CRF is calculated. If `cumulative` is False, the marginal CRF is calculated.
343
+ Takes a single row of the TimeSeries Pandas DataFrame (corresponding to a set of (`date`/`amount`/`flow`/`activity`).
344
+ For earch year in the given period, the CRF is calculated.
345
+ Units are watts/square meter/kilogram of N2O.
346
+
347
+ Parameters
348
+ ----------
349
+ series : array-like
350
+ A single row of the TimeSeries dataframe.
351
+ period : int, optional
352
+ Time period for calculation (number of years), by default 100
353
+ cumulative : bool, optional
354
+ Should the RF amounts be summed over time?
355
+
356
+ Returns
357
+ -------
358
+ A TimeSeries dataframe with the following columns:
359
+ - date: datetime64[s]
360
+ - amount: float
361
+ - flow: str
362
+ - activity: str
363
+
364
+ See also
365
+ --------
366
+ Joos2013: Relevant scientific publication on CRF: https://doi.org/10.5194/acp-13-2793-2013
367
+ Schivley2015: Relevant scientific publication on the numerical calculation of CRF: https://doi.org/10.1021/acs.est.5b01118
368
+ Forster2023: Updated numerical values from IPCC AR6 Chapter 7 (Table 7.15): https://doi.org/10.1017/9781009157896.009
369
+ """
370
+
371
+ # functional variables and units (from publications listed in docstring)
372
+ radiative_efficiency_ppb = 2.8e-3 # # W/m2/ppb; 2019 background cch4 concentration; IPCC AR6 Table 7.15. This number includes indirect effects.
373
+
374
+ # for conversion from ppb to kg-CH4
375
+ M_n2o = 44.01 # g/mol
376
+ M_air = 28.97 # g/mol, dry air
377
+ m_atmosphere = 5.135e18 # kg [Trenberth and Smith, 2005]
378
+
379
+ radiative_efficiency_kg = (
380
+ radiative_efficiency_ppb * M_air / M_n2o * 1e9 / m_atmosphere
381
+ ) # W/m2/kg-N2O
382
+
383
+ tau = 109 # Lifetime (years)
384
+
385
+ date_beginning: np.datetime64 = series["date"].to_numpy()
386
+ date_characterized: np.ndarray = date_beginning + np.arange(
387
+ start=0, stop=period, dtype="timedelta64[Y]"
388
+ ).astype("timedelta64[s]")
389
+
390
+ decay_multipliers: list = np.array(
391
+ [
392
+ radiative_efficiency_kg * tau * (1 - np.exp(-year / tau))
393
+ for year in range(period)
394
+ ]
395
+ )
396
+
397
+ forcing = pd.Series(data=series.amount * decay_multipliers, dtype="float64")
398
+ if not cumulative:
399
+ forcing = forcing.diff(periods=1).fillna(0)
400
+
401
+ return pd.DataFrame(
402
+ {
403
+ "date": pd.Series(data=date_characterized, dtype="datetime64[s]"),
404
+ "amount": forcing,
405
+ "flow": series.flow,
406
+ "activity": series.activity,
407
+ }
408
+ )
409
+
410
+
411
+ def create_generic_characterization_function(decay_series) -> pd.DataFrame:
412
+ """
413
+ Creates a characterization function for a GHG based on a decay series, by calling the nested method `characterize_generic()`.
414
+
415
+ Parameters
416
+ ----------
417
+ decay_series : np.ndarray
418
+ A decay series for a specific GHG. This is retrieved from `../data/decay_multipliers.pkl`
419
+
420
+ Returns
421
+ -------
422
+ A function called `characterize_generic`, which in turn returns a TimeSeries dataframe that contains the forcing of the emission of the row over the given period based on the decay series of that biosphere flow.
423
+
424
+ """
425
+
426
+ def characterize_generic(
427
+ series,
428
+ period: int = 100,
429
+ cumulative=False,
430
+ ) -> pd.DataFrame:
431
+ """
432
+ Uses lookup generated in /dev/calculate_metrics.ipynb
433
+ Data originates from https://doi.org/10.1029/2019RG000691
434
+
435
+ Parameters
436
+ ----------
437
+ series : array-like
438
+ A single row of the dynamic inventory dataframe.
439
+ period : int, optional
440
+ Time period for calculation (number of years), by default 100
441
+ cumulative : bool,
442
+ cumulative impact
443
+
444
+ Returns
445
+ -------
446
+ A TimeSeries dataframe that contains the forcing of the point emission from the row for each year in the given period.
447
+ date: datetime64[s]
448
+ amount: float (forcing at this timestep)
449
+ flow: str
450
+ activity: str
451
+
452
+ See also
453
+ --------
454
+ Joos2013: Relevant scientific publication on CRF: https://doi.org/10.5194/acp-13-2793-2013
455
+ Schivley2015: Relevant scientific publication on the numerical calculation of CRF: https://doi.org/10.1021/acs.est.5b01118
456
+ Forster2023: Updated numerical values from IPCC AR6 Chapter 7 (Table 7.15): https://doi.org/10.1017/9781009157896.009
457
+
458
+ """
459
+
460
+ date_beginning: np.datetime64 = series["date"].to_numpy()
461
+
462
+ dates_characterized: np.ndarray = date_beginning + np.arange(
463
+ start=0, stop=period, dtype="timedelta64[Y]"
464
+ ).astype("timedelta64[s]")
465
+
466
+ decay_multipliers = decay_series[:period]
467
+
468
+ forcing = pd.Series(data=series.amount * decay_multipliers, dtype="float64")
469
+
470
+ if not cumulative:
471
+ forcing = forcing.diff(periods=1).fillna(0)
472
+
473
+ return pd.DataFrame(
474
+ {
475
+ "date": pd.Series(data=dates_characterized, dtype="datetime64[s]"),
476
+ "amount": forcing,
477
+ "flow": series.flow,
478
+ "activity": series.activity,
479
+ }
480
+ )
481
+
482
+ return characterize_generic
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dynamic_characterization
3
- Version: 0.0.1.dev1
3
+ Version: 0.0.2
4
4
  Summary: Collection of dynamic characterization functions for life cycle inventories with temporal information
5
5
  Author-email: Timo Diepers <timo.diepers@ltt.rwth-aachen.de>
6
6
  Maintainer-email: Timo Diepers <timo.diepers@ltt.rwth-aachen.de>
@@ -20,6 +20,8 @@ Classifier: Topic :: Scientific/Engineering
20
20
  Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
+ Requires-Dist: pandas
24
+ Requires-Dist: numpy
23
25
  Provides-Extra: testing
24
26
  Requires-Dist: dynamic_characterization; extra == "testing"
25
27
  Requires-Dist: pytest; extra == "testing"
@@ -7,4 +7,8 @@ dynamic_characterization.egg-info/PKG-INFO
7
7
  dynamic_characterization.egg-info/SOURCES.txt
8
8
  dynamic_characterization.egg-info/dependency_links.txt
9
9
  dynamic_characterization.egg-info/requires.txt
10
- dynamic_characterization.egg-info/top_level.txt
10
+ dynamic_characterization.egg-info/top_level.txt
11
+ dynamic_characterization/temporalis/__init__.py
12
+ dynamic_characterization/temporalis/radiative_forcing.py
13
+ dynamic_characterization/timex/__init__.py
14
+ dynamic_characterization/timex/radiative_forcing.py
@@ -31,6 +31,8 @@ requires-python = ">=3.9"
31
31
  dependencies = [
32
32
  # dependencies as strings with quotes, e.g. "foo"
33
33
  # You can add version requirements like "foo>2.0"
34
+ "pandas",
35
+ "numpy",
34
36
  ]
35
37
 
36
38
  [project.urls]
@@ -60,7 +62,11 @@ dev = [
60
62
  [tool.setuptools]
61
63
  license-files = ["LICENSE"]
62
64
  include-package-data = true
63
- packages = ["dynamic_characterization"]
65
+ packages = [
66
+ "dynamic_characterization",
67
+ "dynamic_characterization.temporalis",
68
+ "dynamic_characterization.timex",
69
+ ]
64
70
 
65
71
  [tool.setuptools.dynamic]
66
72
  version = {attr = "dynamic_characterization.__version__"}