test_conversion.py 17 KB

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