3D atmosphere!

Added multiple layers to the atmosphere. These have adjacent radiation exchange but not cross-level exchange, and no vertical momentum calculated. Also tweaked the absorption in different levels of the atmosphere. Differential sections of the code have been generalised for 3D fields
This commit is contained in:
Simon Clark 2020-07-01 21:18:16 +01:00
parent 72247c418d
commit 4a5aa8abbf
2 changed files with 84 additions and 35 deletions

BIN
save_file.p Normal file

Binary file not shown.

View File

@ -7,10 +7,12 @@ import numpy as np
import matplotlib.pyplot as plt
import time, sys, pickle
### CONTROL
### CONTROL ###
day = 60*60*24 # define length of day (used for calculating Coriolis as well) (s)
dt = 60*9 # <----- TIMESTEP (s)
resolution = 3 # how many degrees between latitude and longitude gridpoints
nlevels = 4 # how many vertical layers in the atmosphere
planet_radius = 6.4E6 # define the planet's radius (m)
insolation = 1370 # TOA radiation from star (W m^-2)
@ -18,7 +20,7 @@ advection = True # if you want to include advection set this to be True (cur
advection_boundary = 7 # how many gridpoints away from poles to apply advection
save = False
load = True
load = False
###########################
@ -28,14 +30,16 @@ lon = np.arange(0,360,resolution)
nlat = len(lat)
nlon = len(lon)
lon_plot, lat_plot = np.meshgrid(lon, lat)
heights = np.arange(0,100E3,100E3/nlevels)
# initialise arrays for various physical fields
temperature_planet = np.zeros((nlat,nlon)) + 270
temperature_atmosp = np.zeros_like(temperature_planet) + 270
air_pressure = np.zeros_like(temperature_planet)
u = np.zeros_like(temperature_planet)
v = np.zeros_like(temperature_planet)
air_density = np.zeros_like(temperature_planet) + 1.3
temperature_atmosp = np.zeros((nlat,nlon,nlevels)) + 270
air_pressure = np.zeros_like(temperature_atmosp)
u = np.zeros_like(temperature_atmosp)
v = np.zeros_like(temperature_atmosp)
w = np.zeros_like(temperature_atmosp)
air_density = np.zeros_like(temperature_atmosp) + 1.3
albedo = np.zeros_like(temperature_planet) + 0.5
heat_capacity_earth = np.zeros_like(temperature_planet) + 1E7
@ -54,7 +58,10 @@ for i in range(nlat):
# heat_capacity_earth[2:30,85:110] = 1E6
# define physical constants
epsilon = 0.75
epsilon = np.zeros(nlevels)
epsilon[0] = 0.75
for i in np.arange(1,nlevels):
epsilon[i] = epsilon[i-1]*0.5
heat_capacity_atmos = 1E7
specific_gas = 287
@ -75,35 +82,67 @@ angular_speed = 2*np.pi/day
for i in range(nlat):
dx[i] = dy*np.cos(lat[i]*np.pi/180)
coriolis[i] = angular_speed*np.sin(lat[i]*np.pi/180)
dz = np.zeros(nlevels)
for k in range(nlevels-1): dz[k] = heights[k+1] - heights[k]
dz[-1] = dz[-2]
###################### FUNCTIONS ######################
# define various useful differential functions:
# gradient of scalar field a in the local x direction at point i,j
def scalar_gradient_x(a,i,j):
def scalar_gradient_x(a,i,j,k=999):
if k == 999:
return (a[i,(j+1)%nlon]-a[i,(j-1)%nlon])/dx[i]
else:
return (a[i,(j+1)%nlon,k]-a[i,(j-1)%nlon,k])/dx[i]
# gradient of scalar field a in the local y direction at point i,j
def scalar_gradient_y(a,i,j):
if i == 0 or i == nlat-1:
return 0
def scalar_gradient_y(a,i,j,k=999):
if k == 999:
if i == 0:
return 2*(a[i+1,j]-a[i,j])/dy
elif i == nlat-1:
return 2*(a[i,j]-a[i-1,j])/dy
else:
return (a[i+1,j]-a[i-1,j])/dy
else:
if i == 0:
return 2*(a[i+1,j,k]-a[i,j,k])/dy
elif i == nlat-1:
return 2*(a[i,j,k]-a[i-1,j,k])/dy
else:
return (a[i+1,j,k]-a[i-1,j,k])/dy
# laplacian of scalar field a in the local x direction
def scalar_gradient_z(a,i,j,k):
if k == 0:
return (a[i,j,k+1]-a[i,j,k])/dz[k]
elif k == nlevels-1:
return (a[i,j,k]-a[i,j,k-1])/dz[k]
else:
return (a[i,j,k+1]-a[i,j,k-1])/(2*dz[k])
# laplacian of scalar field a
def laplacian(a):
output = np.zeros_like(a)
for i in np.arange(1,len(a[:,0])-1):
for j in range(len(a[0,:])):
if output.ndim == 2:
for i in np.arange(1,nlat-1):
for j in range(nlon):
output[i,j] = (scalar_gradient_x(a,i,(j+1)%nlon) - scalar_gradient_x(a,i,(j-1)%nlon)/dx[i]) + (scalar_gradient_y(a,i+1,j) - scalar_gradient_y(a,i-1,j))/dy
return output
if output.ndim == 3:
for i in np.arange(1,nlat-1):
for j in range(nlon):
for k in range(nlevels-1):
output[i,j,k] = (scalar_gradient_x(a,i,(j+1)%nlon,k) - scalar_gradient_x(a,i,(j-1)%nlon,k)/dx[i]) + (scalar_gradient_y(a,i+1,j,k) - scalar_gradient_y(a,i-1,j,k))/dy + (scalar_gradient_z(a,i,j,k+1)-scalar_gradient_z(a,i,j,k-1))/2*dz[k]
return output
# divergence of (a*u) where a is a scalar field and u is the atmospheric velocity field
def divergence_with_scalar(a):
output = np.zeros_like(a)
for i in range(len(a[:,0])):
for j in range(len(a[0,:])):
output[i,j] = scalar_gradient_x(a*u,i,j) + scalar_gradient_y(a*v,i,j)
for i in range(nlat):
for j in range(nlon):
for k in range(nlevels):
output[i,j] = scalar_gradient_x(a*u,i,j,k) + scalar_gradient_y(a*v,i,j,k) + scalar_gradient_z(a*w,i,j,k)
return output
# power incident on (lat,lon) at time t
@ -120,7 +159,7 @@ def solar(insolation, lat, lon, t):
f, ax = plt.subplots(2,figsize=(9,9))
f.canvas.set_window_title('CLAuDE')
ax[0].contourf(lon_plot, lat_plot, temperature_planet, cmap='seismic')
ax[1].contourf(lon_plot, lat_plot, temperature_atmosp, cmap='seismic')
ax[1].contourf(lon_plot, lat_plot, temperature_atmosp[:,:,0], cmap='seismic')
plt.subplots_adjust(left=0.1, right=0.75)
ax[0].set_title('Ground temperature')
ax[1].set_title('Atmosphere temperature')
@ -153,8 +192,14 @@ while True:
# calculate change in temperature of ground and atmosphere due to radiative imbalance
for i in range(nlat):
for j in range(nlon):
temperature_planet[i,j] += dt*((1-albedo[i,j])*solar(insolation,lat[i],lon[j],t) + epsilon*sigma*temperature_atmosp[i,j]**4 - sigma*temperature_planet[i,j]**4)/heat_capacity_earth[i,j]
temperature_atmosp[i,j] += dt*(epsilon*sigma*temperature_planet[i,j]**4 - 2*epsilon*sigma*temperature_atmosp[i,j]**4)/heat_capacity_atmos
temperature_planet[i,j] += dt*((1-albedo[i,j])*solar(insolation,lat[i],lon[j],t) + epsilon[0]*sigma*temperature_atmosp[i,j,0]**4 - sigma*temperature_planet[i,j]**4)/heat_capacity_earth[i,j]
for k in range(nlevels):
if k == 0:
temperature_atmosp[i,j,k] += dt*epsilon[k]*sigma*(temperature_planet[i,j]**4 + epsilon[k+1]*temperature_atmosp[i,j,k+1]**4 - 2*temperature_atmosp[i,j,k]**4)/(air_density[i,j,k]*heat_capacity_atmos)
elif k == nlevels-1:
temperature_atmosp[i,j,k] += dt*epsilon[k]*sigma*(epsilon[k-1]*temperature_atmosp[i,j,k-1]**4 - 2*temperature_atmosp[i,j,k]**4)/(air_density[i,j,k]*heat_capacity_atmos)
else:
temperature_atmosp[i,j,k] += dt*epsilon[k]*sigma*(epsilon[k+1]*temperature_atmosp[i,j,k+1]**4 + epsilon[k-1]*temperature_atmosp[i,j,k-1]**4 - 2*temperature_atmosp[i,j,k]**4)/(air_density[i,j,k]*heat_capacity_atmos)
# update air pressure
air_pressure = air_density*specific_gas*temperature_atmosp
@ -164,15 +209,19 @@ while True:
# introduce temporary arrays to update velocity in the atmosphere
u_temp = np.zeros_like(u)
v_temp = np.zeros_like(v)
w_temp = np.zeros_like(w)
# calculate acceleration of atmosphere using primitive equations on beta-plane
for i in np.arange(1,nlat-1):
for j in range(nlon):
u_temp[i,j] += dt*( -u[i,j]*scalar_gradient_x(u,i,j) - v[i,j]*scalar_gradient_y(u,i,j) + coriolis[i]*v[i,j] - scalar_gradient_x(air_pressure,i,j)/air_density[i,j] )
v_temp[i,j] += dt*( -u[i,j]*scalar_gradient_x(v,i,j) - v[i,j]*scalar_gradient_y(v,i,j) - coriolis[i]*u[i,j] - scalar_gradient_y(air_pressure,i,j)/air_density[i,j] )
for k in range(nlevels):
u_temp[i,j,k] += dt*( -u[i,j,k]*scalar_gradient_x(u,i,j,k) - v[i,j,k]*scalar_gradient_y(u,i,j,k) + coriolis[i]*v[i,j,k] - scalar_gradient_x(air_pressure,i,j,k)/air_density[i,j,k] )
v_temp[i,j,k] += dt*( -u[i,j,k]*scalar_gradient_x(v,i,j,k) - v[i,j,k]*scalar_gradient_y(v,i,j,k) - coriolis[i]*u[i,j,k] - scalar_gradient_y(air_pressure,i,j,k)/air_density[i,j,k] )
w_temp[i,j,k] += 0
u += u_temp
v += v_temp
w += w_temp
u *= 0.99
v *= 0.99
@ -180,15 +229,15 @@ while True:
if advection:
# allow for thermal advection in the atmosphere, and heat diffusion in the atmosphere and the ground
atmosp_addition = dt*(thermal_diffusivity_air*laplacian(temperature_atmosp) + divergence_with_scalar(temperature_atmosp))
temperature_atmosp[advection_boundary:-advection_boundary,:] -= atmosp_addition[advection_boundary:-advection_boundary,:]
temperature_atmosp[advection_boundary-1,:] -= 0.5*atmosp_addition[advection_boundary-1,:]
temperature_atmosp[-advection_boundary,:] -= 0.5*atmosp_addition[-advection_boundary,:]
temperature_atmosp[advection_boundary:-advection_boundary,:,:] -= atmosp_addition[advection_boundary:-advection_boundary,:,:]
temperature_atmosp[advection_boundary-1,:,:] -= 0.5*atmosp_addition[advection_boundary-1,:,:]
temperature_atmosp[-advection_boundary,:,:] -= 0.5*atmosp_addition[-advection_boundary,:,:]
# as density is now variable, allow for mass advection
density_addition = dt*divergence_with_scalar(air_density)
air_density[advection_boundary:-advection_boundary,:] -= density_addition[advection_boundary:-advection_boundary,:]
air_density[(advection_boundary-1),:] -= 0.5*density_addition[advection_boundary-1,:]
air_density[-advection_boundary,:] -= 0.5*density_addition[-advection_boundary,:]
air_density[advection_boundary:-advection_boundary,:,:] -= density_addition[advection_boundary:-advection_boundary,:,:]
air_density[(advection_boundary-1),:,:] -= 0.5*density_addition[advection_boundary-1,:,:]
air_density[-advection_boundary,:,:] -= 0.5*density_addition[-advection_boundary,:,:]
temperature_planet += dt*(thermal_diffusivity_roc*laplacian(temperature_planet))
@ -196,8 +245,8 @@ while True:
test = ax[0].contourf(lon_plot, lat_plot, temperature_planet, cmap='seismic')
ax[0].set_title('$\it{Ground} \quad \it{temperature}$')
ax[1].contourf(lon_plot, lat_plot, temperature_atmosp, cmap='seismic')
ax[1].streamplot(lon_plot,lat_plot,u,v,density=0.75,color='black')
ax[1].contourf(lon_plot, lat_plot, temperature_atmosp[:,:,0], cmap='seismic')
ax[1].streamplot(lon_plot,lat_plot,u[:,:,0],v[:,:,0],density=0.75,color='black')
ax[1].set_title('$\it{Atmospheric} \quad \it{temperature}$')
for i in ax:
i.set_xlim((lon.min(),lon.max()))