struggling to recreate urban heat islands in netlogo

Summary

This postmortem analyzes a failed attempt to model urban heat islands in NetLogo. The user aimed to simulate thermal buffering—where urban structures absorb more heat during the day and release it slowly at night—but the implementation resulted in unstable oscillations and unrealistic temperature spikes. The root cause was thermodynamic incoherence: the model ignored mass, energy conservation, and the heat capacity difference between urban surfaces and rural air. The attempted formulas mixed variables inconsistently (e.g., ptemp vs. c-u-air-temp), leading to runaway feedback loops.

Root Cause

The primary failure stems from violating physical conservation laws in the simulation logic:

  • Missing Heat Capacity Mechanism: The user attempted to use ΔT = Q / (m·c) but abandoned it, yet the code lacks any mass or specific heat capacity variables. held-heat is treated as a static buffer, not a function of urban patch mass.
  • Temperature vs. Heat Confusion: ptemp (patch temperature) and c-u-air-temp (city air temperature) are updated independently without ensuring energy balance. For example, ptemp = c-u-air-temp + held-heat violates the principle that temperature is intensive; it cannot be added to a heat value.
  • Unbounded Feedback Loops: In update-urban-air-temp, during the day, held-heat accumulates based on excess, which derives from comparitive-ptemp and divergence. This creates positive feedback: rising temperatures increase excess, further inflating ptemp, leading to exponential spikes.
  • Inconsistent Diffusion: diffuse-into-air calculates c-u-air-temp as a ratio (ptemp * c-u-air-temp) / base-air-temp, which lacks physical meaning. It doesn’t conserve energy and can produce non-physical values (e.g., negative or infinite temperatures).
  • Diurnal Cycle Misalignment: Nightly cooling subtracts a fixed value (-0.04) from air temperatures but fails to account for rural-urban thermal inertia. Urban patches should cool slower than rural ones, but the code uses the same loss rate for c-u-air-temp.

Key takeaway: Simulation models must respect energy conservation; arbitrary formulas without physical anchors (e.g., mass, specific heat) lead to chaotic behavior.

Why This Happens in Real Systems

Urban heat islands (UHI) emerge from physical properties of materials and geometry:

  • Thermal Inertia: Urban surfaces (concrete, asphalt) have higher thermal mass and lower albedo than rural vegetation/soil, absorbing more solar radiation (Q). They store heat via specific heat capacity (c) and release it slowly as temperature gradients reverse at night.
  • Radiative and Convective Transfer: During the day, net radiation heats surfaces; at night, longwave emission and convection cool them. UHI intensity peaks when rural air cools rapidly (low thermal inertia), but urban buffers delay urban cooling.
  • Diurnal Forcing: Solar input (Q) drives cycles; urban geometry reduces wind (convection), trapping heat. In models, if heat storage isn’t capped (e.g., by maximum capacity or dissipation), it diverges from reality.

In real systems, temperatures don’t “spike” infinitely; they plateau due to equilibrium. The code’s lack of damping (e.g., no radiative cooling proportional to T^4) exacerbates this.

Real-World Impact

If left uncorrected, such models misrepresent UHI dynamics, leading to flawed insights:

  • Policy Misguidance: Simulations showing runaway urban heating could overestimate heatwave risks, skewing urban planning (e.g., excessive green infrastructure spending without evidence of buffering).
  • Educational Outcomes: In coursework, incorrect models reinforce bad habits, like ignoring conservation laws, reducing credibility of student research.
  • Scalability Issues: Without proper heat capacity, multi-agent systems (e.g., integrating human behavior via workers) amplify errors, as agents react to unstable environments.
  • Broader Scientific Gaps: Unrealistic models hinder climate adaptation studies, where accurate UHI simulation informs zoning, insulation, and energy use predictions.

Bullet impacts:

  • Predictive Failure: Models may show urban areas cooling faster than rural, contradicting observed UHI persistence (urban temps 1-3°C higher at night).
  • Resource Waste: Debugging consumes time; without fixes, outputs are unusable for presentations or reports.
  • Cognitive Load: Non-coders (like the user) struggle with debugging invisible physics, leading to abandonment rather than learning.

Example or Code

The following corrected NetLogo code snippet demonstrates a simplified, physically grounded approach. It introduces thermal mass (as a patch variable thermal-mass) and specific heat capacity (c = 1.0 for simplicity, adjustable for materials). Heat absorption uses ΔT = Q / (m·c) implicitly: stored-heat accumulates Q and converts to temperature via temperature = stored-heat / (thermal-mass * c). Cooling is proportional to temperature gradient (Newton’s law of cooling). This prevents runaway by limiting updates and ensuring conservation.

patches-own [
  urban?            ; Boolean: true for urban
  ptemp             ; Patch surface temperature (K)
  stored-heat       ; Stored heat energy in J (proportional to m*c*ΔT)
  thermal-mass      ; Mass equivalent (kg, higher for urban)
  albedo            ; Reflectivity (0-1, lower for urban)
]

globals [
  day-timer
  day-length
  night-length
  day?
  c-air-temp        ; General ambient air temp
  c-u-air-temp      ; Urban air temp
  c-r-air-temp      ; Rural air temp
  solar-input       ; Solar flux (W/m²)
]

to setup
  ca
  reset-timer
  set day-length 100  ; Ticks for day
  set night-length 140
  set day-timer 0
  set day? true
  set solar-input 2.0 ; Initial solar flux
  set c-air-temp 298  ; 25°C baseline
  set c-u-air-temp 298
  set c-r-air-temp 298

  ask patches [
    set urban? (random-float 1.0 = day-length [
      set day-timer 0
      set day? false
    ]
  ]
  if not day? [
    nightly-temp-loss
    if day-timer >= night-length [
      set day-timer 0
      set day? true
      set days-passed days-passed + 1
      set solar-input solar-input + 0.01 ; Warming trend
    ]
  ]
  ask patches [ update-patch-temp ] ; Unified temp update
end

to daily-solar-input
  ; Solar input: Q = solar-input * (1 - albedo)
  ask patches [
    let Q solar-input * (1 - albedo)
    set stored-heat stored-heat + Q
  ]
  ; Urban air temp rises with average urban heat
  set c-u-air-temp mean [ptemp] of patches with [urban?]
  set c-r-air-temp mean [ptemp] of patches with [not urban?]
  set c-air-temp (c-u-air-temp + c-r-air-temp) / 2
end

to nightly-temp-loss
  ; Cooling: proportional to temp difference (Newton's law)
  ; dT/dt = -k * (T - T_env), with k ~0.05 for realism
  let k 0.05
  ask patches [
    set stored-heat max list 0 stored-heat - (k * (ptemp - c-air-temp) * thermal-mass)
  ]
  set c-u-air-temp max list 280 c-u-air-temp - 0.03 ; Fixed slight loss for stability
  set c-r-air-temp max list 280 c-r-air-temp - 0.05 ; Rural cools faster (lower inertia)
  set c-air-temp (c-u-air-temp + c-r-air-temp) / 2
end

to update-patch-temp
  ; Temperature from stored heat: T = Q / (m * c), c=1 for simplicity
  set ptemp (stored-heat / thermal-mass) + 273 ; Offset to Kelvin
  ; Clamp to realistic bounds (e.g., 280-320 K)
  set ptemp min list 320 max list 280 ptemp
  ; Diffusion: Simple average with neighbors (energy-conserving)
  let neighbors-temp mean [ptemp] of neighbors4
  if neighbors-temp > ptemp [
    set ptemp ptemp + (neighbors-temp - ptemp) * 0.1 ; Slow diffusion factor
  ]
end

This code initializes patches with thermal mass (urban ~2x rural). In daily-solar-input, heat accumulates based on absorption (Q * (1-albedo)). update-patch-temp converts stored heat to temperature (ensuring proportional scaling). Nightly cooling is gradient-based, preventing spikes. Run this in NetLogo; urban patches should show delayed cooling (e.g., urban air 2-3°C warmer at night).

How Senior Engineers Fix It

Senior engineers address UHI models by prioritizing physical fidelity and modular debugging:

  • Incorporate Thermodynamics: Explicitly define mass, specific heat (c), and albedo. Use ΔT = Q / (m·c) directly for updates: e.g., d-temp = Q-absorbed / (mass * c), clamped to prevent extremes. Add radiative cooling term: -k * σ * (T^4 - T_env^4) (Stefan-Boltzmann) for realism.
  • Ensure Conservation: Track total energy: sum(stored-heat) should be ~constant minus losses. Use energy partitioning (e.g., 70% to surface, 30% to air) to avoid direct T additions.
  • Damp Feedback Loops: Introduce limits: cap stored-heat at max-heat = m * c * ΔT_max. Use ODE solvers (e.g., Euler integration) for differential equations like dT/dt = (Q_in – Q_out)/ (m·c).
  • Validation Against Data: Calibrate with real UHI observations (e.g., 2-5°C nighttime delta). Test isolated components: run day-only mode to verify heat absorption.
  • Modularize Code: Separate solar input, conduction/diffusion, and cooling. Use procedures for each phase (e.g., absorb-heat, release-heat). Add logging for variables to trace oscillations.
  • Simulation Best Practices: Start with 1D models (single column) before 2D patches. Use repeat loops for explicit heat transfer (e.g., heat-flow = k * ΔT).

Result: Stable model where urban buffers cooling, with urban temps peaking later and higher than rural by 1-4°C, aligning with empirical UHI studies.

Why Juniors Miss It

Novices struggle because they abstract away physics without realizing consequences:

  • Focus on Syntax Over Semantics: They write formulas like ptemp = c-u-air-temp + held-heat as “clever hacks,” missing that temperature isn’t additive (it’s intensive). No background in thermodynamics leads to invalid equations.
  • Over-Simplification Without Justification: Abandoning ΔT = Q/(m·c) for fixed values (e.g., -0.04) ignores variability; juniors don’t grasp how mass scales heat retention.
  • Lack of Debugging Tools: No energy auditing—e.g., not checking sum(stored-heat). Oscillations are dismissed as “NetLogo quirks” rather than signs of non-conservation.
  • Ignored Scale Interactions: Forgetting patch-agent separation: air temps should smooth via diffusion, not direct patch assignment. Diurnal cycles are coded as simple switches, not forced by physics.
  • Inexperience with Validation: Without real-world benchmarks, they don’t question outputs. Juniors rarely test edge cases (e.g., no solar input → temperature should stabilize at ambient).

Key takeaway: Juniors treat code as procedural steps; seniors view it as a system of equations enforcing physical laws. To bridge: learn basic conservation (mass/energy/momentum) and always ask: “Does this preserve total heat?”