Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

country scc code #284

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 91 additions & 36 deletions src/dscim/menu/main_recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
extrap_formula=None,
fair_dims=None,
save_files=None,
geography=None,
**kwargs,
):
if scc_quantiles is None:
Expand Down Expand Up @@ -194,6 +195,10 @@
"global_consumption_no_pulse",
]

if "country_ISOs" in kwargs:
self.countries_mapping = pd.read_csv(kwargs["country_ISOs"])
self.countries = self.countries_mapping.MatchedISO.dropna().unique()

Check warning on line 200 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L199-L200

Added lines #L199 - L200 were not covered by tests

super().__init__(
sector_path=sector_path,
save_path=save_path,
Expand All @@ -203,6 +208,8 @@
eta=eta,
subset_dict=subset_dict,
ce_path=ce_path,
geography=geography,
kwargs=kwargs,
)

self.rho = rho
Expand Down Expand Up @@ -232,6 +239,7 @@
self.fair_dims = fair_dims
self.save_files = save_files
self.__dict__.update(**kwargs)
self.geography = geography
self.kwargs = kwargs

self.logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -477,7 +485,7 @@

@cachedproperty
@save(name="damage_function_points")
def damage_function_points(self) -> pd.DataFrame:
def damage_function_points(self, country=None) -> pd.DataFrame:
"""Global damages by RCP/GCM or SLR

Returns
Expand Down Expand Up @@ -508,6 +516,37 @@

return df

def country_damage_function_points(self, country) -> pd.DataFrame:
"""Global damages by RCP/GCM or SLR

Returns
--------
pd.DataFrame
"""
df = self.country_damages_calculation(country)

Check warning on line 526 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L526

Added line #L526 was not covered by tests

if "slr" in df.columns:
df = df.merge(self.climate.gmsl, on=["year", "slr"])
if "gcm" in df.columns:
df = df.merge(self.climate.gmst, on=["year", "gcm", "rcp"])

Check warning on line 531 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L528-L531

Added lines #L528 - L531 were not covered by tests

# removing illegal combinations from estimation
if any([i in df.ssp.unique() for i in ["SSP1", "SSP5"]]):
self.logger.info("Dropping illegal model combinations.")
for var in [i for i in df.columns if i in ["anomaly", "gmsl"]]:
df.loc[

Check warning on line 537 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L534-L537

Added lines #L534 - L537 were not covered by tests
((df.ssp == "SSP1") & (df.rcp == "rcp85"))
| ((df.ssp == "SSP5") & (df.rcp == "rcp45")),
var,
] = np.nan

# agriculture lacks ACCESS0-1/rcp85 combo
if "agriculture" in self.sector:
self.logger.info("Dropping illegal model combinations for agriculture.")
df.loc[(df.gcm == "ACCESS1-0") & (df.rcp == "rcp85"), "anomaly"] = np.nan

Check warning on line 546 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L544-L546

Added lines #L544 - L546 were not covered by tests

return df

Check warning on line 548 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L548

Added line #L548 was not covered by tests

def damage_function_calculation(self, damage_function_points, global_consumption):
"""The damage function model fit may be : (1) ssp specific, (2) ssp-model specific, (3) unique across ssp-model.
This depends on the type of discounting. In each case the input data passed to the fitting functions and the formatting of the returned
Expand Down Expand Up @@ -566,47 +605,54 @@
elif (self.discounting_type == "constant") or (
"ramsey" in self.discounting_type
):
for ssp, model in list(
loop = list(
product(
damage_function_points.ssp.unique(),
damage_function_points.model.unique(),
)
):
# Subset dataframe to specific SSP-IAM combination.
fit_subset = damage_function_points[
(damage_function_points["ssp"] == ssp)
& (damage_function_points["model"] == model)
]
)
for country in self.countries if self.geography == "country" else [0]:
if self.geography == "country":
damage_function_points = self.country_damage_function_points(

Check warning on line 616 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L616

Added line #L616 was not covered by tests
country
)
for ssp, model in loop:
# Subset dataframe to specific SSP-IAM combination.

global_c_subset = global_consumption.sel({"ssp": ssp, "model": model})
selection = (damage_function_points["ssp"] == ssp) & (
damage_function_points["model"] == model
)

# Fit damage function curves using the data subset
damage_function = model_outputs(
damage_function=fit_subset,
formula=self.formula,
type_estimation=self.fit_type,
global_c=global_c_subset,
extrapolation_type=self.ext_method,
quantiles=self.quantreg_quantiles,
year_range=yrs,
year_start_pred=self.ext_subset_end_year + 1,
)
fit_subset = damage_function_points[selection]

# Add variables
params = damage_function["parameters"].expand_dims(
dict(
discount_type=[self.discounting_type], ssp=[ssp], model=[model]
)
)
subset = dict(ssp=[ssp], model=[model])

preds = damage_function["preds"].expand_dims(
dict(
discount_type=[self.discounting_type], ssp=[ssp], model=[model]
if self.geography == "country":
subset.update(dict(country=[country]))

Check warning on line 631 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L631

Added line #L631 was not covered by tests

global_c_subset = global_consumption.sel(subset)

# Fit damage function curves using the data subset
damage_function = model_outputs(
damage_function=fit_subset,
formula=self.formula,
type_estimation=self.fit_type,
global_c=global_c_subset,
extrapolation_type=self.ext_method,
quantiles=self.quantreg_quantiles,
year_range=yrs,
year_start_pred=self.ext_subset_end_year + 1,
)
)

params_list.append(params)
preds_list.append(preds)
subset.update(dict(discount_type=[self.discounting_type]))

# Add variables
params = damage_function["parameters"].expand_dims(subset)

preds = damage_function["preds"].expand_dims(subset)

params_list.append(params)
preds_list.append(preds)

elif "gwr" in self.discounting_type:
# Fit damage function across all SSP-IAM combinations, as expected
Expand Down Expand Up @@ -797,9 +843,15 @@
"""

# Calculate global consumption per capita
array_pc = self.global_consumption_calculation(
disc_type
) / self.collapsed_pop.sum("region")
if self.geography == "country":
array_pc = (

Check warning on line 847 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L847

Added line #L847 was not covered by tests
self.global_consumption_calculation(disc_type)
/ self.country_econ_vars.pop
)
else:
array_pc = self.global_consumption_calculation(
disc_type
) / self.collapsed_pop.sum("region")

if self.NAME == "equity":
# equity recipe's growth is capped to
Expand Down Expand Up @@ -840,7 +892,10 @@

# holding population constant
# from 2100 to 2300 with 2099 values
pop = self.collapsed_pop.sum("region")
if self.geography == "country":
pop = self.country_econ_vars.pop

Check warning on line 896 in src/dscim/menu/main_recipe.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/main_recipe.py#L896

Added line #L896 was not covered by tests
else:
pop = self.collapsed_pop.sum("region")
pop = pop.reindex(
year=range(pop.year.min().values, self.ext_end_year + 1),
method="ffill",
Expand Down
45 changes: 43 additions & 2 deletions src/dscim/menu/risk_aversion.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pandas as pd
import xarray as xr
from dscim.menu.main_recipe import MainRecipe
from dscim.descriptors import cachedproperty


class RiskAversionRecipe(MainRecipe):
Expand Down Expand Up @@ -38,7 +39,7 @@

return ce_array

@property
@cachedproperty
def calculated_damages(self) -> xr.DataArray:
"""Calculate damages (difference between CEs)"""
return self.ce_no_cc - self.ce_cc
Expand All @@ -62,6 +63,43 @@

return df

def country_damages_calculation(self, country) -> pd.DataFrame:
"""Aggregate damages to country level

Returns
--------
pd.DataFrame
"""

self.logger.info(f"Calculating damages for {country}")
dams_collapse = self.calculated_damages * self.collapsed_pop

Check warning on line 75 in src/dscim/menu/risk_aversion.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/risk_aversion.py#L74-L75

Added lines #L74 - L75 were not covered by tests

territories = []
for ii, row in self.countries_mapping.iterrows():
if row["MatchedISO"] == country:
territories.append(row["ISO"])
self.logger.debug(f"Including territories {territories}")

Check warning on line 81 in src/dscim/menu/risk_aversion.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/risk_aversion.py#L77-L81

Added lines #L77 - L81 were not covered by tests

dams_collapse = dams_collapse.sel(

Check warning on line 83 in src/dscim/menu/risk_aversion.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/risk_aversion.py#L83

Added line #L83 was not covered by tests
region=[
i
for i in dams_collapse.region.values
if any(j in i for j in territories)
]
)

dams_collapse = (

Check warning on line 91 in src/dscim/menu/risk_aversion.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/risk_aversion.py#L91

Added line #L91 was not covered by tests
dams_collapse.sum(dim="region").to_dataframe("damages").reset_index()
)

if "gwr" in self.discounting_type:
dams_collapse = dams_collapse.assign(

Check warning on line 96 in src/dscim/menu/risk_aversion.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/risk_aversion.py#L95-L96

Added lines #L95 - L96 were not covered by tests
ssp=str(list(self.gdp.ssp.values)),
model=str(list(self.gdp.model.values)),
)

return dams_collapse

Check warning on line 101 in src/dscim/menu/risk_aversion.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/risk_aversion.py#L101

Added line #L101 was not covered by tests

def global_consumption_calculation(self, disc_type):
"""Calculate global consumption

Expand All @@ -71,7 +109,10 @@
"""

if (disc_type == "constant") or ("ramsey" in disc_type):
global_cons_no_cc = self.gdp.sum(dim=["region"])
if self.geography == "country":
global_cons_no_cc = self.country_econ_vars.gdp

Check warning on line 113 in src/dscim/menu/risk_aversion.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/risk_aversion.py#L113

Added line #L113 was not covered by tests
else:
global_cons_no_cc = self.gdp.sum(dim=["region"])

elif disc_type == "constant_model_collapsed":
global_cons_no_cc = self.gdp.sum(dim=["region"]).mean(dim=["model"])
Expand Down
38 changes: 38 additions & 0 deletions src/dscim/menu/simple_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,12 @@
histclim=None,
ce_path=None,
subset_dict=None,
geography=None,
**kwargs,
):
if geography is None:
geography = "global"

self.sector_path = sector_path
self.save_path = save_path
self.gdppc_bottom_code = gdppc_bottom_code
Expand All @@ -317,6 +322,13 @@
self.histclim = histclim
self.ce_path = ce_path
self.eta = eta
self.__dict__.update(**kwargs)
self.geography = geography
self.kwargs = kwargs

if "country_ISOs" in kwargs:
self.countries_mapping = pd.read_csv(kwargs["country_ISOs"])
self.countries = self.countries_mapping.MatchedISO.dropna().unique()

Check warning on line 331 in src/dscim/menu/simple_storage.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/simple_storage.py#L330-L331

Added lines #L330 - L331 were not covered by tests

self.logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -368,6 +380,32 @@
)
return raw

@property
def country_econ_vars(self):
mapping_dict = {}
for ii, row in self.countries_mapping.iterrows():
mapping_dict[row["ISO"]] = row["MatchedISO"]
if row["MatchedISO"] == "nan":
mapping_dict[row["ISO"]] = "nopop"

Check warning on line 389 in src/dscim/menu/simple_storage.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/simple_storage.py#L385-L389

Added lines #L385 - L389 were not covered by tests

socioec = self.econ_vars.econ_vars
socioec = socioec.assign_coords(

Check warning on line 392 in src/dscim/menu/simple_storage.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/simple_storage.py#L391-L392

Added lines #L391 - L392 were not covered by tests
{"region": [region[:3] for region in socioec.region.values]}
)
new_ISOs = []
for ISO in socioec.region.values:
new_ISOs.append(mapping_dict[ISO])
raw = (

Check warning on line 398 in src/dscim/menu/simple_storage.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/simple_storage.py#L395-L398

Added lines #L395 - L398 were not covered by tests
socioec.assign_coords({"region": new_ISOs})
.groupby("region")
.sum()
.rename({"region": "country"})
.drop_sel(country="nan")
)
raw = self.cut(raw, end_year=2099)

Check warning on line 405 in src/dscim/menu/simple_storage.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/simple_storage.py#L405

Added line #L405 was not covered by tests

return raw

Check warning on line 407 in src/dscim/menu/simple_storage.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/menu/simple_storage.py#L407

Added line #L407 was not covered by tests

@cachedproperty
def gdp(self):
return self.cut_econ_vars.gdp
Expand Down
14 changes: 9 additions & 5 deletions src/dscim/utils/rff.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,15 +364,19 @@
weights_path,
pulse_year,
mask,
geography=None,
):
"""Wrapper function for `weight_df()`."""

# ssp GDP for fractionalizing damage functions
ssp_gdp = xr.open_zarr(ssp_gdp, consolidated=True).sum("region").gdp

# get RFF data
region = "USA" if USA else "world"
rff_gdp = xr.open_dataset(rff_gdp).sel(region=region, drop=True).gdp
if geography == "country":
ssp_gdp = xr.open_zarr(ssp_gdp, consolidated=True).gdp
rff_gdp = xr.open_dataset(rff_gdp).gdp

Check warning on line 374 in src/dscim/utils/rff.py

View check run for this annotation

Codecov / codecov/patch

src/dscim/utils/rff.py#L373-L374

Added lines #L373 - L374 were not covered by tests
else:
ssp_gdp = xr.open_zarr(ssp_gdp, consolidated=True).sum("region").gdp
# get RFF data
region = "USA" if USA else "world"
rff_gdp = xr.open_dataset(rff_gdp).sel(region=region, drop=True).gdp

# get global consumption factors to extrapolate damage function
factors = rff_gdp.sel(year=slice(2100, 2300)) / rff_gdp.sel(year=2099)
Expand Down