Skip to content

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 via HAZARD_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: true Copenhagen's EWS also sets nonpositive_threshold_fallback: event_mask so 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.

  1. Identify the GHS-FUA boundary and NUTS3 code for your target city.
  2. Download all required data layers (see table above) and place them under the path given by base_dir: (e.g. urban-heat/data/MyCity/).
  3. Create a data manifest JSON file in urban-heat/data_manifests/<city>_gdrive.json pointing to your Drive files, and reference it from the config's drive_manifest: key.
  4. Copy an existing city YAML and adjust all parameters: bash cp urban-heat/configs/rome.yml urban-heat/configs/mycity.yml
  5. 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, copy template/ to a per-city folder if you want a pinned copy with the CITY line pre-set.)
  6. Run NB01 (01_setup_0126.ipynb) to verify paths and sync data.
  7. Execute notebooks 02 through 08 in order.
  8. Optionally run 09_uncertainty and 10_summary for 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-level track_b: flag — the track is controlled by extreme_hazard and read at runtime via HAZARD_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.