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

Feature/coastal flooding #100

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions climada_petals/conf/climada.conf
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,17 @@
}
}
},
"coastal_flood": {
"local_data" : {
"aqueduct" : "{local_data.system}/CoastalFlood/Aqueduct"
},
"resources" : {
"aqueduct" : "http://wri-projects.s3.amazonaws.com/AqueductFloodTool/download/v2/"
},
"copernicus": {
"local_data": "{local_data.system}/copernicus_data",
"seasonal_forecasts": "{local_data.system}/copernicus_data/seasonal_forecasts"
}
}
}
}
1 change: 1 addition & 0 deletions climada_petals/hazard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
from .tc_rainfield import *
from .tc_surge_bathtub import *
from .wildfire import *
from .coastal_flood import *
130 changes: 130 additions & 0 deletions climada_petals/hazard/coastal_flood.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from typing import Iterable, Union, Optional

Check warning on line 1 in climada_petals/hazard/coastal_flood.py

View check run for this annotation

Jenkins - WCR / Pylint

missing-module-docstring

LOW: Missing module docstring
Raw output
no description found
import numpy as np
from shapely.geometry import Polygon

from climada.hazard import Hazard
from climada.util import files_handler as u_fh
import climada.util.coordinates as u_coord
from climada import CONFIG

AQUEDUCT_SOURCE_LINK = CONFIG.hazard.coastal_flood.resources.aqueduct.str()
DOWNLOAD_DIRECTORY = CONFIG.hazard.coastal_flood.local_data.aqueduct.dir()

HAZ_TYPE = 'CF'
"""Hazard type acronym CoastalFlood"""

__all__ = ['CoastalFlood']

class CoastalFlood(Hazard):
"""
Contains coastal flood events pulled by the Acqueduct project :
https://www.wri.org/data/aqueduct-floods-hazard-maps
"""

def __init__(self, **kwargs):
"""Empty constructor"""

Hazard.__init__(self, HAZ_TYPE, **kwargs)

@classmethod
def from_aqueduct_tif(cls,

Check warning on line 30 in climada_petals/hazard/coastal_flood.py

View check run for this annotation

Jenkins - WCR / Pylint

too-many-arguments

LOW: Too many arguments (9/7)
Raw output
Used when a function or method takes too many arguments.

Check warning on line 30 in climada_petals/hazard/coastal_flood.py

View check run for this annotation

Jenkins - WCR / Pylint

too-many-positional-arguments

LOW: Too many positional arguments (9/5)
Raw output
no description found

Check warning on line 30 in climada_petals/hazard/coastal_flood.py

View check run for this annotation

Jenkins - WCR / Pylint

too-many-locals

LOW: Too many local variables (25/15)
Raw output
Used when a function or method has too many local variables.
Comment on lines +24 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case I think it makes much more sense to remove the from method and put all the functionality in the init. There seems to be no other purpose than to load data (initialize the object).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mhh, as of now maybe yes, but it's likely that the CoastalFlood class will be populated with more data in the future; same as it's happening with the RiverFlood class where we now have ISIMIP and Aqueduct (https://github.com/CLIMADA-project/climada_petals/blob/feature/river_flooding_aqueduct/climada_petals/hazard/river_flood.py) so I would tend to keep it like this.

rcp: str,
target_year : str,
return_periods: Union[int, Iterable[int]],
subsidence: Optional[str] = 'wtsub',
percentile: str = '95',
countries: Optional[Union[str, Iterable[str]]] = None,
boundaries: Iterable[float] = None,
dwd_dir=DOWNLOAD_DIRECTORY):
"""
Download and load coastal flooding events from the Aqueduct dataset.
For more info on the data see:
https://files.wri.org/d8/s3fs-public/aqueduct-floods-methodology.pdf

Parameters
----------
rcp : str
RCPs scenario. Possible values are historical, 45 and 85.
target_year : str
future target year. Possible values are hist, 2030, 2050 and 2080.
return_periods : int or list of int
events' return periods.
Possible values are 2, 5, 10, 25, 50, 100, 250, 500, 1000.
subsidence : str
If land subsidence is simulated or not.
Possible values are nosub and wtsub. Default wtsub.
percentile : str
Sea level rise scenario (in percentile).
Possible values are 05, 50 and 95.
countries : str or list of str
countries ISO3 codes
boundaries : tuple of floats
geographical boundaries in the order:
minimum longitude, minimum latitude,
maximum longitude, maximum latitude
Returns
-------
haz : CoastalFlood instance
"""

if (target_year == 'hist') & (rcp != 'historical'):
raise ValueError(f"RCP{rcp} cannot have hist as target year")

if (rcp == 'historical') & (subsidence == 'nosub') & (target_year != 'hist'):
raise ValueError("Historical without subsidence can only have hist as target year")

if (rcp == 'historical') & (percentile != '0'):
raise ValueError("Historical shall not have a assigned percentiles of sea level rise. Leave default values.")

Check warning on line 77 in climada_petals/hazard/coastal_flood.py

View check run for this annotation

Jenkins - WCR / Pylint

line-too-long

LOW: Line too long (121/100)
Raw output
Used when a line is longer than a given number of characters.

if isinstance(return_periods, int):
return_periods = [return_periods]

return_periods.sort(reverse=True)

if isinstance(countries, str):
countries = [countries]

rcp_name = f"rcp{rcp[0]}p{rcp[1]}" if rcp in ['45', '85'] else rcp
ret_per_names = [f"{str(rp).zfill(4)}" for rp in return_periods]
perc_name = f"0_perc_{percentile.zfill(2)}" if percentile in ['5', '50'] else '0'

file_names = [f"inuncoast_{rcp_name}_{subsidence}_{target_year}_"
f"rp{rp.zfill(4)}_{perc_name}.tif"
for rp in ret_per_names]

file_paths = []
for file_name in file_names:
link_to_file = "".join([AQUEDUCT_SOURCE_LINK, file_name])
file_paths.append(dwd_dir / file_name)

if not file_paths[-1].exists():
u_fh.download_file(link_to_file, download_dir=dwd_dir)

if countries:
# TODO: this actually does not work with multiple countries

Check warning on line 104 in climada_petals/hazard/coastal_flood.py

View check run for this annotation

Jenkins - WCR / Pylint

fixme

NORMAL: TODO: this actually does not work with multiple countries
Raw output
no description found
geom = u_coord.get_land_geometry(countries).geoms

elif boundaries:
min_lon, min_lat, max_lon, max_lat = boundaries
geom = [Polygon([(min_lon, min_lat),
(max_lon, min_lat),
(max_lon, max_lat),
(min_lon, max_lat)])]
else:
geom = None

event_id = np.arange(len(file_names))
frequencies = np.diff(1 / np.array(return_periods), prepend=0)
event_names = [f"1-in-{return_periods[i]}y_{percentile}pct_"
f"{rcp_name}_{target_year}_{subsidence}"
for i in range(len(file_names))]

haz = cls().from_raster(files_intensity=file_paths,
geometry=geom,
attrs={'event_id': event_id,
'event_name': event_names,
'frequency': frequencies})

haz.units = 'm'

return haz
66 changes: 66 additions & 0 deletions climada_petals/hazard/test/test_coastal_flood.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@

"""
This file is part of CLIMADA.

Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.

CLIMADA is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free
Software Foundation, version 3.

CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.

---

Tests on Coastal Flood Hazard"""

import unittest
import requests
import numpy as np
from climada_petals.hazard.coastal_flood import AQUEDUCT_SOURCE_LINK

RCPS = ['historical', 'rcp4p5', 'rcp8p5']
TARGET_YEARS = ['hist', '2030', '2050', '2080']
RETURN_PERIODS = ['0002', '0005', '0010', '0025',
'0050', '0100', '0250', '0500', '1000']
SUBSIDENCE = ['nosub', 'wtsub']
PERCENTILES = ['0_perc_05', '0_perc_50', '0']

class TestReader(unittest.TestCase):
"""Test that Coastal flood data exist"""

def test_files_exist(self):

file_names = [
f'inuncoast_{rcp}_{sub}_{year}_rp{rp}_{perc}.tif'
for rcp in RCPS
for sub in SUBSIDENCE
for year in TARGET_YEARS
for rp in RETURN_PERIODS
for perc in PERCENTILES
# You can't have:
# - year historic with rcp different than historical
# - rcp historical, no subsidence and year different than historic
# - rcp historical and SLR scenarios' percentiles
if not (((year == 'hist') & (rcp != 'historical')) |
((rcp == 'historical') & (sub == 'nosub') & (year != 'hist')) |
((rcp == 'historical') & (perc != '0')))
]

test_files_pos = np.random.choice(range(len(file_names)),
size=10,
replace=False)
for i in test_files_pos:
file_path = "".join([AQUEDUCT_SOURCE_LINK, file_names[i]])
request_code = requests.get(file_path).status_code
self.assertTrue(request_code == 200)

# Execute Tests
if __name__ == "__main__":
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestReader)
unittest.TextTestRunner(verbosity=2).run(TESTS)
Loading
Loading