SupplyChain class

[1]:
import numpy as np
import pandas as pd
from climada.hazard import TCTracks, TropCyclone, Centroids
from climada.entity import LitPop
from climada.entity import ImpactFuncSet, ImpfTropCyclone
from climada.engine import SupplyChain

This tutorial shows how to use the SupplyChain class of CLIMADA. This class allows assessing indirect impacts via Input-Ouput modeling. Before diving into this class, it is highly recommended the user first familiarizes herself with the Exposures, Hazard and Impact classes.

1. Load Multi-Regional Input-Output Tables (MRIOT) data.

At first, one needs to load Input Output data. SupplyChain has a function to download and read multi-regional input-output tables (MRIOT) from the 2016 release of WIOD project (www.wiod.org). Yearly WIOT tables are available for the period 2000-2014. A table is automatically downloaded the first time it is used.

[2]:
supplychain = SupplyChain()
supplychain.read_wiod16(year=2012)
2021-04-23 14:32:14,219 - climada.util.files_handler - INFO - Downloading http://www.wiod.org/protected3/data16/wiot_ROW/WIOT2012_Nov16_ROW.xlsb to file /Users/zeliestalhanske/python_projects/climada_python/doc/tutorial/climada/engine/test/data/WIOT2012_Nov16_ROW.xlsb
60.6kKB [00:14, 4.30kKB/s]
2021-04-23 14:32:28,340 - climada.engine.supplychain - INFO - Downloading WIOD table for year 2012

Let’s now look at what data are now loaded into SupplyChain, i.e. modelled countries, sectors and IO data structure.

[3]:
supplychain.mriot_reg_names
[3]:
array(['AUS', 'AUT', 'BEL', 'BGR', 'BRA', 'CAN', 'CHE', 'CHN', 'CYP',
       'CZE', 'DEU', 'DNK', 'ESP', 'EST', 'FIN', 'FRA', 'GBR', 'GRC',
       'HRV', 'HUN', 'IDN', 'IND', 'IRL', 'ITA', 'JPN', 'KOR', 'LTU',
       'LUX', 'LVA', 'MEX', 'MLT', 'NLD', 'NOR', 'POL', 'PRT', 'ROU',
       'RUS', 'SVK', 'SVN', 'SWE', 'TUR', 'TWN', 'USA', 'ROW'],
      dtype=object)

There are 43 countries plus 1. The additional “country” refers to all countries not explicitly modeled which are aggregated into a Rest of World (ROW) “country”.

[4]:
supplychain.sectors
[4]:
array(['Crop and animal production, hunting and related service activities',
       'Forestry and logging', 'Fishing and aquaculture',
       'Mining and quarrying',
       'Manufacture of food products, beverages and tobacco products',
       'Manufacture of textiles, wearing apparel and leather products',
       'Manufacture of wood and of products of wood and cork, except furniture; manufacture of articles of straw and plaiting materials',
       'Manufacture of paper and paper products',
       'Printing and reproduction of recorded media',
       'Manufacture of coke and refined petroleum products ',
       'Manufacture of chemicals and chemical products ',
       'Manufacture of basic pharmaceutical products and pharmaceutical preparations',
       'Manufacture of rubber and plastic products',
       'Manufacture of other non-metallic mineral products',
       'Manufacture of basic metals',
       'Manufacture of fabricated metal products, except machinery and equipment',
       'Manufacture of computer, electronic and optical products',
       'Manufacture of electrical equipment',
       'Manufacture of machinery and equipment n.e.c.',
       'Manufacture of motor vehicles, trailers and semi-trailers',
       'Manufacture of other transport equipment',
       'Manufacture of furniture; other manufacturing',
       'Repair and installation of machinery and equipment',
       'Electricity, gas, steam and air conditioning supply',
       'Water collection, treatment and supply',
       'Sewerage; waste collection, treatment and disposal activities; materials recovery; remediation activities and other waste management services ',
       'Construction',
       'Wholesale and retail trade and repair of motor vehicles and motorcycles',
       'Wholesale trade, except of motor vehicles and motorcycles',
       'Retail trade, except of motor vehicles and motorcycles',
       'Land transport and transport via pipelines', 'Water transport',
       'Air transport',
       'Warehousing and support activities for transportation',
       'Postal and courier activities',
       'Accommodation and food service activities',
       'Publishing activities',
       'Motion picture, video and television programme production, sound recording and music publishing activities; programming and broadcasting activities',
       'Telecommunications',
       'Computer programming, consultancy and related activities; information service activities',
       'Financial service activities, except insurance and pension funding',
       'Insurance, reinsurance and pension funding, except compulsory social security',
       'Activities auxiliary to financial services and insurance activities',
       'Real estate activities',
       'Legal and accounting activities; activities of head offices; management consultancy activities',
       'Architectural and engineering activities; technical testing and analysis',
       'Scientific research and development',
       'Advertising and market research',
       'Other professional, scientific and technical activities; veterinary activities',
       'Administrative and support service activities',
       'Public administration and defence; compulsory social security',
       'Education', 'Human health and social work activities',
       'Other service activities',
       'Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use',
       'Activities of extraterritorial organizations and bodies'],
      dtype=object)
[5]:
print(supplychain.sectors.shape)
(56,)

There are 56 economic sectors. These sectors can also be grouped into higher-level sectors. For instance, in the aftermath we will model the service sector which will include the following sectors:

[6]:
supplychain.sectors[range(26,56)]
[6]:
array(['Construction',
       'Wholesale and retail trade and repair of motor vehicles and motorcycles',
       'Wholesale trade, except of motor vehicles and motorcycles',
       'Retail trade, except of motor vehicles and motorcycles',
       'Land transport and transport via pipelines', 'Water transport',
       'Air transport',
       'Warehousing and support activities for transportation',
       'Postal and courier activities',
       'Accommodation and food service activities',
       'Publishing activities',
       'Motion picture, video and television programme production, sound recording and music publishing activities; programming and broadcasting activities',
       'Telecommunications',
       'Computer programming, consultancy and related activities; information service activities',
       'Financial service activities, except insurance and pension funding',
       'Insurance, reinsurance and pension funding, except compulsory social security',
       'Activities auxiliary to financial services and insurance activities',
       'Real estate activities',
       'Legal and accounting activities; activities of head offices; management consultancy activities',
       'Architectural and engineering activities; technical testing and analysis',
       'Scientific research and development',
       'Advertising and market research',
       'Other professional, scientific and technical activities; veterinary activities',
       'Administrative and support service activities',
       'Public administration and defence; compulsory social security',
       'Education', 'Human health and social work activities',
       'Other service activities',
       'Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use',
       'Activities of extraterritorial organizations and bodies'],
      dtype=object)

Weather to aggregate sectors into main sectors and how to do it is up to the user, according to the application of interest and data availability. Default settings are available in CLIMADA based on the built-in datasets. These will be introduced below when calculating direct damages.

[7]:
supplychain.mriot_data
[7]:
array([[11105.733356382272, 315.7113177373571, 179.43254266338693, ...,
        9.093853356314124, 0, 1.1873518978687656e-06],
       [116.88308162207898, 139.3046230501366, 0.4165797269551787, ...,
        0.016109951596559337, 0, 2.9150840971500206e-08],
       [22.556627754337466, 0.011392711240065655, 23.191690635397794,
        ..., 0.02463511351049634, 0, 2.9671358991110717e-09],
       ...,
       [2.0888621906340483, 0.06898124909560921, 0.18736619171021462,
        ..., 15914.85428702459, 0, 0.7881946937807305],
       [0.041425098917944464, 4.0179492086967524e-05,
        0.00019545212518185459, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=object)
[8]:
supplychain.mriot_data.shape
[8]:
(2464, 2464)

The MRIO table is a squared matrix with columns (and rows) equal to the number of economic sectors times the number of modeled countries, i.e. 56x44 = 2464. Each column (row) reports input (output) data of all sectors of a given country untill all countries are reported. The total production from all subsectors of all countries is:

[9]:
supplychain.total_prod
[9]:
array([71514.7394, 2525.2804, 3080.4692, ..., 409216.80039034015,
       21108.22611227322, 33.03248952629331], dtype=object)
[10]:
print(supplychain.total_prod.shape)
(2464,)

The following dict allows accessing mriot data of single countries:

[11]:
supplychain.reg_pos
[11]:
{'AUS': range(0, 56),
 'AUT': range(56, 112),
 'BEL': range(112, 168),
 'BGR': range(168, 224),
 'BRA': range(224, 280),
 'CAN': range(280, 336),
 'CHE': range(336, 392),
 'CHN': range(392, 448),
 'CYP': range(448, 504),
 'CZE': range(504, 560),
 'DEU': range(560, 616),
 'DNK': range(616, 672),
 'ESP': range(672, 728),
 'EST': range(728, 784),
 'FIN': range(784, 840),
 'FRA': range(840, 896),
 'GBR': range(896, 952),
 'GRC': range(952, 1008),
 'HRV': range(1008, 1064),
 'HUN': range(1064, 1120),
 'IDN': range(1120, 1176),
 'IND': range(1176, 1232),
 'IRL': range(1232, 1288),
 'ITA': range(1288, 1344),
 'JPN': range(1344, 1400),
 'KOR': range(1400, 1456),
 'LTU': range(1456, 1512),
 'LUX': range(1512, 1568),
 'LVA': range(1568, 1624),
 'MEX': range(1624, 1680),
 'MLT': range(1680, 1736),
 'NLD': range(1736, 1792),
 'NOR': range(1792, 1848),
 'POL': range(1848, 1904),
 'PRT': range(1904, 1960),
 'ROU': range(1960, 2016),
 'RUS': range(2016, 2072),
 'SVK': range(2072, 2128),
 'SVN': range(2128, 2184),
 'SWE': range(2184, 2240),
 'TUR': range(2240, 2296),
 'TWN': range(2296, 2352),
 'USA': range(2352, 2408),
 'ROW': range(2408, 2464)}

For example, focusing on Switzerland, to find output data from all swiss sectors one can do:

[12]:
supplychain.mriot_data[supplychain.reg_pos['CHE']]
[12]:
array([[0.16796253031842162, 0.004777144111849153, 0.002736270353709005,
        ..., 0.12834852430129112, 0, 1.6758007628524304e-08],
       [8.51026746906875e-05, 7.127402684742127e-05,
        1.9350907062673556e-06, ..., 0.0007570977491247835, 0,
        1.3699629047508064e-09],
       [4.7138344808091295e-05, 9.671160838557614e-09,
        6.697562625578982e-05, ..., 0.0009232593169242273, 0,
        1.1120045630264473e-10],
       ...,
       [0.008508978608710327, 3.1972146323171236e-05,
        0.00046072292226048554, ..., 0.029862336991154915, 0,
        1.4789538839516966e-06],
       [9.500104969801383e-07, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=object)

Similarly, one can find the total production of all Swiss sectors:

[13]:
supplychain.total_prod[supplychain.reg_pos['CHE']]
[13]:
array([10884.279940160777, 674.088481002245, 41.224100570050936,
       2054.5309263989934, 39841.6239844894, 3648.168376381703,
       8480.201098809475, 3688.826352338922, 4255.674165800708,
       3501.317455453859, 18363.278595872554, 78379.69147195964,
       8150.894879859878, 7447.266269845102, 5718.605069058962,
       20135.29803849402, 66467.12373541784, 22646.288496050656,
       31162.492702290187, 2178.324505028241, 5869.153355999175,
       10800.277292026229, 4680.307868675113, 48856.01258517912,
       5780.830493280612, 0, 77613.75291956161, 14161.940349695808,
       125376.47352416613, 42389.765628680085, 41523.925997007325,
       1039.0976694128697, 13234.059687011602, 17225.389030894996,
       8050.766299432082, 24044.706531639047, 10080.003387207098, 0,
       17138.91756562977, 27213.104595857796, 63105.9204496511,
       45928.766683415735, 0, 65051.40098246406, 60745.78454637557, 0,
       19266.754769461568, 0, 9057.661417570102, 31139.843888469703,
       63363.23886998102, 41714.31400559072, 69213.80028365219,
       24057.595454974882, 2126.7054909496537, 0], dtype=object)

2. Define Hazard, Exposure and Vulnerability

Let’s now define hazard, exposure and vulnerability. This is handled via the related CLIMADA classes. In this tutorial we use LitPop for exposure and TropCyclone for hazard. We will focus on the impact of tropical cyclones affecting the Philippines, Taiwan, Vietnam and Japan in 2012 and 2013. Japan and Taiwan are modeled explicitely by the MRIO table, while the Philippines and Vietnam are modeled as Rest of World, they are thus aggregated into a single country.

[ ]:
countries = ['PHL', 'TWN', 'VNM', 'JPN']
exp_lp = LitPop()
exp_lp.set_country(countries, res_arcsec=150)
exp_lp.set_geometry_points()
[ ]:
exp_lp.plot_hexbin(pop_name=False)
[ ]:
tc_tracks=TCTracks()
tc_tracks.read_ibtracs_netcdf(year_range=(2012,2013), basin='WP')
[ ]:
tc_tracks.plot()
[ ]:
centr=Centroids()
centr.set_lat_lon(exp_lp.gdf.latitude.values, exp_lp.gdf.longitude.values)
[ ]:
tc_cyclone = TropCyclone()
tc_cyclone.set_from_tracks(tracks=tc_tracks, centroids=centr)
[ ]:
tc_cyclone.plot_intensity(event=0)
[ ]:
impf_tc= ImpfTropCyclone()
impf_tc.set_emanuel_usa()

# add the impact function to an Impact function set
impf_set = ImpactFuncSet()
impf_set.append(impf_tc)
impf_set.check()
[ ]:
[haz_type] = impf_set.get_hazard_types()
[haz_id] = impf_set.get_ids()[haz_type]

# Exposures: rename column and assign id
exp_lp.gdf.rename(columns={"impf_": "impf_" + haz_type}, inplace=True)
exp_lp.gdf['impf_' + haz_type] = haz_id
exp_lp.check()

3. Calculate direct, indirect and total impact per sector and country

Let’s now calculate direct, indirect and total impacts. For the direct impact, SupplyChain requires as inputs Hazard, Exposures and ImpactFuncSet. In addition, one may want to specify selected_subsec, which allows the user to either define her own aggregation of sectors by providing a list with the positions of the sectors to aggregate or to use built-in sectors aggregations passing a string being either service, manufacturing, agriculture or mining.

For this tutorial, we will model the service sector, as this sector’s exposure can reasonably be modelled via nighlights and population data, i.e. via LitPop.

3.1 Direct impact

[ ]:
supplychain.calc_sector_direct_impact(tc_cyclone, exp_lp, impf_set, selected_subsec='service')

Let’s see what new attributes the class has got now.

[ ]:
supplychain.direct_impact
[ ]:
supplychain.direct_impact.shape

All impact matrixes (also those below) provide impacts aggregated over years. They have a number of rows equal to the years being modeled (2 years this time, i.e. 2012-2013) and columns equal to the number of countries times the number of sectors. , i.e. 2464 (see also above).

[ ]:
supplychain.direct_aai_agg
[ ]:
supplychain.direct_aai_agg.shape

The annual aggregated impact (aai) matrixes provide yearly average impact. They are row vectors with columns equal to the number of countries times the number of sectors, i.e. 2464.

Info for a given country can be accessed as done below:

[ ]:
supplychain.direct_aai_agg[supplychain.reg_pos['CHE']]

with e.g. CHE, we obviously get zeros, as we are modeling direct impacts in east Asia.

[ ]:
supplychain.direct_aai_agg[supplychain.reg_pos['JPN']]

for e.g. Japan we instead have direct damages. In order to get all positions of countries undergoing direct damages, one can access the following list:

[ ]:
supplychain.reg_dir_imp #note we have two ROW for PHL and VNM

and do the following:

[ ]:
all_pos = [y for cntry in np.unique(supplychain.reg_dir_imp) for y in supplychain.reg_pos[cntry]]
[ ]:
print(supplychain.direct_impact.sum(), supplychain.direct_impact[:, all_pos].sum())
[ ]:
print(supplychain.direct_aai_agg.sum(), supplychain.direct_aai_agg[all_pos].sum())

i.e., the matrix has non-zero values only at positions corresponding to the modelled countries.

3.2 Indirect impact

For the indirect impact, one can choose the IO modeling approach between Leontief, Ghosh and EEIOA. References are provided below:

[ ]:
supplychain.calc_indirect_impact?

Let’s calculate indirect impacts according to the Ghosh method:

[ ]:
supplychain.calc_indirect_impact(io_approach='ghosh')

The class now has the indirect impact matrix and vector, with structure equal to those introduced for the direct impact:

[ ]:
supplychain.indirect_impact
[ ]:
supplychain.indirect_impact.shape
[ ]:
supplychain.indirect_aai_agg

If we now check damages for e.g. Switzerland:

[ ]:
supplychain.indirect_aai_agg[supplychain.reg_pos['CHE']]

there are non-zero values, as CH undergoes indirect impacts due to events happening in east Asia.

We can also visualize coefficients, inverse matrix and risk matrix of the selected IO approach:

[ ]:
supplychain.io_data

3.3 Total impact

Finally, let’s calculate total impacts, as the sum of both direct and indirect. Therefore, the impact matrixes have the same structure as the direct and indirect matrixes.

[ ]:
supplychain.calc_total_impact()
[ ]:
supplychain.total_impact
[ ]:
supplychain.total_aai_agg

Finally, one can for example visualize total annual average impacts to all Japanese (direct plus indirect) and Swiss (only direct) subsector after TC in East Asia:

[ ]:
df_imp = pd.DataFrame(data=np.vstack([supplychain.total_aai_agg[supplychain.reg_pos['CHE']],
                                      supplychain.total_aai_agg[supplychain.reg_pos['JPN']]]),
                      columns=supplychain.sectors,
                      index=['CHE', 'JPN'])
[ ]:
df_imp #in M USD
[ ]: