test_conversion.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. import climate_categories as cc
  2. import primap2 as pm2
  3. from src.faostat_data_primap.helper.paths import downloaded_data_path
  4. from src.faostat_data_primap.read import read_data
  5. def test_read(tmp_path):
  6. domains_and_releases_to_read = [
  7. # ("farm_gate_agriculture_energy", "2024-11-14"),
  8. # ("farm_gate_emissions_crops", "2024-11-14"),
  9. # ("farm_gate_livestock", "2024-11-14"),
  10. # ("land_use_drained_organic_soils", "2024-11-14"),
  11. # ("land_use_fires", "2024-11-14"),
  12. # ("land_use_forests", "2024-11-14"),
  13. ("pre_post_agricultural_production", "2024-11-14"),
  14. ]
  15. read_data(
  16. domains_and_releases_to_read=domains_and_releases_to_read,
  17. read_path=downloaded_data_path,
  18. save_path=tmp_path,
  19. )
  20. def test_yaml_to_python():
  21. cat = cc.from_yaml("FAO.yaml")
  22. cat.to_python("FAO.py")
  23. def test_python_to_yaml():
  24. from FAO import spec
  25. cat = cc.from_spec(spec)
  26. assert cat
  27. def test_make_dict_comprehension_for_faster_typing(): # noqa: PLR0912 PLR0915
  28. spec = {
  29. "name": "FAO",
  30. "title": (
  31. "Food and Agriculture Organization of the United Nations (FAO) "
  32. "FAOSTAT data set categorisation"
  33. ),
  34. "comment": "Needed to add FAOSTAT data to PRIMAP-hist",
  35. "references": "",
  36. "institution": "FAO",
  37. "hierarchical": True,
  38. "last_update": "2024-12-10",
  39. "version": "2024",
  40. "total_sum": True,
  41. "canonical_top_level_category": "0",
  42. }
  43. categories = {}
  44. # 0. main categories
  45. categories["0"] = {
  46. "title": "Total",
  47. "comment": "All emissions and removals",
  48. "children": [["1", "2", "3", "4", "5", "6", "7"]],
  49. }
  50. children_1 = ["1.A", "1.B"]
  51. children_2 = ["2.A", "2.B", "2.C", "2.D", "2.E"]
  52. children_3 = [f"3.{i}" for i in "ABCDEFGHIJKLMNOPQR"]
  53. # children_4 = ["4.A"]
  54. # children_5 = ["5.A", "5.B"]
  55. # children_6 = ["6.A", "6.B", "6.C"]
  56. # children_7 = [f"3.{i}" for i in "ABCDEFGHIJKLM"]
  57. main_categories = (
  58. # category code, name and comment, gases, children
  59. ("1", "Crops", ["CH4", "N2O"], children_1),
  60. (
  61. "2",
  62. "Energy use in agriculture",
  63. ["CH4", "N2O", "CO2"],
  64. children_2,
  65. ),
  66. ("3", "Livestock", ["CH4", "N2O"], children_3),
  67. # ("4", "Forest", ["CO2"], children_4),
  68. # (
  69. # "5",
  70. # "Drained organic soils",
  71. # ["N2O", "CO2"],
  72. # children_5,
  73. # ),
  74. # ("6", "Fires", ["CH4", "N2O", "CO2"], children_6),
  75. # (
  76. # "7",
  77. # "Pre and post agricultural production",
  78. # ["CH4", "N2O", "CO2"],
  79. # children_7,
  80. # ),
  81. )
  82. for code, name, gases, children in main_categories:
  83. categories[code] = {
  84. "title": name,
  85. "comment": name,
  86. # "alternative_codes": code.replace(".", ""),
  87. "children": [children],
  88. "info": {"gases": gases},
  89. }
  90. # 1. crops
  91. # all crops category
  92. code_all_crops = "1.A"
  93. codes_crops = [f"1.A.{i}" for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]]
  94. categories[code_all_crops] = {
  95. "title": "All crops",
  96. "comment": "All crops",
  97. # "alternative_codes": code_all_crops.replace(".", ""),
  98. "children": [codes_crops],
  99. "info": {"gases": ["CH4", "N2O"]},
  100. }
  101. crops = [
  102. "Wheat",
  103. "Rice",
  104. "Potatoes",
  105. "Millet",
  106. "Barley",
  107. "Maize (corn)",
  108. "Sugar cane",
  109. "Beans, dry",
  110. "Oats",
  111. "Rye",
  112. "Sorghum",
  113. "Soya beans",
  114. ]
  115. crop_burnings = [
  116. True,
  117. True,
  118. False,
  119. False,
  120. False,
  121. True,
  122. True,
  123. False,
  124. False,
  125. False,
  126. False,
  127. False,
  128. ]
  129. rice_cultivations = [
  130. False,
  131. True,
  132. False,
  133. False,
  134. False,
  135. False,
  136. False,
  137. False,
  138. False,
  139. False,
  140. False,
  141. False,
  142. ]
  143. for crop, code, crop_burning, rice_cultivation in zip(
  144. crops, codes_crops, crop_burnings, rice_cultivations
  145. ):
  146. # all crops have at least N2O emissions
  147. gases_main = "N2O"
  148. if crop_burning or rice_cultivation:
  149. gases_main = ["CH4", "N2O"]
  150. # all crops have at least crop residues as child
  151. children_main = [f"{code}.a"]
  152. if crop_burning:
  153. children_main.append(f"{code}.b")
  154. if rice_cultivation:
  155. children_main.append(f"{code}.c")
  156. categories[f"{code}"] = {
  157. "title": f"{crop}",
  158. "comment": f"{crop}",
  159. # "alternative_codes": [f"{code}".replace(".", "")],
  160. "info": {"gases": gases_main},
  161. "children": [children_main],
  162. }
  163. # crop residues (every crop has it)
  164. categories[f"{code}.a.i"] = {
  165. "title": f"{crop} crop residues direct emissions",
  166. "comment": f"{crop} crop residues direct emissions",
  167. # "alternative_codes": [f"{code}.a".replace(".", "")],
  168. "info": {"gases": ["N2O"]},
  169. }
  170. categories[f"{code}.a.ii"] = {
  171. "title": f"{crop} crop residues indirect emissions",
  172. "comment": f"{crop} crop residues indirect emissions",
  173. # "alternative_codes": [f"{code}.a.i".replace(".", "")],
  174. "info": {"gases": ["N2O"]},
  175. }
  176. categories[f"{code}.a"] = {
  177. "title": f"{crop} crop residues",
  178. "comment": f"{crop} crop residues",
  179. # "alternative_codes": [f"{code}.a".replace(".", "")],
  180. "info": {"gases": ["N2O"]},
  181. "children": [[f"{code}.a.ii", f"{code}.a.i"]],
  182. }
  183. if crop_burning:
  184. categories[f"{code}.b"] = {
  185. "title": f"{crop} burning crop residues",
  186. "comment": f"{crop} burning crop residues",
  187. # "alternative_codes": [f"{code}.b".replace(".", "")],
  188. "info": {"gases": ["CH4", "N2O"]},
  189. }
  190. if rice_cultivation:
  191. categories[f"{code}.c"] = {
  192. "title": "Rice cultivation",
  193. "comment": "Rice cultivation",
  194. # "alternative_codes": [f"{code}.c".replace(".", "")],
  195. "info": {"gases": ["CH4"]},
  196. }
  197. # synthetic fertilisers
  198. codes_synthetic_fertilisers = ["1.B", "1.B.1", "1.B.2", "1.B.2.a", "1.B.2.b"]
  199. names = [
  200. "Synthetic fertilisers",
  201. "Direct emissions",
  202. "Indirect emissions",
  203. "Indirect emissions that volatilise",
  204. "Indirect emissions that leach",
  205. ]
  206. children_cats = [["1.B.1", "1.B.2"], None, ["1.B.2.a", "1.B.2.b"], None, None]
  207. for code, name, child_cat in zip(codes_synthetic_fertilisers, names, children_cats):
  208. categories[code] = {
  209. "title": name,
  210. "comment": name,
  211. # "alternative_codes": [code.replace(".", "")],
  212. "info": {"gases": ["N2O"]},
  213. }
  214. if child_cat:
  215. categories[code]["children"] = [child_cat]
  216. # 2. energy use
  217. names = [
  218. "Natural gas",
  219. "Electricity",
  220. "Coal",
  221. "Heat",
  222. "Petroleum",
  223. ]
  224. codes = children_2
  225. for name, code in zip(names, codes):
  226. categories[code] = {
  227. "title": name,
  228. "comment": name,
  229. # "alternative_codes": code.replace(".", ""),
  230. "info": {"gases": ["CH4", "N2O", "CO2"]},
  231. }
  232. # 3 livestock
  233. animals = [
  234. "Asses",
  235. "Camels",
  236. "Cattle, dairy",
  237. "Cattle, non-dairy",
  238. "Chickens, broilers",
  239. "Chickens, layers",
  240. "Goats",
  241. "Horses",
  242. "Mules and hinnies",
  243. "Sheep",
  244. "Llamas",
  245. "Chickens",
  246. "Poultry Birds",
  247. "Buffalo",
  248. "Ducks",
  249. "Swine, breeding",
  250. "Swine, market",
  251. "Turkeys",
  252. ]
  253. codes_animals = [f"3.{i}" for i in "ABCDEFGHIJKLMNOPQR"]
  254. enteric_fermentation = [
  255. "Asses",
  256. "Camels",
  257. "Cattle, dairy",
  258. "Cattle, non-dairy",
  259. "Goats",
  260. "Horses",
  261. "Sheep",
  262. "Mules and hinnies",
  263. "Buffalo",
  264. "Swine, breeding",
  265. "Swine, market",
  266. "Llamas",
  267. ]
  268. for animal, code in zip(animals, codes_animals):
  269. if animal in enteric_fermentation:
  270. gases = ["CH4", "N2O"]
  271. animal_children = [f"{code}.{i}" for i in "1234"]
  272. categories[f"{code}.4"] = {
  273. "title": f"{animal} enteric fermentation",
  274. "comment": f"{animal} enteric fermentation",
  275. # "alternative_codes" : code.replace(".", ""),
  276. "info": {"gases": gases},
  277. }
  278. else:
  279. gases = ["N2O"]
  280. animal_children = [f"{code}.{i}" for i in "123"]
  281. categories[code] = {
  282. "title": animal,
  283. "comment": animal,
  284. # "alternative_codes" : code.replace(".", ""),
  285. "info": {"gases": gases},
  286. "children": [animal_children],
  287. }
  288. # manure management branch
  289. manure_management_children = [f"{code}.1.{i}" for i in "abc"]
  290. categories[f"{code}.1"] = {
  291. "title": f"{animal} manure management",
  292. "comment": f"{animal} manure management",
  293. # "alternative_codes" : code.replace(".", ""),
  294. "info": {"gases": gases},
  295. "children": [manure_management_children],
  296. }
  297. categories[f"{code}.1.a"] = {
  298. "title": f"{animal} decomposition of organic matter",
  299. "comment": f"{animal} decomposition of organic matter",
  300. # "alternative_codes" : code.replace(".", ""),
  301. "info": {"gases": "CH4"},
  302. }
  303. categories[f"{code}.1.b"] = {
  304. "title": f"{animal} manure management (Direct emissions N2O)",
  305. "comment": f"{animal} manure management (Direct emissions N2O)",
  306. # "alternative_codes" : code.replace(".", ""),
  307. "info": {"gases": "N2O"},
  308. }
  309. categories[f"{code}.1.c"] = {
  310. "title": f"{animal} manure management (Indirect emissions N2O)",
  311. "comment": f"{animal} manure management (Indirect emissions N2O)",
  312. # "alternative_codes" : code.replace(".", ""),
  313. "info": {"gases": "N2O"},
  314. }
  315. # manure left on pasture branch
  316. manure_left_on_pasture_children = [f"{code}.2.{i}" for i in "ab"]
  317. categories[f"{code}.2"] = {
  318. "title": f"{animal} manure left on pasture",
  319. "comment": f"{animal} manure left on pasture",
  320. # "alternative_codes" : code.replace(".", ""),
  321. "info": {"gases": "N2O"},
  322. "children": [manure_left_on_pasture_children],
  323. }
  324. categories[f"{code}.2.a"] = {
  325. "title": f"{animal} manure left on pasture (direct emissions N2O)",
  326. "comment": f"{animal} manure left on pasture (direct emissions N2O)",
  327. # "alternative_codes" : code.replace(".", ""),
  328. "info": {"gases": "N2O"},
  329. }
  330. categories[f"{code}.2.b"] = {
  331. "title": f"{animal} manure left on pasture (indirect emissions N2O)",
  332. "comment": f"{animal} manure left on pasture (indirect emissions N2O)",
  333. # "alternative_codes" : code.replace(".", ""),
  334. "info": {"gases": "N2O"},
  335. "children": [[f"{code}.2.b.i", f"{code}.2.b.ii"]],
  336. }
  337. categories[f"{code}.2.b.i"] = {
  338. "title": (
  339. f"{animal} manure left on pasture "
  340. f"(indirect emissions, N2O that leaches)"
  341. ),
  342. "comment": (
  343. f"{animal} manure left on pasture (indirect "
  344. f"emissions, N2O that leaches)"
  345. ),
  346. # "alternative_codes" : code.replace(".", ""),
  347. "info": {"gases": "N2O"},
  348. }
  349. categories[f"{code}.2.b.ii"] = {
  350. "title": (
  351. f"{animal} manure left on pasture "
  352. f"(indirect emissions, N2O that volatilises)"
  353. ),
  354. "comment": (
  355. f"{animal} manure left on pasture (indirect "
  356. f"emissions, N2O that volatilises)"
  357. ),
  358. # "alternative_codes" : code.replace(".", ""),
  359. "info": {"gases": "N2O"},
  360. }
  361. # manure applied branch
  362. manure_applied_children = [f"{code}.3.{i}" for i in "ab"]
  363. categories[f"{code}.3"] = {
  364. "title": f"{animal} manure applied",
  365. "comment": f"{animal} manure applied",
  366. # "alternative_codes" : code.replace(".", ""),
  367. "info": {"gases": "N2O"},
  368. "children": [manure_applied_children],
  369. }
  370. categories[f"{code}.3.a"] = {
  371. "title": f"{animal} manure applied (direct emissions N2O)",
  372. "comment": f"{animal} manure applied (direct emissions N2O)",
  373. # "alternative_codes" : code.replace(".", ""),
  374. "info": {"gases": "N2O"},
  375. }
  376. categories[f"{code}.3.b"] = {
  377. "title": f"{animal} manure applied (indirect emissions N2O)",
  378. "comment": f"{animal} manure applied (indirect emissions N2O)",
  379. # "alternative_codes" : code.replace(".", ""),
  380. "info": {"gases": "N2O"},
  381. "children": [[f"{code}.3.b.i", f"{code}.3.b.ii"]],
  382. }
  383. categories[f"{code}.3.b.i"] = {
  384. "title": (
  385. f"{animal} manure applied " f"(indirect emissions, N2O that leaches)"
  386. ),
  387. "comment": (
  388. f"{animal} manure applied (indirect " f"emissions, N2O that leaches)"
  389. ),
  390. # "alternative_codes" : code.replace(".", ""),
  391. "info": {"gases": "N2O"},
  392. }
  393. categories[f"{code}.3.b.ii"] = {
  394. "title": (
  395. f"{animal} manure applied "
  396. f"(indirect emissions, N2O that volatilises)"
  397. ),
  398. "comment": (
  399. f"{animal} manure applied (indirect "
  400. f"emissions, N2O that volatilises)"
  401. ),
  402. # "alternative_codes" : code.replace(".", ""),
  403. "info": {"gases": "N2O"},
  404. }
  405. # forests
  406. categories["4"] = {
  407. "title": "Carbon stock change in forests",
  408. "comment": "Carbon stock change in forests",
  409. "info": {"gases": "CO2"},
  410. "children": [["4.A", "4.B"]],
  411. }
  412. categories["4.A"] = {
  413. "title": "Forest land",
  414. "comment": "Forest land",
  415. "info": {"gases": "CO2"},
  416. }
  417. categories["4.B"] = {
  418. "title": "Net Forest conversion",
  419. "comment": "Net Forest conversion",
  420. "info": {"gases": "CO2"},
  421. }
  422. # drained organic soils
  423. categories["5"] = {
  424. "title": "Drained organic soils",
  425. "comment": "Drained organic soils",
  426. "info": {"gases": "CO2"},
  427. "children": [["5.A", "5.B"]],
  428. }
  429. categories["5.A"] = {
  430. "title": "Drained grassland",
  431. "comment": "Drained grassland",
  432. "info": {"gases": ["CO2", "N2O"]},
  433. }
  434. categories["5.B"] = {
  435. "title": "Drained cropland",
  436. "comment": "Drained cropland",
  437. "info": {"gases": ["CO2", "N2O"]},
  438. }
  439. # 6 Fires
  440. # Forest fires
  441. forest_fires_children = ["Humid tropical forests", "Other forests"]
  442. forest_fires_children_codes = ["6.A.1", "6.A.2"]
  443. for cat_name, code in zip(forest_fires_children, forest_fires_children_codes):
  444. categories[code] = {
  445. "title": cat_name,
  446. "comment": cat_name,
  447. "info": {"gases": ["CO2", "N2O", "CH4"]},
  448. }
  449. categories["6.A"] = {
  450. "title": "Forest fires",
  451. "comment": "Forest fires",
  452. "info": {"gases": ["CO2", "N2O", "CH4"]},
  453. "children": [forest_fires_children_codes],
  454. }
  455. # Savanna fires
  456. savanna_fires_children = [
  457. "Closed shrubland",
  458. "Grassland",
  459. "Open shrubland",
  460. "Savanna",
  461. "Woody savanna",
  462. ]
  463. savanna_fires_children_codes = ["6.B.1", "6.B.2", "6.B.3", "6.B.4", "6.B.5"]
  464. for cat_name, code in zip(savanna_fires_children, savanna_fires_children_codes):
  465. categories[code] = {
  466. "title": cat_name,
  467. "comment": cat_name,
  468. "info": {"gases": ["CO2", "N2O", "CH4"]},
  469. }
  470. categories["6.B"] = {
  471. "title": "Savanna fires",
  472. "comment": "Savanna fires",
  473. "info": {"gases": ["CO2", "N2O", "CH4"]},
  474. "children": [savanna_fires_children_codes],
  475. }
  476. # fires in organic soils
  477. categories["6.C"] = {
  478. "title": "Fires in organic soils",
  479. "comment": "Fires in organic soils",
  480. "info": {"gases": ["CO2", "N2O", "CH4"]},
  481. }
  482. # 6 fires
  483. categories["6"] = {
  484. "title": "Fires",
  485. "comment": "Fires",
  486. "info": {"gases": ["CO2", "N2O", "CH4"]},
  487. "children": [["6.A", "6.B", "6.C"]],
  488. }
  489. # 7 pre and post production
  490. pre_post_production_categories = [
  491. "Fertilizers Manufacturing",
  492. "Food Transport",
  493. "Food Retail",
  494. "Food Household Consumption",
  495. "Solid Food Waste",
  496. "Domestic Wastewater",
  497. "Industrial Wastewater",
  498. "Incineration",
  499. "Pre- and Post- Production",
  500. "Energy Use (Pre- and Post-Production)",
  501. "Agrifood Systems Waste Disposal",
  502. "Cold Chain F-Gas",
  503. "Pesticides Manufacturing",
  504. "Food Processing",
  505. "Food Packaging",
  506. ]
  507. pre_post_production_categories_codes = ["7." + i for i in "ABCDEFGHIJKLMNO"]
  508. pre_post_production_categories_gases = [
  509. ["CO2", "N2O", "KYOTOGHG (AR5GWP100)"],
  510. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)", "FGASES (AR5GWP100)"],
  511. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)", "FGASES (AR5GWP100)"],
  512. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)", "FGASES (AR5GWP100)"],
  513. ["KYOTOGHG (AR5GWP100)", "CH4"],
  514. ["KYOTOGHG (AR5GWP100)", "CH4", "N2O"],
  515. ["KYOTOGHG (AR5GWP100)", "CH4", "N2O"],
  516. ["CO2", "KYOTOGHG (AR5GWP100)"], # incineration
  517. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)", "FGASES (AR5GWP100)"],
  518. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)"],
  519. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)"],
  520. ["FGASES (AR5GWP100)"],
  521. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)"],
  522. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)", "FGASES (AR5GWP100)"],
  523. ["CO2", "CH4", "N2O", "KYOTOGHG (AR5GWP100)"],
  524. ]
  525. for cat_name, code, gases in zip(
  526. pre_post_production_categories,
  527. pre_post_production_categories_codes,
  528. pre_post_production_categories_gases,
  529. ):
  530. categories[code] = {
  531. "title": cat_name,
  532. "comment": cat_name,
  533. "info": {"gases": gases},
  534. }
  535. categories["7"] = {
  536. "title": "Pre and post agricultural production",
  537. "comment": "Pre and post agricultural production",
  538. "info": {
  539. "gases": [
  540. "CO2",
  541. "CH4",
  542. "N2O",
  543. "KYOTOGHG (AR5GWP100)",
  544. "FGASES (AR5GWP100)",
  545. ],
  546. },
  547. "children": [pre_post_production_categories_codes],
  548. }
  549. spec["categories"] = categories
  550. fao_cats = cc.HierarchicalCategorization.from_spec(spec.copy())
  551. # run print(fao_cats.show_as_tree())
  552. fao_cats.to_python("FAO.py")
  553. fao_cats.to_yaml("FAO.yaml")
  554. pass
  555. # @pytest.mark.xfail
  556. def test_conversion_from_FAO_to_IPCC2006_PRIMAP():
  557. # make categorisation A from yaml
  558. categorisation_a = cc.from_python("FAO.py")
  559. # make categorisation B from yaml
  560. categorisation_b = cc.IPCC2006_PRIMAP
  561. # category a not part of climate categories, so we need to add them manually
  562. cats = {
  563. "A": categorisation_a,
  564. "B": categorisation_b,
  565. }
  566. # make conversion from csv
  567. conv = cc.Conversion.from_csv("conversion.FAO.IPPCC2006_PRIMAP.csv", cats=cats)
  568. ds = pm2.open_dataset(
  569. "extracted_data/v2024-11-14/FAOSTAT_Agrifood_system_emissions_v2024-11-14.nc"
  570. )
  571. result = ds.pr.convert(
  572. dim="category",
  573. conversion=conv,
  574. auxiliary_dimensions={"gas": "source (gas)"},
  575. )
  576. assert result