Waste heat in Melbourne

Cities can be much hotter than surrounding regions. This is in part because of waste heat from our energy use. This post shows how much waste heat is emitted during a typical day into Melbourne, Australia.

Most of the electricity and other forms of energy we use in cities ends up as waste heat. Whether it’s from car engines, air-conditioners, or heat leaking from buildings in the winter, our energy intensive activities can measurably increase temperatures in cities.

Here I’m plotting the intensity of waste heat based on a global dataset produced by a group from the Tokyo Institute of Technology. Dong et al. describe the dataset in a recent paper here. It’s a fantastic tool as the dataset has high spatial resolution (30 arc-seconds or about 1km) for every hour and for each month, over the entire globe.

The animation below cycles through a typical day in February (summer) for the Melbourne metropolitan area. Also highlighted is Preston, a low/medium density suburb of Melbourne. It’s an area quite typical for Australian cities; one and two story buildings, primarily detached, so is useful to test how the new components of our urban climate model are performing.

A typical day in summer. A peak occurs in the mid-to-late afternoon from building cooling and traffic.

You can see much more waste heat is being emitted during work hours, and that denser areas of the city emit more heat. In summer, heat emitted during the day is pretty constant, with a slight peak between 4-5pm when air-conditioning, peak-hour traffic and general electricity use combine.

Winter waste heat has a different diurnal profile:

A typical day in Winter. The peaks in early morning and evening are from heating buildings.

For Preston which is dominated by residential areas, peaks occur in the morning and early evening when people are waking up, commuting, or arriving home and heating their homes. For a cooler climate city like Melbourne, on average more heat is emitted during the winter than in the summer, with a peak of about 23 $W/m^2$ for Preston in July.

The peak values in the centre of Melbourne are about 50 $W/m^2.$ This is still small compared to the world’s largest cities - for example the Hong Kong maximum intensity is 493 $W/m^2$ and New York has a peak of 297 $W/m^2$. Compare these values to the maximum summer midday sun in Australia at about 1000 $W/m^2$, and you can appreciate just how much extra energy our activities are pumping into the urban environment.

Plotting the data

I use python for data analysis. I had to learn some new techniques to plot the data and create the animations in this post.

To create the gif animation, I found the package imageio very easy to use.

I had a bit of difficulty arranging the two plots and the colorbar together, so ended up using a helper function make_axes_locatable from mpl_toolkits.axes_grid1. This simplified the process of associating the size of the colorbar with the top axes only.

I also had difficulty drawing the red zoom lines from one axes to the other until I came across the handy tool ConnectionPatch in matplotlib.patches.

As for the data, because of its high spatial and temporal resolution, the dataset is large, about 500 GB compressed. Instructions for download are here. Thanks to Alvin Varquez, one of the authors of the dataset, for asistance in seperating out the area around Melbourne.

The full plot code is available here.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import matplotlib.patches as patches
import matplotlib.colors as colors
import imageio
import sys

melbm = {}
preston=np.zeros((12,24))
months = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec']
hours = ['03','04','05','06','07','08','09','10','11','12','13','14',
            '15','16','17','18','19','20','21','22','23','00','01','02']

# set colormap colors
cmap=plt.get_cmap('jet')
cmap.set_bad(color='Lavender')
bounds=np.linspace(0, 50, 41)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=cmap.N)

plt.close('all')
for i, month in enumerate(months):
    animation = []
    # populate arrays
    for j, hour in enumerate(hours):
        filename = '%s_ahe_%s' %(month,hour)
        # load melbourne arrays
        melb = np.load('melbourne/%s.npy' %filename)
        melb = melb[10:90,10:90]

        preston[i,j] = melb[39:41,39:41].mean()
        melbm['%s_%s' %(month,hour)] = np.ma.masked_where(melb < 0, melb)
    # plot figure for month
    for j, hour in enumerate(hours):
        plt.close('all')
        fig, axes = plt.subplots(ncols=1,nrows=2, 
                gridspec_kw = {'height_ratios':[2, 1], 'width_ratios':[1,1]})
        # plot information
        im = axes[0].imshow(melbm['%s_%s' %(month,hour)], norm=norm,cmap=cmap, 
                vmin=0, vmax=50, interpolation='nearest')
        axes[1].plot([0.5+k for k in range(24)],preston[i,:])
        axes[1].scatter(j+0.5,preston[i,j],marker='s',color='r',s=10)
        # annotations
        axes[0].set_title('Waste heat emitted in Melbourne: %s %s:00' %(month,hour), 
                fontsize=11,y=1.05,x=0.42)
        axes[1].set_title('%s diurnal cycle in Preston' %month,fontsize=9,y=1.00,x=0.5)
        axes[0].add_patch(patches.Rectangle((39,39),2,2, edgecolor='red',fill=False))
        axes[0].annotate('Preston',xy=(40,40), xytext=(40,10), color='red', fontsize=8,
                ha='center',va='bottom', arrowprops={'arrowstyle':'->','color':'red'})
        axes[0].text(15, 74,'Port Phillip Bay', fontsize=8,color='black')
        axes[0].set_yticklabels([])
        axes[0].set_xticklabels([])
        # colour bar
        divider = make_axes_locatable(axes[0])
        cax = divider.append_axes("left",size="5%",pad=0.10)
        fig.colorbar(im,cax=cax,norm=norm,boundaries=bounds,ticks=[0,10,20,30,40,50])
        cax.yaxis.set_ticks_position('left')
        cax.set_ylabel(r'Waste heat $(W/m^2)$',labelpad=-52)
        # ticks and labels
        axes[1].set_ylabel(r'Waste heat $(W/m^2)$')
        axes[1].set_xlim(0,24)
        axes[1].set_xticks([0,3,6,9,12,15,18,21,24])
        axes[1].set_xticklabels([3,6,9,12,15,18,21,24,3])
        axes[1].set_xlabel('Hour')
        axes[1].set_ylim(0,25)
        axes[1].set_yticks([0,5,10,15,20,25])
        axes[1].grid(color='0.8')
        for spine in axes[1].spines.values():
            spine.set_edgecolor('red')
        # zoom line
        connect1 = patches.ConnectionPatch(xyA=(39,39), xyB=(0,25),coordsA='data', 
                coordsB='data', axesA=axes[0], axesB=axes[1],
                color='red',linestyle=':',linewidth=0.6,shrinkA=2,shrinkB=2)
        connect2 = patches.ConnectionPatch(xyA=(41,39), xyB=(24,25), coordsA='data', 
                coordsB='data', axesA=axes[0], axesB=axes[1],
                color='red',linestyle=':',linewidth=0.6,shrinkA=2,shrinkB=2)
        axes[0].add_artist(connect1)
        axes[0].add_artist(connect2)

        fig.subplots_adjust(hspace=0.05)
        fig.text(0.51,0.03, 'matlipson@gmail.com', size=5, ha='right')
        fig.savefig('plot/MelbourneQF_%s%s.png' %(month,hour), dpi=150, bbox_inches='tight')
        # add image for animation
        animation.append(imageio.imread('plot/MelbourneQF_%s%s.png' %(month,hour)))
    # save animation
    imageio.mimsave('plot/MelbourneQF_%s.gif' %(month), animation)
Share Comments
comments powered by Disqus