City Configuration¶
URBADAPT-HEAT is designed to operate across any European city with a single city-specific YAML configuration file and a corresponding data manifest. The same analytical code runs everywhere — only the config changes.
Config file structure¶
City configs live in urban-heat/configs/<city>.yml. The YAML is divided into named sections, each consumed by the corresponding notebook stage. The same city-agnostic notebooks read whichever config is selected by the CITY environment variable. Below is an annotated representative excerpt (see rome.yml for a full example — real configs also carry inline calibration provenance comments):
```yaml
── Identity ────────────────────────────────────────────────¶
slug: rome city_name: Rome wp_iso3: ITA wp_country: Italy base_dir: data/Rome drive_manifest: data_manifests/rome_gdrive.json
── Demographic scenario ─────────────────────────────────────¶
Available: SSP1, SSP2, SSP3, SSP4, SSP5, SSP2DM, SSP2ZM¶
exp_scenario: SSP2
── Spatial layers ───────────────────────────────────────────¶
layers: fua: fua # Functional Urban Area boundary cap: cap # Capital/core boundary regions: regions # Sub-city policy regions muni: muni # Municipal boundaries
── Air conditioning module ──────────────────────────────────¶
ac: ssp: 2 years: [2020, 2030, 2040, 2050] penetration_file: ACgridded/ac_penetration_NUTSregions.csv kwh_file: ACgridded/ac_kwh_NUTSregions.csv nuts_id: ITI43 # NUTS3 region identifier (penetration) tariff_eur_per_kwh: 0.17 capex_per_user: 1498 # installed cost, PLI-adjusted maint_rate: 0.04 lifetime_years: 12 waste_heat: enabled: true lut_case_default: central # low / central / high dailymean_from_night_default: 0.50 dailymean_from_night_range: [0.33, 0.67] activation: # thermally-weighted activation of waste heat method: kou_cdd_share t_on_c: 18.0 t_full_c: 25.0 lut: # Salamanca 2014: penetration → nighttime ΔT (°C) 0.00: {low: 0.00, central: 0.00, high: 0.00} 0.35: {low: 0.25, central: 0.375, high: 0.50} 0.65: {low: 0.50, central: 0.750, high: 1.00} 1.00: {low: 1.00, central: 1.250, high: 1.50} cop_degradation: # Kou et al. 2026 enabled: true sensitivity_per_C: {low: 0.04, central: 0.065, high: 0.09} cop_ref: 3.0
── Trees / vegetation module ────────────────────────────────¶
CAPEX/O&M are city-specific (PLI-adjusted): Rome €680/€63, Athens €612/€57,¶
Lisbon €606/€56, Copenhagen €978/€91.¶
trees: capex_per_index_pt_eur: 10000000 # € per +1 GVI point capex_per_tree_eur: 680 om_per_tree_per_year_eur: 63 lifetime_years: 25 ramp_years: 12 # maturity ramp in years focus_year: 2050 veg_regions: regions # policy geography: regions | zones veg_emulator_bundle: "emulator/bundles/emulator_bundle_pooled_all_quadratic_holdout_safe.json"
── Vegetation → AC electricity co-benefit (Falchetta, De Cian & Lunghi 2026) ─¶
electricity_feedback: enabled: true pct_reduction_per_gvi_point: 0.008 # % AC electricity saved per GVI point summer_months: 3 co2_intensity_gCO2_per_kwh: 180
── EWS module ───────────────────────────────────────────────¶
ews: archetype: mature_hhwws operational_trigger_mode: epi_deaths_file warning_season: {mode: exact_dates, start_md: "05-01", end_md: "09-30"} threshold_method: epi_deaths_file # epi_deaths_file | event_mask target_activation_days: 23 # options: 15 / 23 / 30 threshold_recalib_interval: 5 # years; options: 3 / 5 / 7 interpretation: marginal # marginal | counterfactual efficacy_counterfactual: {low: 0.2, central: 0.35, high: 0.5} efficacy_marginal: # age-differentiated (sensitivity) "65+": {low: 0.08, central: 0.15, high: 0.25} ramp_years: 3 ac_overlap_factor: {low: 0.3, central: 0.5, high: 0.7} cost_model: pavanello # pavanello | chiabai
── Income (drives AC downscaling geography) ─────────────────¶
income: source: observed # observed | emulator (see cityheat.income_source) aggregation: weighted
zones: # geography at which income exists source: shapefile # shapefile | osm shapefile: "CAPZONE/CAPZONE.shp" regions: # coarser reporting geography (optional) enabled: true source: osm
── Vulnerability ─────────────────────────────────────────────¶
vulnerability: enabled: true weights: {thermal: 0.333, foreign_born: 0.333, unemployment: 0.333} files: ghs_age: "vulnerability/GHS_AGE_..._100_V1_0.tif" census_oth: "vulnerability/ESTAT_OBS-VALUE-OTH_2021_V2.tiff" census_emp: "vulnerability/ESTAT_OBS-VALUE-EMP_2021_V2.tiff" dynamic: enabled: true baseline_year: 2021 drmkc: {file: "vulnerability/drmkc_vulnerability_projection.csv", region_id: ITI43} gvi: {file: "vulnerability/gvi_projections.csv", country_code: ITA} k: {default: 0.85} phi: {default_2030: 0.92, default_2050: 0.75} thermal_projection: retrofit_rate_per_year: 0.0138 new_build_vulnerability: 0.15
── Climate / hazard ─────────────────────────────────────────¶
climate: t2m_clim_scen_default: CurPol # default scenario t2m_clim_scen_options: [CurPol, GS, SP, ssp585] t2m_baseline_mode_default: climatology_mean # climatology_mean | pixelwise_doy_max | # domain_peak_day | warmest_summer t2m_delta_pct_band_default: central # low | central | high (GCM spread) t2m_var: tas years: [2020, 2030, 2040, 2050] urbclim_api: # historical UrbClim download (NB01) enabled: true t2m_url: "https://provide.marvin.vito.be/ftp/compressed_daily/Rome/" years: [2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017]
── Impact-function efficacy (AC attenuation) ────────────────¶
efficacy: {"<15": 0.20, "15-64": 0.30, "65+": 0.40, default: 0.30}
── CBA ──────────────────────────────────────────────────────¶
cba: discount_rate: 0.03 horizon_years: 25 ```
Track A vs Track B (hazard track). Most cities run on the standard daily-mean hazard (Track A). Cool/maritime cities where daily-mean T2M carries little heat signal use the extreme-event (heatwave) track (Track B), enabled with an
extreme_hazard:block. NB08 reads the selected track viaHAZARD_TRACK. For Copenhagen:yaml extreme_hazard: enabled: true run_standard_track: true # kept for comparability (policies disabled) run_extreme_track: true # the policy-analysis track for this city standard_track: {name: standard_dailymean, run_policies: false} event_track: name: heatwave_extreme season: {mode: exact_dates, start_md: "05-15", end_md: "09-30"} threshold_percentile_default: 95 # options: 95 / 93 / 92 / 90 min_duration_default: 3 # options: 3 / 2 (days) intensity_method: exceedance t_ref_mode_default: local_p90 cold_city_if_variant: trueCopenhagen's EWS also setsnonpositive_threshold_fallback: event_maskso that when the deaths-based threshold is non-positive (too few heat deaths to calibrate), warnings fall back to the extreme event-day mask.
Data requirements per city¶
| Data layer | Source | Notes |
|---|---|---|
| UrbClim T2M (2008–2017) | PROVIDE/VITO API | ~100 m, EPSG:3035, daily NetCDF |
| GHS-FUA boundary | GHSL JRC | FUA identifier needed |
| WorldPop age-structured rasters (2020, 2030) | worldpop.org | Three age groups |
| GHS-AGE raster | GHSL JRC (2025 release) | 100 m, epoch codes 1–10 |
| EUROSTAT Census 2021 | Eurostat | Foreign-born share + employment rasters |
| Green View Index (GVI) | Huisman et al. (2025) dataset | 356-city dataset |
| Local Climate Zone (LCZ) raster | LCZ global map | lcz_filter_v3.tif |
| AC penetration (NUTS3) | National/regional statistics | City-level aggregate |
| Income data (sub-municipal) | National tax records | For logistic AC downscaling |
| CMIP6 warming deltas | PROVIDE climate service | Monthly, per scenario |
| WCDE demographics | Wittgenstein Centre | Age-specific, SSP-consistent |
| DRMKC trend indicators | Risk Data Hub | NUTS3, for SVI short-run projection |
Adding a new city¶
Because the notebooks are city-agnostic, adding a city is mostly a config + data task — no notebook editing is required.
- Identify the GHS-FUA boundary and NUTS3 code for your target city.
- Download all required data layers (see table above) and place them under the path given by
base_dir:(e.g.urban-heat/data/MyCity/). - Create a data manifest JSON file in
urban-heat/data_manifests/<city>_gdrive.jsonpointing to your Drive files, and reference it from the config'sdrive_manifest:key. - Copy an existing city YAML and adjust all parameters:
bash cp urban-heat/configs/rome.yml urban-heat/configs/mycity.yml - Point the template notebooks at your city by setting one line near the top of NB01:
python os.environ["CITY"] = "MyCity" # resolves configs/mycity.yml(Optionally, copytemplate/to a per-city folder if you want a pinned copy with theCITYline pre-set.) - Run NB01 (
01_setup_0126.ipynb) to verify paths and sync data. - Execute notebooks
02through08in order. - Optionally run
09_uncertaintyand10_summaryfor uncertainty quantification and reporting.
Note on Track B: For maritime or northern-latitude climates where daily-mean T2M carries limited heat signal, add an
extreme_hazard:block (see above) to activate the event-exceedance hazard representation and the event-mask EWS fallback. Copenhagen is the reference Track-B implementation. There is no top-leveltrack_b:flag — the track is controlled byextreme_hazardand read at runtime viaHAZARD_TRACK.
Configured cities¶
Over 40 European cities ship with a config and a Drive manifest, e.g. Amsterdam, Athens, Barcelona, Berlin, Bologna, Bratislava, Brussels, Bucharest, Budapest, Cologne, Copenhagen, Dublin, Genova, Hamburg, Helsinki, Lisbon, Ljubljana, Lyon, Madrid, Marseille, Milan, Munich, Nantes, Naples, Palermo, Paris, Porto, Prague, Riga, Rome, Rotterdam, Sevilla, Sofia, Stockholm, Tallinn, Thessaloniki, Varna, Vienna, Vilnius, Warsaw, Zagreb (see urban-heat/configs/).
Four cities are demonstrated end-to-end (full pipeline including uncertainty and summary):
| City | Config file | Country | Climate | Hazard track |
|---|---|---|---|---|
| Rome | rome.yml |
Italy | Hot-summer Mediterranean | A (standard) |
| Athens | athens.yml |
Greece | Hot-summer Mediterranean | A (standard) |
| Lisbon | lisbon.yml |
Portugal | Warm-summer Mediterranean | A (standard) |
| Copenhagen | copenhagen.yml |
Denmark | Oceanic | B (extreme-event) |
This page is maintained in the URBADAPT-HEAT wiki and synced automatically. Edit it there, not in the website repository.