Periodic variations in seafloor temperature

We will explore how periodic variations in seafloor temperature affect the sub-seafloor temperature structure. For this we will learn about flux boundary conditions

[1]:
import numpy as np
from scipy.sparse.linalg import factorized
from scipy.sparse import diags, csc_matrix
import matplotlib.pyplot as plt
from matplotlib import animation
from IPython.display import HTML
import matplotlib as mpl
[2]:
# Physical parameters
L       = 30           # Length of modeled domain [m]
Ttop    = 4            # Temperature at seafloor [C]
kappa   = 1e-6         # Thermal diffusivity of rock [m2/s]
day     = 60*60*24     # seconds per day
hf      = 0.06          # basement heat flow in W/m2
k       = 1.5           # thermal conductivity of sediment
ampl    = 1             # surface temperature variation
zeval   = -5
r_time  = 10*365*day

# Numerical parameters
ny      = 101           # Number of gridpoints in y-direction
steps   = 301;
Time,dt    = np.linspace(0, r_time, steps,  retstep=True)
Yvec,dy = np.linspace(-L, 0, ny,  retstep=True)    # Y coordinate vector, constant spacing
[3]:
# Boundary and initial conditions
Tsurf   = Ttop + ampl*np.sin(2*np.pi/(365/2*day)*Time)
gradT   = -hf/k
T_init  = Ttop + gradT*Yvec
time    = 0
beta    = kappa*dt/dy**2
[4]:
# hide: the code in this cell is hidden by the author
/Users/lruepke/miniconda3/envs/py312_fem_class/lib/python3.12/site-packages/scipy/sparse/_index.py:210: SparseEfficiencyWarning: Changing the sparsity structure of a csc_matrix is expensive. lil and dok are more efficient.
  self._set_arrayXarray(i, j, x)
[5]:
# define diagonals
#lower = -beta * np.ones(ny - 1)
#diag  = (1 + 2*beta) * np.ones(ny)
#upper = -beta * np.ones(ny - 1)

# puzzle into sparse matrix
#A = diags([lower, diag, upper], offsets=[-1, 0, 1], format='csc')

# Boundary conditions
# constant temperature at seafloor
#A[-1, :] = ?; A[-1, -1] = ?

# constant gradient at the bottom
# ???

# factorize matrix A for fast solves
#solve_A = factorized(A)

print(A[0:6,0:6].toarray())
[[ 24.36 -23.36   0.     0.     0.     0.  ]
 [-11.68  24.36 -11.68   0.     0.     0.  ]
 [  0.   -11.68  24.36 -11.68   0.     0.  ]
 [  0.     0.   -11.68  24.36 -11.68   0.  ]
 [  0.     0.     0.   -11.68  24.36 -11.68]
 [  0.     0.     0.     0.   -11.68  24.36]]
[6]:
def fdm_solve(Rhs: np.ndarray) -> np.ndarray:
    # fast per-timestep solve
    return solve_A(Rhs)
[7]:
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure(figsize=(10,5))
ax = plt.axes(xlim=(Ttop-ampl-1, Ttop-L*gradT+1), ylim=(-L, 0))

# how many lines do you want to display
num_solutions       = 20
solution_list,lines = [],[]
for i in range(0,num_solutions):
    line, = ax.plot([], [], lw=1)
    lines.append(line)
timeLabel=ax.text(0.02,0.98,'Time: ',transform=ax.transAxes,va='top')
ax.set_xlabel('Temperature (°C)')
ax.set_ylabel('Depth (m)')

# Initialize Tnew
Tnew=T_init
# Animation function which updates figure data.  This is called sequentially
def animate(i):
    timeLabel._text='Time: %.1f day'%(i*dt/day)

    # use global keyword to store the latest solution and update it using fdm_solve function
    global Tnew
    Rhs = Tnew.copy()
    Rhs[0]= Rhs[0] + 2*beta*dy*hf/k;
    Rhs[-1]    = Tsurf[i];
    Tnew=fdm_solve(Rhs)
    # Update solution list and always keep latest solutions
    if(len(solution_list)<num_solutions):
        solution_list.append(Tnew)
    elif(len(solution_list)==num_solutions):
        solution_list.pop(0) #remove the oldest resolution
        solution_list.append(Tnew)
    # update lines
    for i in range(0,len(solution_list)):
        lines[i].set_data(solution_list[i], Yvec)
        lines[i].set_color('k')
        lines[i].set_alpha((num_solutions-i)/num_solutions)
# Call the animator.
anim = animation.FuncAnimation(fig, animate,frames=steps,interval=50)
plt.close(anim._fig)

# Call function to display the animation
#HTML(anim.to_html5_video())  # lower resolution
HTML(anim.to_jshtml())  # higher resolution
[7]: