Skip to content

Commit

Permalink
Breaking: Add future data for worldclim and improve climate model han…
Browse files Browse the repository at this point in the history
…dling (#69)

* define climate models programatically

* first draft for future wordclim climate

* define ClimateModel before using it

* first draft for future worldclim bioclim

* move exports to main file

* reorganise future worldclim

* reorganise some code

* always use _format not string

* add a dispatch on worldclim future with layers argument

* fix some dispatches

* add worldclim future tests

* always _format(T) and no dashes in file path

* update chelsa future test

* fix a test

* clean up a line outside testset

* soft document why dashes in modelname path are dropped

* add a generic date formatter

* make future worldclim work for multiple dates

* fix worldclim futur tests

* fix date_step and range for chelsa bioclim

* define layerkeys for WorldClim{Future{BioClim}}
  • Loading branch information
tiemvanderdeure authored Oct 9, 2024
1 parent 8aa489d commit a4d36d1
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 313 deletions.
19 changes: 6 additions & 13 deletions src/RasterDataSources.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,6 @@ export ECO4ESIPTJPL,ECO4WUE,GEDI03,GEDI04_B,MCD12Q1,MCD12Q2,MCD15A2H,
MYD17A2H, MYD17A3HGF, MYD21A2, SIF005, SIF_ANN, VNP09A1, VNP09H1,
VNP13A1, VNP15A2H, VNP21A2, VNP22Q2

# Climate models from CMIP5 (used in CHELSA)
export ACCESS1, BNUESM, CCSM4, CESM1BGC, CESM1CAM5, CMCCCMS, CMCCCM, CNRMCM5,
CSIROMk3, CanESM2, FGOALS, FIOESM, GFDLCM3, GFDLESM2G, GFDLESM2M, GISSE2HCC,
GISSE2H, GISSE2RCC, GISSE2R, HadGEM2AO, HadGEM2CC, IPSLCM5ALR, IPSLCM5AMR,
MIROCESMCHEM, MIROCESM, MIROC5, MPIESMLR, MPIESMMR, MRICGCM3, MRIESM1, NorESM1M,
BCCCSM1, Inmcm4

# Climate models from CMIP6 (used in WorldClim)
export BCCCSM2MR, CNRMCM61, CNRMESM21, CanESM5, GFDLESM4, IPSLCM6ALR, MIROCES2L, MIROC6, MRIESM2

# Climate models from CMIP6 (CHELSA)
export UKESM, MPIESMHR

export Values, Deciles

export getraster
Expand All @@ -63,6 +50,7 @@ include("worldclim/bioclim.jl")
include("worldclim/climate.jl")
include("worldclim/weather.jl")
include("worldclim/elevation.jl")
include("worldclim/future.jl")

include("chelsa/shared.jl")
include("chelsa/climate.jl")
Expand All @@ -84,4 +72,9 @@ include("modis/products.jl")
include("modis/utilities.jl")
include("modis/examples.jl")

for model in [CMIP5_MODELS; CMIP6_MODELS]
symb = nameof(model)
@eval export $symb
end

end # module
198 changes: 94 additions & 104 deletions src/chelsa/future.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ layers(::Type{<:CHELSA{T}}) where T <:Future{BioClimPlus} = layers(T)
layerkeys(T::Type{<:CHELSA{<:Future{BioClim}}}, args...) = layerkeys(BioClim, args...)

layers(::Type{<:CHELSA{<:Future{Climate}}}) = (:prec, :temp, :tmin, :tmax)

date_step(::Type{<:CHELSA{<:Future{Climate,CMIP5}}}) = Year(20)
date_step(::Type{<:CHELSA{<:Future{Climate,CMIP6}}}) = Year(30)

# A modified key is used in the file name, while the key is used as-is in the path
const CHELSAKEY = (prec="pr", temp="tas", tmin="tasmin", tmax="tasmax", bio="bio")

date_step(::Type{<:CHELSA{<:Future{<:Any,CMIP5}}}) = Year(20)
date_range(::Type{<:CHELSA{<:Future{<:Any,CMIP5}}}) = (Date(2041), Date(2080))
date_step(::Type{<:CHELSA{<:Future{<:Any,CMIP6}}}) = Year(30)
date_range(::Type{<:CHELSA{<:Future{<:Any,CMIP6}}}) = (Date(1981), Date(2100))

"""
getraster(T::Type{CHELSA{Future{BioClim}}}, [layer]; date) => String
Expand Down Expand Up @@ -154,54 +155,56 @@ end
function _rastername(
::Type{CMIP5}, T::Type{<:CHELSA{<:Future{BioClim}}}, layer::Integer; date
)
date_string = _date_string(_phase(T), date)
mod = _format(CHELSA, _model(T))
scen = _format(CHELSA, _scenario(T))
date_string = _format(T, date)
mod = _format(T, _model(T))
scen = _format(T, _scenario(T))
return "CHELSA_bio_mon_$(mod)_$(scen)_r1i1p1_g025.nc_$(layer)_$(date_string)_V1.2.tif"
end
function _rastername(
::Type{CMIP5}, T::Type{<:CHELSA{<:Future{Climate}}}, layer::Symbol; date, month
)
date_string = _date_string(_phase(T), date)
mod = _format(CHELSA, _model(T))
scen = _format(CHELSA, _scenario(T))
date_string = _format(T, date)
mod = _format(T, _model(T))
scen = _format(T, _scenario(T))
key = CHELSAKEY[layer]
suffix = layer === :prec ? "" : "_V1.2" # prec filenames dont end in _V1.2
return "CHELSA_$(key)_mon_$(mod)_$(scen)_r1i1p1_g025.nc_$(month)_$(date_string)$(suffix).tif"
end
function _rastername(::Type{CMIP6}, T::Type{<:CHELSA{<:Future{BioClim}}}, layer::Integer; date)
date_string = _date_string(_phase(T), date)
mod = _format(CHELSA, _model(T))
scen = _format(CHELSA, _scenario(T))
date_string = _format(T, date)
mod = _format(T, _model(T))
scen = _format(T, _scenario(T))
return "CHELSA_bio$(layer)_$(date_string)_$(mod)_$(scen)_V.2.1.tif"
end
function _rastername(::Type{CMIP6}, T::Type{<:CHELSA{<:Future{BioClimPlus}}}, layer::Symbol; date)
date_string = _date_string(_phase(T), date)
mod = _format(CHELSA, _model(T))
scen = _format(CHELSA, _scenario(T))
date_string = _format(T, date)
mod = _format(T, _model(T))
scen = _format(T, _scenario(T))
return "CHELSA_$(layer)_$(date_string)_$(mod)_$(scen)_V.2.1.tif"
end
function _rastername(
::Type{CMIP6}, T::Type{<:CHELSA{<:Future{Climate}}}, layer::Symbol; date, month
)
# CMIP6 Climate uses an underscore in the date string, of course
date_string = replace(_date_string(_phase(T), date), "-" => "_")
mod = _format(CHELSA, _model(T))
scen = _format(CHELSA, _scenario(T))
date_string = replace(_format(T, date), "-" => "_")
mod = _format(T, _model(T))
scen = _format(T, _scenario(T))
key = CHELSAKEY[layer]
mon = lpad(month, 2, '0')
return "CHELSA_$(mod)_r1i1p1f1_w5e5_$(scen)_$(key)_$(mon)_$(date_string)_norm.tif"
end

function rasterpath(T::Type{<:CHELSA{<:Future}})
joinpath(rasterpath(CHELSA), "Future", string(_dataset(T)), string(_scenario(T)), string(_model(T)))
# drop dashes so paths are more (but not entirely!) compatibile with v0.6
modelname = replace(_format(_model(T)), "-" => "")
joinpath(rasterpath(CHELSA), "Future", _format(T, _dataset(T)), _format(_scenario(T)), modelname)
end
function rasterpath(T::Type{<:CHELSA{<:Future}}, layer; kw...)
joinpath(rasterpath(T), rastername(T, layer; kw...))
end

function rasterurl(T::Type{<:CHELSA{<:Future}}, layer; date, kw...)
date_str = _date_string(_phase(T), date)
date_str = _format(T, date)
key = _chelsa_layer(_dataset(T), layer)
path = _urlpath(_phase(T), T::Type{<:CHELSA{<:Future}}, key, date_str)
joinpath(rasterurl(CHELSA), path, rastername(T, layer; date, kw...))
Expand All @@ -216,99 +219,86 @@ function _urlpath(::Type{CMIP5}, T::Type{<:CHELSA{<:Future}}, name, date_str)
end
function _urlpath(::Type{CMIP6}, T::Type{<:CHELSA{<:Future}}, name, date_str)
# The model is in uppercase in the URL for CMIP6
mod = uppercase(_format(CHELSA, _model(T)))
scen = _format(CHELSA, _scenario(T))
mod = uppercase(_format(T, _model(T)))
scen = _format(T, _scenario(T))
key = CHELSAKEY[name]
return "chelsav2/GLOBAL/climatologies/$date_str/$mod/$scen/$key/"
end

function _date_string(::Type{CMIP5}, date)
if date < DateTime(2041)
_cmip5_date_error(date)
elseif date < DateTime(2061)
"2041-2060"
elseif date < DateTime(2081)
"2061-2080"
else
_cmip5_date_error(date)
end
end

function _date_string(::Type{CMIP6}, date)
if date < DateTime(1981)
_cmip6_date_error(date)
elseif date < DateTime(2011)
"1981-2010"
elseif date < DateTime(2041)
"2011-2040"
elseif date < DateTime(2071)
"2041-2070"
elseif date < DateTime(2101)
"2071-2100"
else
_cmip6_date_error(date)
end
end

_cmip5_date_error(date) = error("CMIP5 covers the period from 2041-2080, not including $date")
_cmip6_date_error(date) = error("CMIP6 covers the period from 1981-2100, not including $date")

_dataset(::Type{<:CHELSA{F}}) where F<:Future = _dataset(F)
_dataset(::Type{<:Future{BioClimPlus}}) = BioClim # to make sure bioclimplus and bioclim end up in the same folder
_phase(::Type{<:CHELSA{F}}) where F<:Future = _phase(F)
_model(::Type{<:CHELSA{F}}) where F<:Future = _model(F)
_scenario(::Type{<:CHELSA{F}}) where F<:Future = _scenario(F)

# Climate model string formatters for CHELSA Future
## overload _format to use lowercase
_format(::Type{<:CHELSA}, T::Type{<:SharedSocioeconomicPathway}) = lowercase(_format(T))
_format(::Type{<:CHELSA}, T::Type{<:RepresentativeConcentrationPathway}) = lowercase(_format(T))
_format(::Type{<:CHELSA{<:Future{<:Any, CMIP6}}}, T::Type{<:ClimateModel}) = lowercase(_format(T))

## Climate model string formatters for CHELSA Future
# CMIP5
_format(::Type{CHELSA}, ::Type{ACCESS1}) = "ACCESS1-0"
_format(::Type{CHELSA}, ::Type{BNUESM}) = "BNU-ESM"
_format(::Type{CHELSA}, ::Type{CCSM4}) = "CCSM4"
_format(::Type{CHELSA}, ::Type{CESM1BGC}) = "CESM1-BGC"
_format(::Type{CHELSA}, ::Type{CESM1CAM5}) = "CESM1-CAM5"
_format(::Type{CHELSA}, ::Type{CMCCCMS}) = "CMCC-CMS"
_format(::Type{CHELSA}, ::Type{CMCCCM}) = "CMCC-CM"
_format(::Type{CHELSA}, ::Type{CNRMCM5}) = "CNRM-CM5"
_format(::Type{CHELSA}, ::Type{CSIROMk3}) = "CSIRO-Mk3"
_format(::Type{CHELSA}, ::Type{CanESM2}) = "CanESM2"
_format(::Type{CHELSA}, ::Type{FGOALS}) = "FGOALS-g2"
_format(::Type{CHELSA}, ::Type{FIOESM}) = "FIO-ESM"
_format(::Type{CHELSA}, ::Type{GFDLCM3}) = "GFDL-CM3"
_format(::Type{CHELSA}, ::Type{GFDLESM2G}) = "GFDL-ESM2G"
_format(::Type{CHELSA}, ::Type{GFDLESM2M}) = "GFDL-ESM2M"
_format(::Type{CHELSA}, ::Type{GISSE2HCC}) = "GISS-E2-H-CC"
_format(::Type{CHELSA}, ::Type{GISSE2H}) = "GISS-E2-H"
_format(::Type{CHELSA}, ::Type{GISSE2RCC}) = "GISS-E2-R-CC"
_format(::Type{CHELSA}, ::Type{GISSE2R}) = "GISS-E2-R"
_format(::Type{CHELSA}, ::Type{HadGEM2AO}) = "HadGEM2-AO"
_format(::Type{CHELSA}, ::Type{HadGEM2CC}) = "HadGEM2-CC"
_format(::Type{CHELSA}, ::Type{IPSLCM5ALR}) = "IPSL-CM5A-LR"
_format(::Type{CHELSA}, ::Type{IPSLCM5AMR}) = "IPSL-CM5A-MR"
_format(::Type{CHELSA}, ::Type{MIROCESMCHEM}) = "MIROC-ESM-CHEM"
_format(::Type{CHELSA}, ::Type{MIROCESM}) = "MIROC-ESM"
_format(::Type{CHELSA}, ::Type{MIROC5}) = "MIROC5"
_format(::Type{CHELSA}, ::Type{MPIESMLR}) = "MPI-ESM-LR"
_format(::Type{CHELSA}, ::Type{MPIESMMR}) = "MPI-ESM-MR"
_format(::Type{CHELSA}, ::Type{MRICGCM3}) = "MRI-CGCM3"
_format(::Type{CHELSA}, ::Type{MRIESM1}) = "MRI-ESM1"
_format(::Type{CHELSA}, ::Type{NorESM1M}) = "NorESM1-M"
_format(::Type{CHELSA}, ::Type{BCCCSM1}) = "bcc-csm-1"
_format(::Type{CHELSA}, ::Type{Inmcm4}) = "inmcm4"
const CHELSA_CMIP5_MODELS = Type{<:ClimateModel{CMIP5}}[]
const CHELSA_CMIP5_MODEL_STRINGS =
[
"ACCESS1-0"
"BNU-ESM"
"CCSM4"
"CESM1-BGC"
"CESM1-CAM5"
"CMCC-CMS"
"CMCC-CM"
"CNRM-CM5"
"CSIRO-Mk3"
"CanESM2"
"FGOALS-g2"
"FIO-ESM"
"GFDL-CM3"
"GFDL-ESM2G"
"GFDL-ESM2M"
"GISS-E2-H-CC"
"GISS-E2-H"
"GISS-E2-R-CC"
"GISS-E2-R"
"HadGEM2-AO"
"HadGEM2-CC"
"IPSL-CM5A-LR"
"IPSL-CM5A-MR"
"MIROC-ESM-CHEM"
"MIROC-ESM"
"MIROC5"
"MPI-ESM-LR"
"MPI-ESM-MR"
"MRI-CGCM3"
"MRI-ESM1"
"NorESM1-M"
"bcc-csm-1"
"inmcm4"
]

# CMIP6
_format(::Type{CHELSA}, ::Type{GFDLESM4}) = "gfdl-esm4"
_format(::Type{CHELSA}, ::Type{IPSLCM6ALR}) = "ipsl-cm6a-lr"
_format(::Type{CHELSA}, ::Type{MPIESMHR}) = "mpi-esm1-2-hr"
_format(::Type{CHELSA}, ::Type{MRIESM2}) = "mri-esm2-0"
_format(::Type{CHELSA}, ::Type{UKESM}) = "ukesm1-0-ll"

# Format scenarios
_format(::Type{CHELSA}, ::Type{RCP26}) = "rcp26"
_format(::Type{CHELSA}, ::Type{RCP45}) = "rcp45"
_format(::Type{CHELSA}, ::Type{RCP60}) = "rcp60"
_format(::Type{CHELSA}, ::Type{RCP85}) = "rcp85"

_format(::Type{CHELSA}, ::Type{SSP126}) = "ssp126"
_format(::Type{CHELSA}, ::Type{SSP245}) = "ssp245"
_format(::Type{CHELSA}, ::Type{SSP370}) = "ssp370"
_format(::Type{CHELSA}, ::Type{SSP585}) = "ssp585"
const CHELSA_CMIP6_MODELS = Type{<:ClimateModel{CMIP6}}[]
const CHELSA_CMIP6_MODEL_STRINGS = [
"GFDL-ESM4"
"IPSL-CM6A-LR"
"MPI-ESM1-2-HR"
"MRI-ESM2-0"
"UKESM1-0-LL"
]

for CMIP in [:CMIP5, :CMIP6]
strings = eval(Symbol("CHELSA_$(CMIP)_MODEL_STRINGS"))
models = eval(Symbol("CHELSA_$(CMIP)_MODELS"))
for model_str in strings
type = Symbol(replace(model_str, "-" => "_"))
@eval begin
if !(@isdefined $type)
struct $type <: ClimateModel{$CMIP} end
export $type
end
push!($models, $type)
end
end
append!(eval(Symbol("$(CMIP)_MODELS")), models)
unique!(eval(Symbol("$(CMIP)_MODELS")))
end
31 changes: 31 additions & 0 deletions src/shared.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,32 @@ _date_sequence(step, dates::AbstractArray) = dates
_date_sequence(step, dates::NTuple{2}) = first(dates):step:last(dates)
_date_sequence(step, date) = date:step:date

function _format(T::Type{<:RasterDataSource}, date::TimeType)
daterange = date_range(T)
datestep = date_step(T)
# check if the date is within the range
if date < first(daterange) || date > last(daterange)
_date_error(date, daterange)
end

# find which bin it is in
r = range(daterange...; step = datestep)
idx = searchsortedfirst(r, date)

# from here on just use integer math
startyear = Dates.year(first(daterange))
yearstep = Dates.value(datestep)
startyear = startyear + yearstep * (idx - 2)
endyear = startyear + yearstep - 1
return "$startyear-$endyear"
end

function _date_error(date, daterange)
startyear = Dates.year(first(daterange))
endyear = Dates.year(last(daterange))
error("The requested dataset covers the period from $startyear-$endyear, which does not include $date")
end

function _maybe_download(uri::URI, filepath, headers = [])
if !isfile(filepath)
mkpath(dirname(filepath))
Expand Down Expand Up @@ -81,3 +107,8 @@ function _map_layers(T, layers, args...; kw...)
keys = layerkeys(T, layers)
return NamedTuple{keys}(filenames)
end

# fallback for _format
_format(::Type, T) = _format(T)
_format(T::Type) = string(nameof(T))
_format(M::Type{<:ClimateModel}) = replace(string(nameof(M)), "_" => "-")
Loading

0 comments on commit a4d36d1

Please sign in to comment.