mirror of https://github.com/Askill/claude.git
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:
parent
72247c418d
commit
4a5aa8abbf
Binary file not shown.
119
toy_model.py
119
toy_model.py
|
|
@ -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):
|
||||
return (a[i,(j+1)%nlon]-a[i,(j-1)%nlon])/dx[i]
|
||||
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:
|
||||
return (a[i+1,j]-a[i-1,j])/dy
|
||||
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,:])):
|
||||
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 == 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()))
|
||||
|
|
|
|||
Loading…
Reference in New Issue