mrio-toolbox 1.0.0__py3-none-any.whl → 1.1.2__py3-none-any.whl

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.

Potentially problematic release.


This version of mrio-toolbox might be problematic. Click here for more details.

Files changed (67) hide show
  1. __init__.py +21 -0
  2. {mrio_toolbox/_parts → _parts}/_Axe.py +95 -37
  3. {mrio_toolbox/_parts → _parts}/_Part.py +346 -111
  4. _parts/__init__.py +7 -0
  5. {mrio_toolbox/_parts → _parts}/part_operations.py +24 -17
  6. extractors/__init__.py +20 -0
  7. extractors/downloaders.py +36 -0
  8. extractors/emerging/__init__.py +3 -0
  9. extractors/emerging/emerging_extractor.py +117 -0
  10. extractors/eora/__init__.py +3 -0
  11. extractors/eora/eora_extractor.py +132 -0
  12. extractors/exiobase/__init__.py +3 -0
  13. extractors/exiobase/exiobase_extractor.py +270 -0
  14. extractors/extractors.py +81 -0
  15. extractors/figaro/__init__.py +3 -0
  16. extractors/figaro/figaro_downloader.py +280 -0
  17. extractors/figaro/figaro_extractor.py +187 -0
  18. extractors/gloria/__init__.py +3 -0
  19. extractors/gloria/gloria_extractor.py +202 -0
  20. extractors/gtap11/__init__.py +7 -0
  21. extractors/gtap11/extraction/__init__.py +3 -0
  22. extractors/gtap11/extraction/extractor.py +129 -0
  23. extractors/gtap11/extraction/harpy_files/__init__.py +6 -0
  24. extractors/gtap11/extraction/harpy_files/_header_sets.py +279 -0
  25. extractors/gtap11/extraction/harpy_files/har_file.py +262 -0
  26. extractors/gtap11/extraction/harpy_files/har_file_io.py +974 -0
  27. extractors/gtap11/extraction/harpy_files/header_array.py +300 -0
  28. extractors/gtap11/extraction/harpy_files/sl4.py +229 -0
  29. extractors/gtap11/gtap_mrio/__init__.py +6 -0
  30. extractors/gtap11/gtap_mrio/mrio_builder.py +158 -0
  31. extractors/icio/__init__.py +3 -0
  32. extractors/icio/icio_extractor.py +121 -0
  33. extractors/wiod/__init__.py +3 -0
  34. extractors/wiod/wiod_extractor.py +143 -0
  35. mrio_toolbox/mrio.py → mrio.py +254 -94
  36. {mrio_toolbox-1.0.0.dist-info → mrio_toolbox-1.1.2.dist-info}/METADATA +11 -7
  37. mrio_toolbox-1.1.2.dist-info/RECORD +59 -0
  38. {mrio_toolbox-1.0.0.dist-info → mrio_toolbox-1.1.2.dist-info}/WHEEL +1 -1
  39. mrio_toolbox-1.1.2.dist-info/top_level.txt +6 -0
  40. msm/__init__.py +6 -0
  41. msm/multi_scale_mapping.py +863 -0
  42. utils/__init__.py +3 -0
  43. utils/converters/__init__.py +5 -0
  44. {mrio_toolbox/utils → utils}/converters/pandas.py +5 -6
  45. {mrio_toolbox/utils → utils}/converters/xarray.py +6 -15
  46. utils/formatting/formatter.py +527 -0
  47. utils/loaders/__init__.py +7 -0
  48. {mrio_toolbox/utils → utils}/loaders/_loader.py +60 -4
  49. {mrio_toolbox/utils → utils}/loaders/_loader_factory.py +22 -1
  50. {mrio_toolbox/utils → utils}/loaders/_nc_loader.py +37 -1
  51. {mrio_toolbox/utils → utils}/loaders/_pandas_loader.py +29 -3
  52. {mrio_toolbox/utils → utils}/loaders/_parameter_loader.py +61 -16
  53. {mrio_toolbox/utils → utils}/savers/__init__.py +3 -0
  54. utils/savers/_path_checker.py +37 -0
  55. {mrio_toolbox/utils → utils}/savers/_to_folder.py +6 -1
  56. utils/savers/_to_nc.py +60 -0
  57. mrio_toolbox/__init__.py +0 -5
  58. mrio_toolbox/_parts/__init__.py +0 -3
  59. mrio_toolbox/utils/converters/__init__.py +0 -2
  60. mrio_toolbox/utils/loaders/__init__.py +0 -3
  61. mrio_toolbox/utils/savers/_path_checker.py +0 -19
  62. mrio_toolbox/utils/savers/_to_nc.py +0 -52
  63. mrio_toolbox-1.0.0.dist-info/RECORD +0 -26
  64. mrio_toolbox-1.0.0.dist-info/top_level.txt +0 -1
  65. {mrio_toolbox-1.0.0.dist-info → mrio_toolbox-1.1.2.dist-info/licenses}/LICENSE +0 -0
  66. {mrio_toolbox/utils → utils/formatting}/__init__.py +0 -0
  67. {mrio_toolbox/utils → utils}/loaders/_np_loader.py +0 -0
@@ -0,0 +1,863 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Multi-scale mapping of PRIMAP data into an MRIO table.
4
+
5
+ Created on Wed May 11 12:57:54 2022
6
+ @author: beaufils
7
+ """
8
+
9
+ import os
10
+ import io
11
+ import csv
12
+ import pandas as pd
13
+ import numpy as np
14
+ from mrio_toolbox import MRIO
15
+ import logging as log
16
+
17
+ log.getLogger(__name__)
18
+
19
+ #Paths collection
20
+ package_directory = os.path.dirname(os.path.abspath(__file__))
21
+ mrio_path = os.path.join(package_directory,"MRIO")
22
+ primap_path = os.path.join(package_directory,'PRIMAP')
23
+ mapping_path = os.path.join(package_directory,'mappings')
24
+ extension_path = os.path.join(mrio_path,"eora26","formatted")
25
+ crf_version = '2021_1'
26
+ crf_name = 'Guetschow-et-al-2021-PRIMAP-crf_2021-v1'
27
+ hist_version='2.4.2'
28
+ hist_name = 'Guetschow-et-al-2023a-PRIMAP-hist_v2.4.2_final_09-Mar-2023'
29
+
30
+ #Global Warming Potentials (GWPs) for computing the Kyoto GHG basket
31
+ #Current version: Fourth Assessment Report (AR4)
32
+ gwp = {
33
+ "CO2" : 1,
34
+ "CH4" : 25,
35
+ "N2O" : 298,
36
+ "HFCS (AR4GWP100)" : 1,
37
+ "PFCS (AR4GWP100)" : 1,
38
+ "SF6" : 22800,
39
+ }
40
+
41
+ def multi_scale_mapping(
42
+ mrio,
43
+ mapping_file='template_crf_mapping', year=2015, emissions_year = "same",
44
+ mapping_extension=".csv",
45
+ table='icio2021',
46
+ primap_path = primap_path,
47
+ mapping_path = mapping_path,
48
+ entities =['CO2'], kyoto_basket = False,
49
+ crf_version = crf_version, crf_name = crf_name,
50
+ hist_version = hist_version, hist_name = hist_name,
51
+ categories_output=['M.0.EL'],min_sources=5,
52
+ **kwargs):
53
+ """
54
+ Maps PRIMAP data into an MRIO.
55
+ Produces satellite matrices for GHGs listed in 'entities'.
56
+
57
+ Parameters
58
+ ----------
59
+ mapping_file : str, optional
60
+ Name of the mapping file to use.
61
+ year : int, optional
62
+ Data year to use.
63
+ Make sure that data are available for that year
64
+ in both PRIMAP and the MRIOT.
65
+ emissions_year : int or "same", optional
66
+ Year to use as a source for emission data.
67
+ If "same", taken as the "year" variable.
68
+ mapping_extension : str
69
+ Extension under which the mapping table is saved.
70
+ Currently supported: excel (.xslx) and csv (.csv)
71
+ primap_path : path-like, optional
72
+ Path to the folder containing the Primap data.
73
+ mapping_path : path-like, optional
74
+ Path to the folder containing the mapping instructions
75
+ entities : list of strings, optional
76
+ List of GHGs to map.
77
+ Refer to the PRIMAP-hist file for the GHGs available.
78
+ The default is CO2.
79
+ kyoto_basket : bool, optional
80
+ Whether to include a custom estimation of global GHG emissions
81
+ (in kt/CO2e) based on the global warming potential.
82
+ crf_version : str, optional
83
+ Version of the PRIMAP-crf database.
84
+ crf_name : str, optional
85
+ Name of the PRIMAP-crf file.
86
+ hist_version : str
87
+ Version of the PRIMAP-hist database.
88
+ hist_name : str, optional
89
+ Name of the PRIMAP-hist file.
90
+ categories_output : str, optional
91
+ List of crf categories to return.
92
+ The default is M.0.EL, i.e. total emissions excluding land use.
93
+ If "all", all crf categories are returned.
94
+ min_sources : int, optional
95
+ Minimal number of sources available in Primap crf to compute
96
+ a world average intensity coefficients.
97
+ The default is 10.
98
+ include_emission_version : bool
99
+ Whether to include crf and hist data code to the file name
100
+
101
+ Returns
102
+ -------
103
+ None. Files are saved directly in the extension_path folder.
104
+
105
+ """
106
+ log.info(f"Map emissions for {table} in {year}")
107
+ emissions_year = kwargs.get("emissions_year", year)
108
+
109
+ countries_name = kwargs.get("countries","countries")
110
+ sectors_name = kwargs.get("sectors","sectors")
111
+
112
+ log.info("Loading MRIO data")
113
+ go,countries,sectors = get_go(mrio,
114
+ countries_name,
115
+ sectors_name,
116
+ kwargs.get("final_demand","y"),
117
+ kwargs.get("inter_industry","t"))
118
+ nsectors = len(sectors) - 1 #Excludes final demand from sectors count
119
+ log.info("Done")
120
+
121
+ log.info("Load mapping and PRIMAP crf")
122
+ cat_crf,mapping,crf_parents,crf_childs = \
123
+ load_mapping(mapping_path,mapping_file,sectors,mapping_extension)
124
+ crf = load_primap(primap_path, crf_version, crf_name,emissions_year,
125
+ countries, cat_crf, entities, mode='crf')
126
+ log.info("Done")
127
+ #Compute the intensity factors for each emission category and each GHG
128
+ intensity,sources_intensity = \
129
+ intensity_factor(crf,mapping,cat_crf,countries,go,entities,
130
+ nsectors, min_sources)
131
+
132
+ #Order crf data into a 3D file,
133
+ #with information available for each GHG/country/category
134
+ crf = order_primap(crf, countries,cat_crf,entities,mode='crf')
135
+ #Create a coupled 'crf' file for storing the state of knowledge of the data
136
+ computed = np.copy(crf)
137
+ ratio = np.zeros([len(countries),len(entities),len(cat_crf)])
138
+ ratio[crf!=0] = 1 #Mark known values as sourced inputs
139
+
140
+ #First step: extrapolation using crf intensity factors
141
+ for country_id in range(len(countries)):
142
+ for entity_id in range(len(entities)):
143
+ data = np.copy(crf[country_id,entity_id])
144
+ #Extract data for the corresponding country/gas from the crf table
145
+ extract_i = intensity[entity_id]
146
+ #Extract intensity factors for the corresponding gas
147
+ goi = np.concatenate((go[0][
148
+ country_id*(len(sectors)-1):(country_id+1)*(len(sectors)-1)],
149
+ [go[1][country_id]]))
150
+ #Extract and aggregate national gross output and final demand
151
+ #Incorporate estimations in 'computed' file,
152
+ #track changes (previously known/aggregated/unknown)
153
+ #in the 'ratio' file
154
+ data,ratio[country_id,entity_id] = \
155
+ aggregate(data,extract_i,goi,ratio[country_id,entity_id],
156
+ crf_parents,crf_childs,mapping)
157
+ computed[country_id,entity_id],ratio[country_id,entity_id] = \
158
+ down_adjustment(data,ratio[country_id,entity_id],
159
+ crf_parents,crf_childs)
160
+ check_consistency(computed,crf,ratio,crf_parents,crf_childs)
161
+ log.info('First emission tree estimated from Primap crf')
162
+
163
+ #Second step: integrate Primp-hist data
164
+ hist = load_primap(primap_path, hist_version, hist_name, emissions_year,
165
+ countries, cat_crf, entities, mode='hist')
166
+ hist = order_primap(hist,countries,cat_crf,entities,mode='hist')
167
+ computed[hist!=0] = hist[hist!=0]
168
+ ratio[hist!=0] = 1
169
+ for country_id in range(len(countries)):
170
+ for entity_id in range(len(entities)):
171
+ data = np.copy(hist[country_id,entity_id])
172
+ #Set total emissions to 0 for countries not covered in primap_hist
173
+ computed[country_id,entity_id,0] = hist[country_id,entity_id,0]
174
+ computed[country_id,entity_id],ratio[country_id,entity_id] =\
175
+ down_adjustment(computed[country_id,entity_id],
176
+ ratio[country_id,entity_id],
177
+ crf_parents,crf_childs)
178
+ check_consistency(computed,hist,ratio,crf_parents,crf_childs)
179
+ log.info('Emission tree adjusted to Primap hist')
180
+
181
+ if kyoto_basket:
182
+ log.info("Estimating Kyoto GHG basket")
183
+ computed,ratio,entities = kyoto_allocation(computed,ratio,entities)
184
+ intensity = np.insert(intensity, -1, np.zeros(np.shape(intensity[0])),
185
+ axis=0)
186
+ sources_intensity = np.insert(sources_intensity, -1,
187
+ np.zeros(np.shape(sources_intensity[0])),
188
+ axis=0)
189
+
190
+ if categories_output == "all":
191
+ cat_output = [i for i in range(len(cat_crf))]
192
+ else:
193
+ cat_output = [cat_crf.index(i) for i in categories_output]
194
+ labels_cat = [cat_crf[cat] for cat in cat_output]
195
+ qt,qy = to_mrio(computed,len(countries),crf_parents,crf_childs,
196
+ mapping,entities,go,len(sectors)-1,cat_output)
197
+ intensity = pd.DataFrame(data=np.transpose(intensity),
198
+ columns=entities,index=cat_crf)
199
+ sources_intensity = pd.DataFrame(data = np.transpose(sources_intensity),
200
+ columns=entities,index=cat_crf)
201
+
202
+ mrio.add_dimensions({
203
+ "GHG" : entities,
204
+ "Emission categories" : labels_cat,
205
+ })
206
+ mrio.parts["qy"] = mrio.new_part(
207
+ name="qy", data=qy,
208
+ dimensions = [["GHG","Emission categories"],[countries_name]]
209
+ )
210
+ mrio.parts["qt"] = mrio.new_part(
211
+ name="qt", data=qt,
212
+ dimensions = [["GHG","Emission categories"],[countries_name,sectors_name]]
213
+ )
214
+
215
+ #LOADERS
216
+
217
+ def get_go(mrio,
218
+ countries,
219
+ sectors,
220
+ final_demand,
221
+ inter_industry):
222
+ """
223
+ Extracts the gross output and total final demand per sector
224
+ from an MRIO table as well as the countries and sectors lists.
225
+
226
+ Parameters
227
+ ----------
228
+ mrio : mrio instance
229
+ countries, sectors, final_demand, inter_industry : str
230
+ Aliases for the MRIO table elements
231
+ Default: countries, sectors, y, t
232
+
233
+ Returns
234
+ -------
235
+ list of list of floats
236
+ First list holds gross output per sector.
237
+ Second list holds final demand per sector (domestic + foreign).
238
+ """
239
+ countries = mrio.labels[countries].copy()
240
+ sectors = mrio.labels[sectors].copy()
241
+ sectors.append("Final demand") #For allocating emission from final use
242
+ y = mrio.parts[final_demand]
243
+ t = mrio.parts[inter_industry]
244
+ fd_sum = y.sum(axis=0)
245
+ x = t.sum(axis=1) + y.sum(axis=1)
246
+ x.data[x.data==0] = 1 #Avoid division by 0
247
+ return [x.data,fd_sum.data],countries,sectors
248
+
249
+ ## Mapping loader
250
+
251
+ def load_mapping(path,name,sectors, extension,
252
+ exclude_LULUCF = True):
253
+ """
254
+ Load the mapping between Primap emissions categories and MRIO sectors.
255
+ The variable 'closed_branch' tracks if subcategories should be explored.
256
+ It is set to false once an end-category is defined, until the algorithm
257
+ explores a new branch.
258
+
259
+ Parameters
260
+ ----------
261
+ path : path-like
262
+ Path to the folder storing the mapping file.
263
+ name : str
264
+ Name of the mapping description file.
265
+ sectors : list of str
266
+ List of economic sectors.
267
+ extension : str
268
+ Extension under which the mapping table is saved.
269
+ Currently supported: excel (.xslx) and csv (.csv)
270
+ exclude_LULUCF : Boolean, optional
271
+ Whether to exclude Land use related emissions. The default is True.
272
+ The grand total per country is set to 'M.0.EL' instead of '0'.
273
+
274
+ Returns
275
+ -------
276
+ cat : list of strings
277
+ Label of IPCC categories.
278
+ mapping : list of ints
279
+ Index of corresponding EORA sector.
280
+ parents : list of ints
281
+ List of index of parent categories.
282
+ childs : list of list of ints
283
+ Lists of indexes of children categories.
284
+ Index of list of children refers to index in parents' list.
285
+
286
+ """
287
+ if extension == ".xslx":
288
+ source = pd.read_excel(os.path.join(path,
289
+ '{}.xlsx'.format(name))).to_numpy(dtype=str)
290
+ elif extension == ".csv":
291
+ source = pd.read_csv(os.path.join(path,
292
+ '{}.csv'.format(name))
293
+ ).to_numpy(dtype=str)
294
+ else:
295
+ raise ValueError(f"Unsupported mapping extension: {extension}")
296
+
297
+ #List all end-categories
298
+ end_categories,id_end = list(),list()
299
+ for i,row in enumerate(source):
300
+ if row[2]!="nan":
301
+ end_categories.append(row[0])
302
+ id_end.append(i)
303
+
304
+ categories = ["M.0.EL"]
305
+ parents = [0]
306
+ children = [[]]
307
+ mapping = [[]]
308
+
309
+ #Select the categories to include in the tree
310
+ for category in end_categories:
311
+ split = category.split('.')
312
+ current = str()
313
+ for part in split:
314
+ if len(current) == 0:
315
+ current = part
316
+ else:
317
+ current = ".".join([current,part])
318
+ if current not in categories:
319
+ categories.append(current)
320
+
321
+ #Create the tree
322
+ for i,category in enumerate(categories):
323
+ if category == "M.0.EL":
324
+ #Split the total emission category
325
+ continue
326
+ if len(category) == 1:
327
+ #If the category is a direct child of M.0.EL
328
+ children[0].append(i)
329
+ else:
330
+ #If the category is a child of another category
331
+ split = category.split('.')
332
+ parent = ".".join(split[:-1])
333
+ id_parent = categories.index(parent)
334
+ if id_parent not in parents:
335
+ children.append([])
336
+ parents.append(id_parent)
337
+ children[parents.index(id_parent)].append(i)
338
+ tomap = []
339
+ if category in end_categories:
340
+ row = source[id_end[end_categories.index(category)]]
341
+ j=2
342
+ while j<len(row) and row[j]!='nan':
343
+ tomap.append(sectors.index(str(row[j].strip())))
344
+ j+=1
345
+ mapping.append(tomap)
346
+
347
+ if exclude_LULUCF:
348
+ categories[categories.index('3')] = 'M.AG'
349
+
350
+ return categories, mapping, parents, children
351
+
352
+ ## Primap loader
353
+
354
+ def load_primap(path,version,name,year,countries_list,cat_crf,entities,
355
+ mode='crf'):
356
+ """
357
+ Extract data from a PRIMAP database for a given year and given GHGs.
358
+
359
+ Parameters
360
+ ----------
361
+ path : path-like
362
+ Path to the PRIMAP data.
363
+ version : str
364
+ Version of the PRIMAP data.
365
+ name : str
366
+ Name of the PRIMAP file
367
+ year : int
368
+ Year data to consider.
369
+ countries : list of strings
370
+ List of A3 country codes for countries covered in the MRIOT.
371
+ cat_crf : list of strings
372
+ Name of emission categories to extract.
373
+ entities : list of strings
374
+ GHGs to extract.
375
+ mode : 'crf' or 'hist'
376
+ Type of database to load.
377
+ Selection criterions are different depending on the table to laod.
378
+
379
+ Returns
380
+ -------
381
+ list of arrays
382
+ First array holds labels (country,category,GHG).
383
+ Second array holds values.
384
+ """
385
+ file = io.open(os.path.join(path,'{}_{}'.format(mode,version),
386
+ '{}.csv'.format(name)),
387
+ "r",encoding = 'utf-8-sig')
388
+ reader = csv.reader(file, delimiter = ',')
389
+ labels = []
390
+ values = []
391
+ countries = countries_list.copy()
392
+ if "ROW" in countries and mode == 'hist':
393
+ #If extracts from hist, includes world emissions to deduce ROW
394
+ countries.append('EARTH')
395
+ for row in reader:
396
+ if mode == 'crf':
397
+ if (row[3] in countries and row[6] in cat_crf and
398
+ row[4] in entities and row[year-1979] != ''):
399
+ labels.append([row[3],row[6],row[4]])
400
+ values.append(row[year-1979])
401
+ else:
402
+ if (row[1] == 'HISTTP' and row[2] in countries and
403
+ row[5] in cat_crf and row[3] in entities and
404
+ row[year-1744]!=''):
405
+ labels.append([row[2],row[5],row[3]])
406
+ values.append(row[year-1744])
407
+ return [np.array(labels,dtype=str),np.array(values,dtype=float)]
408
+
409
+ def order_primap(data, countries_list,cat_crf,entities, mode ='crf'):
410
+ """
411
+ Order data from PRIMAP into a 3D table (GHG x country x category).
412
+ Uncovered categories/countries are set to 0.
413
+ If a rest of the world region is defined, its emissions are set
414
+ as the difference between the world emissions and the emissions
415
+ assigned to other countries.
416
+
417
+ Parameters
418
+ ----------
419
+ data : list of lists
420
+ PRIMAP-crf base data (as returned by load_crf)
421
+ countries : list of strings
422
+ List of country A3 codes from the MRIOT.
423
+ cat_crf : list of strings
424
+ List of emission categories.
425
+ entities : list of strings
426
+ GHG covered.
427
+ mode : 'crf' or 'hist'
428
+ Type of database to format.
429
+ If hist, row emissions are deducted from world emissions.
430
+
431
+ Returns
432
+ -------
433
+ output : 3D array
434
+ Table of sourced emissions per GHG, country and emission category.
435
+ """
436
+ countries = countries_list.copy()
437
+ if 'ROW' in countries and mode =='hist':
438
+ countries.append('EARTH')
439
+ output = np.zeros([len(countries),len(entities),len(cat_crf)])
440
+ for i in range(len(data[0])):
441
+ country = countries.index(data[0][i][0])
442
+ entity = entities.index(data[0][i][2])
443
+ category = cat_crf.index(data[0][i][1])
444
+ output[country,entity,category] = data[1][i]
445
+
446
+ if 'ROW' in countries and mode=='hist':
447
+ #Deduce emissions from the rest of the world
448
+ earth = output[-1,:,:]
449
+ output = np.delete(output,-1,0)
450
+ row = countries.index('ROW')
451
+ world = np.sum(output,axis=0)
452
+ for j in range(len(entities)):
453
+ for k in range(len(cat_crf)):
454
+ #Round world emissions to 3 significant digits
455
+ #To avoid rounding errors
456
+ world[j,k] = float('%.3g' %world[j,k])
457
+ residual = earth - world
458
+ neg_row = np.where(residual<0)
459
+ for i in range(len(neg_row[0])):
460
+ log.info("Negative estimation for row {} emissions in IPCC {}".format(
461
+ entities[neg_row[0][i]],cat_crf[neg_row[1][i]]))
462
+ log.info("Found {} cumulated emissions instead of {}".format(
463
+ world[neg_row[0][i],neg_row[1][i]],
464
+ earth[neg_row[0][i],neg_row[1][i]]))
465
+ log.info("Overwrite row emissions to 0.")
466
+ residual[neg_row[0][i],neg_row[1][i]] = 0
467
+ output[row,:,:] = residual
468
+ #assert np.sum(output<0) == 0
469
+ return output
470
+
471
+ # Operations
472
+
473
+ def intensity_factor(crf,concordance,cat_crf,countries_list,go,entities,
474
+ nsectors,min_sources=10):
475
+ """
476
+ Computes average emissions intensity coefficient for end-categories.
477
+ End-categories are defined are emission categories associated with
478
+ one or several economic sectors.
479
+ Emission intensity coefficients are computed using PRIMAP-crf emission data
480
+ and WIOD economic output values.
481
+
482
+ Parameters
483
+ ----------
484
+ crf : list of arrays
485
+ Data from PRIMAP-crf, as extracted from load_crf.
486
+ concordance : list of list of ints
487
+ Mapping table from emission categories to economic sectors.
488
+ cat_crf : list of strings
489
+ List of IPCC emission categories labels.
490
+ countries : list of string
491
+ List of A3 country codes for countries covered in WIOD.
492
+ go : list of float
493
+ Economic output per sector.
494
+ min_sources : int, optional
495
+ Minimal number of sources to create an intensity coefficient.
496
+ The default is 0.
497
+
498
+ Returns
499
+ -------
500
+ intensity : 2D matrix
501
+ Emission intensity coefficient for each GHG and each category.
502
+ sources : 2D matrix
503
+ Number of countries used for determining the emission intensity coeff.
504
+
505
+ """
506
+ countries = countries_list.copy()
507
+ intensity = np.zeros([len(entities),len(cat_crf)])
508
+ sources = np.zeros([len(entities),len(cat_crf)])
509
+ for rowid,entity in enumerate(entities):
510
+ for colid, category in enumerate(cat_crf):
511
+ countries_name = []
512
+ emissions = 0
513
+ source = 0
514
+ if concordance[colid] != []:
515
+ #If the category is associated with some economic sectors
516
+ indices = [(label[1] == category and label[2] == entity)
517
+ for label in crf[0]]
518
+ #List all countries for which data is reported
519
+ source = np.sum(indices)
520
+ emissions = np.sum(crf[1][indices])
521
+ countries_name = crf[0][indices][:,0]
522
+ sources[rowid,colid] = source
523
+ if source>min_sources:
524
+ #If enough sources are available,
525
+ #computes the average factor of the category
526
+ index_countries = []
527
+ production = 0
528
+ for country in countries_name:
529
+ index_countries.append(countries.index(country))
530
+ for sector in concordance[colid]:
531
+ if sector == nsectors:
532
+ production += \
533
+ np.sum([go[1][i] for i in index_countries])
534
+ else:
535
+ production += np.sum(
536
+ [go[0][i*nsectors + sector]
537
+ for i in index_countries])
538
+ intensity[rowid,colid] = emissions/production
539
+ return intensity,sources
540
+
541
+ def aggregate(data,intensity,go,known,crf_parents,crf_childs,concordance):
542
+ """
543
+ Bottom up consolidation of the emission tree.
544
+ Successively estimates emissions in the emission tree,
545
+ starting from end-categories up to total emissions.
546
+ Uses emission intensity coeffs when no emission data are reported.
547
+
548
+ Parameters
549
+ ----------
550
+ data : 1D array
551
+ IPCC emission data from PRIMAP-crf for a country/GHG.
552
+ intensity : 1D array
553
+ Intensity coefficient array for the corresponding GHG.
554
+ go : 1D array
555
+ Economic output per sector.
556
+ known : 1D array
557
+ Source status associated with source data.
558
+ - 0 is unknown
559
+ - 1 is known from PRIMAP-crf
560
+ - 2 is estimated
561
+
562
+ crf_parents : list of ints
563
+ Indices of parent categories.
564
+ crf_childs : list of list of ints
565
+ List of children categories per parent.
566
+ concordance : list of ints
567
+ Mapping of emission categories with economic sectors.
568
+
569
+ Returns
570
+ -------
571
+ data : 1D array
572
+ aggregated emission array.
573
+ known : 1D array
574
+ Updated information on data source.
575
+ """
576
+ aggregation = np.copy(data)
577
+ for i in range(len(crf_parents),0,-1):
578
+ #Identify parents and children categories
579
+ parent = crf_parents[i-1]
580
+ childs_id = crf_childs[i-1]
581
+ for children in childs_id:
582
+ if known[children] == 0 and concordance[children] != []:
583
+ #If a children is null and is an end-category,
584
+ #estimate is computed using the intensity factor
585
+ for sector in concordance[children]:
586
+ aggregation[children] += \
587
+ intensity[children]*go[sector]
588
+ known[children] = 2 #Children is marked as aggregated
589
+ if known[parent] == 0:
590
+ #If the parent is unknown,
591
+ #child estimation are aggregated to form a parent estimation
592
+ aggregation[parent] = np.sum([aggregation[child] for child in childs_id])
593
+ known[parent] = 2
594
+ return aggregation,known
595
+
596
+ def down_adjustment(data,known,crf_parents,crf_childs):
597
+ """
598
+ Top-down adjustment of the emission tree.
599
+ Ensures consistence between aggregated data and sub-categories.
600
+ Adjusts estimated coefficients.
601
+ If unconsistences in sourced data, known sub-categories are adjusted
602
+ and estimated sub-categories are set to 0.
603
+
604
+ Parameters
605
+ ----------
606
+ data : 1D numpy array
607
+ Emissions values estimated for the emission categories
608
+ of the corresponding gas and country.
609
+ known : 1D numpy array
610
+ State of knowledge of the available information:
611
+ - 0 is no information
612
+ - 1 is original information
613
+ - 2 is estimated value.
614
+
615
+ crf_parents : list of ints
616
+ list of aggregated emission categories.
617
+ crf_childs : list of list of ints
618
+ List of list of sub-categories associated with the parent categories.
619
+
620
+ Returns
621
+ -------
622
+ data : 1D numpy array
623
+ Updated emission values.
624
+ known : 1D numpy array
625
+ Updated information on the estimations:
626
+ 2 = source data
627
+ x (float) = adjustment ratio
628
+ -1 = has been set to 0 because children exceeded parents
629
+
630
+ """
631
+ for i in range(len(crf_parents)):
632
+ parent = crf_parents[i]
633
+ childs = crf_childs[i]
634
+ current_multiplier = known[parent]
635
+ #When splitting data between children, known children left untouched
636
+ sure = 0
637
+ unsure = 0
638
+ tobalance = []
639
+ for child in childs:
640
+ if known[child] == 1:
641
+ sure += data[child] #Sums up the values of the sourced childs
642
+ else:
643
+ tobalance.append(child)
644
+ unsure += data[child]
645
+ #Sums up the values of the estimated childs
646
+ tomake = data[parent] - sure
647
+
648
+ #Computes the emissions to allocate between the estimated childs
649
+ for kid in tobalance:
650
+ if unsure > 0 and tomake > 0:
651
+ #If residual is positive,
652
+ #proportionally allocate extra emissions to estimated children
653
+ data[kid] = data[kid]*tomake/unsure
654
+ known[kid] = current_multiplier*tomake/unsure
655
+ #Support array marks the adjustment coefficient
656
+ if tomake <= 0:
657
+ #If residual is negative, estimated emissions are assumed null
658
+ data[kid] = 0
659
+ #Support array marks that extrapolation is not possible
660
+ #because all emissions are already allocated
661
+ known[kid] = 0
662
+ children = [data[children] for children in childs]
663
+ made = np.sum(children)
664
+ if made != data[parent]:
665
+ #If total of children is still different to parent,
666
+ #sourced childs are proportionally adjusted
667
+ for children in childs:
668
+ if made == 0:
669
+ #If parent is reported but all children are null
670
+ #Emissions are allocated proportionally to childs
671
+ data[children] = data[parent]/len(childs)
672
+ known[children] = 2
673
+ else:
674
+ data[children] = data[children]*data[parent]/made
675
+ known[children] = current_multiplier*data[parent]/made
676
+ #Support array registers the adjustment coefficient
677
+ children = [data[children] for children in childs]
678
+ assert np.isclose(np.sum(children),data[parent])
679
+ return data, known
680
+
681
+ def kyoto_allocation(data,ratio,entities):
682
+ """
683
+ Computes the total GHG emissions using the GWP defined in preambles.
684
+ Replaces the Kyoto GHG estimated from the PRIMAP Kyoto estimates.
685
+
686
+ Parameters
687
+ ----------
688
+ data : 3D array
689
+ Table of sourced emissions per GHG, country and emission category.
690
+ ratio : 3D array
691
+ Table of adjustment coefficents per GHG, country and emission category.
692
+ entities : list of str
693
+ List of GHG allocated.
694
+
695
+ Returns
696
+ -------
697
+ data : 3D array
698
+ Table of sourced emissions per emission category GHG and country,
699
+ with estimated Kyoto GHG basket.
700
+ ratio : 3D array
701
+ Table of adjustment coefficents per GHG, country and emission category.
702
+ All coefficients for the Kyoto basket are set to 2 (estimated).
703
+
704
+ """
705
+
706
+ kyoto = np.zeros((len(data),1,len(data[0,0])))
707
+ kratio = np.zeros((len(data),1,len(data[0,0])))
708
+ for i,entity in enumerate(entities):
709
+ if entity in gwp:
710
+ kyoto[:,0,:] += data[:,i,:]*gwp[entity]
711
+ kratio[:,:,:] = 2
712
+ data = np.concatenate((data, kyoto), axis=1)
713
+ kratio = np.concatenate((ratio, kratio),axis=1)
714
+ entities.append("Kyoto GHG AR4")
715
+ return data,ratio,entities
716
+
717
+
718
+ # Annex functions
719
+
720
+ def check_consistency(test,source,known,crf_parents,crf_childs):
721
+ """
722
+ Test the consistency of the emission tree.
723
+ Ensure that the sum of each children category equals its parent.
724
+
725
+ Parameters
726
+ ----------
727
+ test : 3D array
728
+ Emission table to assess.
729
+ primap :
730
+ crf_parents : list of ints
731
+ list of aggregated emission categories.
732
+ crf_childs : list of list of ints
733
+ List of list of sub-categories associated with the parent categories.
734
+
735
+ Returns
736
+ -------
737
+ None. Asserts that sum of children equals parent for every pair.
738
+ """
739
+ for i in range(len(test)):
740
+ for j in range(len(test[0])):
741
+ for k in range(len(crf_parents)):
742
+ parent = test[i,j,crf_parents[k]]
743
+ children = [test[i,j,child] for child in crf_childs[k]]
744
+ children = np.sum(children)
745
+ assert np.isclose(parent,children)
746
+
747
+ def to_mrio(data,countries,crf_parents,crf_childs,mapping,entities,go,
748
+ sectors=56,categories=[0]):
749
+ """
750
+ Maps emission tables into MRIO economic sectors.
751
+ If an emission category is mapped to multiple economic sectors,
752
+ emissions are allocated proportionally to the output of each sector.
753
+
754
+ Parameters
755
+ ----------
756
+ data : 3D array
757
+ Emission table.
758
+ countries : int
759
+ Number of countries covered.
760
+ crf_parents : list of ints
761
+ list of aggregated emission categories.
762
+ crf_childs : list of list of ints
763
+ List of list of sub-categories associated with the parent categories.
764
+ mapping : list of ints
765
+ Concordance between WIOD sectors and emission categories.
766
+ entities : list of strings
767
+ List of GHGs covered.
768
+ go : list of float
769
+ Economic output per sector.
770
+ sectors : int, optional
771
+ Economic sectors per country The default is 26.
772
+ categories : list of ints, optional
773
+ Emission categories to report. The default is [0].
774
+
775
+ Returns
776
+ -------
777
+ qt : 2D array
778
+ Emission matrix for intermediate sectors.
779
+ qy : 2D array
780
+ Emission matrix for final demand.
781
+
782
+ """
783
+ n = len(categories)
784
+ e = len(entities)
785
+ qt = np.zeros((e*n,countries*sectors))
786
+ qy = np.zeros((e*n,countries))
787
+
788
+
789
+ selection = [look_for_children(i,crf_parents,crf_childs,[])
790
+ for i in categories]
791
+ for entity in range(e):
792
+ for country in range(countries):
793
+ for i,cat in enumerate(selection):
794
+ count = 0
795
+ ref = data[country,entity,categories[i]]
796
+ for item in cat:
797
+ count += data[country,entity,item]
798
+ subref = data[country,entity,item]
799
+ subcount = 0
800
+ production = 0
801
+ #Allocate emissions to sectors associated to end categories
802
+ for sector in mapping[item]:
803
+ #Aggregate production from associated sectors
804
+ if sector != sectors:
805
+ production += go[0][country*sectors+sector]
806
+ elif sector == sectors:
807
+ production += go[1][country]
808
+ for sector in mapping[item]:
809
+ #Allocate emissions proportionally to the sector size
810
+ if sector != sectors and production !=0:
811
+ subcount += data[country,entity,item]*\
812
+ go[0][country*sectors+sector]/production
813
+ qt[entity*n+i,country*sectors+sector] += \
814
+ data[country,entity,item]*\
815
+ go[0][country*sectors+sector]/production
816
+ elif sector == sectors and production !=0:
817
+ subcount += data[country,entity,item]*\
818
+ go[1][country]/production
819
+ qy[entity*n+i,country] += \
820
+ data[country,entity,item]*\
821
+ go[1][country]/production
822
+ if production == 0 and data[country,entity,item]!=0:
823
+ #If the sector is null in the MRIO, emissions
824
+ #are allocated to the final demand
825
+ qy[entity*n+i,country] += data[country,entity,item]
826
+ subcount += data[country,entity,item]
827
+ log.info("Empty sector at index {} in country {}".format(
828
+ sector,country))
829
+ log.info("Allocating {} emissions to the final demand."\
830
+ .format(data[country,entity,item]))
831
+ assert np.isclose(subcount,subref)
832
+ assert np.isclose(count,ref)
833
+ #Check whether emissions reported match aggregated values
834
+ return qt,qy
835
+
836
+ def look_for_children(category,parents,childs,output=[]):
837
+ '''
838
+ Recursive function to find all end-categories to include
839
+ in the reporting of selected category.
840
+
841
+ Parameters
842
+ ----------
843
+ category : int
844
+ Emission category under investigation
845
+ parents : list of ints
846
+ list of aggregated emission categories.
847
+ childs : list of list of ints
848
+ List of list of sub-categories associated with the parent categories.
849
+ output : list of ints
850
+ Aggregated list of categories covered by the inital category called.
851
+
852
+ Returns
853
+ -------
854
+ output: list of ints
855
+ List of end-categories covered by the initial category called.
856
+
857
+ '''
858
+ if category not in parents:
859
+ output.append(category)
860
+ else:
861
+ for child in childs[parents.index(category)]:
862
+ output=look_for_children(child,parents,childs,output)
863
+ return output