def move_thermal_boundary(r, T, Tcen, n_points): #Calculate movement of boundary by matching the gradients #s_new = move_bound2(Tcen,lb) ####################### #Match temperature # if T[-1]-adiabat(r[-1],Tcen) < 0: # s_new = r[-1] # r_new = np.ones(n_points)*r[-1] # T_new = np.ones(n_points)*adiabat(r[-1],Tcen) # else: # def f(guess,T,Tcen): # return T-adiabat(guess,Tcen) # # s_new = bisect(f,0,3480e3,args=(T[0],Tcen),maxiter=200) ####################### #Match gradient ##################### dT_dr = np.gradient(T, r[1] - r[0], edge_order=2) dT_dr_func = interp1d(r, dT_dr, kind='linear') s_new = move_bound2(Tcen, dT_dr[0]) if 1 == 1: ##################### T_rel = T - adiabat(r, Tcen) if s_new < r[0]: #Add grid points below layer #Append solution r, T_rel = expand_domain(r, s_new, T_rel) else: #Find point that is also sub-adiabatic grad_diff = np.gradient(T, r[1] - r[0], edge_order=2) - adiabat_grad(r, Tcen) grad_diff_func = interp1d(r, grad_diff, kind='linear') #If super-adiabatic everywhere if len(grad_diff[grad_diff > 0]) == 0: s_new = r[-1] #If super-adiabatic at the base of the lauer but still sub-adiabatic at the top, reduce to just sub-adiabatic region elif grad_diff[0] < 0 and grad_diff[-1] > 0: s_new = bisect(grad_diff_func, r[0], r[-1], maxiter=200) #Interpolate onto regular grid r_new = np.linspace(s_new, r[-1], n_points) T_rel = interp1d(r, T_rel, kind='linear')(r_new) T_new = T_rel + adiabat(r_new, Tcen) return r_new, T_new
def layer_growth(r, c, T, Tcen, dTa_dt, dc_int, dc_dr, dc_sl): if prm.compositional_stratification: s_new_c = r[0] + (dc_int - dc_sl) / dc_dr else: s_new_c = r[-1] if prm.thermal_stratification: ######## Match Gradients ####### T_rel = T - adiabat(r, Tcen) s_new_T = move_bound2(Tcen, r, T) else: T_rel = T - adiabat(r, Tcen) s_new_T = r[-1] s_new = np.min([s_new_c, s_new_T]) if s_new < r[0]: #Interface moves down #Append solution r, T_rel = append_thermal_solution(r, s_new, T_rel) T = T_rel + adiabat(r, Tcen) r, c = append_compositional_solution(r, s_new, c, dc_dr) #Interpolate solution onto regular grid r_new = np.linspace(s_new, prm_c.r_cmb, prm.n_points) T_new = interp1d(r, T, kind='linear')(r_new) c_new = interp1d(r, c, kind='linear')(r_new) else: #Interface moves down #Interpolate solution onto regular grid r_new = np.linspace(s_new, prm_c.r_cmb, prm.n_points) T_new = interp1d(r, T, kind='linear')(r_new) c_new = interp1d(r, c, kind='linear')(r_new) return r_new, T_new, c_new
def secular_cool(r, rho, Ta, M): #Values are normalised to the cooling rate. Tcen = Ta[0] Is = 4 * np.pi * integrate(r, rho * Ta * r**2) Qs = -prm.cp * Is / Tcen Ts = adiabat(r[-1], Tcen) Es = prm.cp * (M - Is / Ts) / Tcen return Qs, Es
def evolve_thermal_layer(r, T, Tcen, dT_dr_cmb, dt): ub = dT_dr_cmb #upper boundary condition (fixed gradient) lb = adiabat(r[0], Tcen) #lower boundary condition (fixed gradient) ub_type = 1 #upper boundary condition type (0=Fixed value 1=Fixed gradient) lb_type = 0 #upper boundary condition type (0=Fixed value 1=Fixed gradient) #Diffuse the solution and cool adiabat T_new = diffusion(T, r, dt, prm_c.kappa, lb_type, ub_type, lb, ub) return T_new
def ic_growth(r, Tcen, Tm): if adiabat(r[0], Tcen) > Tm[0]: ri = 0 elif adiabat(r[-1], Tcen) > Tm[-1]: dT = adiabat(r, Tcen) - Tm x1 = np.arange(dT.size)[dT > 0][0] - 1 x2 = np.arange(dT.size)[dT > 0][0] dx = dT[x1] / (dT[x1] - dT[x2]) ri = r[x1] + dx * (r[x2] - r[x1]) # f_Tm = interpolate.interp1d(r,Tm) # def f(r,Tcen,f_Tm): # return adiabat(r,Tcen)-f_Tm(r) # # ri = bisect(f,r[0],r[-1],args=(Tcen,f_Tm)) else: assert adiabat(r[-1], Tcen) >= Tm[-1], 'The whole core has frozen!' return ri
def expand_layer(ds, sl_model, Tcen): T = sl_model.T r = sl_model.r s_new = r[0] + ds Ta = adiabat(s_new, Tcen) r_insert = np.linspace(s_new, r[0], 10) T_insert = np.linspace(T[0], Ta, 10) n = np.ceil(1 + len(r) * ds / (r[-1] - r[0])) T = np.insert(T, 0, T_insert) r = np.insert(r, 0, r_insert) r_new = np.linspace(r[0], r[-1], n) T_new = np.interp(r_new, r, T) sl_model.r = r_new sl_model.T = T_new return sl_model
def initialise_layer(s, Tcen): r = np.linspace(s, prm.r_upper, prm.n_points) T = adiabat(r, Tcen) return r, T
def mantle_evolution(model, core_model=False): #Read in variables from model and parameters file time = model.time Tm = model.Tm #average mantle temperature r_upper = model.r_upper #upper radius r_lower = model.r_lower #lower radius nu_upper = model.nu_upper #viscosity in upper mantle nu_lower = model.nu_lower #viscosity in lower mantle k_upper = model.k_upper #thermal conductivity in upper mantle k_lower = model.k_lower #thermal conductivity in lower mantle Tsurf = model.Tsurf #Surface temperature Qr0 = model.Qr0 #Initial radiogenic heating Trad = model.Trad #radiogenic heating decay mass = model.mass #mantle mass alpha = model.alpha #volumetric expansion g = model.g #gravity kappa = model.kappa #thermal diffusivity cp = model.cp #specific heat capacity Rac = model.Rac #critical Rayleigh Number if core_model: from thermal_history.core.profiles import adiabat Tcen = core_model.Tcen Tcmb = adiabat(r_lower, Tcen) else: Tcmb = model.Tcmb_func(model) #Calculate boundary layers Ta_upper = Tm * 0.7 Ta_lower = Tm * 1.3 D = r_upper - r_lower #Mantle Thickness delta_upper = D * ((kappa * nu_upper * Rac) / ((D**3) * alpha * g * (Ta_upper - Tsurf)))**(1 / 3) delta_lower = D * ((kappa * nu_lower * Rac) / ((D**3) * alpha * g * (Tcmb - Ta_lower)))**(1 / 3) #Conductive heat flow through boundaries Qsurface = 4 * np.pi * r_upper**2 * k_upper * ( (Ta_upper - Tsurf) / delta_upper) Qcmb = 4 * np.pi * r_lower**2 * k_lower * (( (Tcmb - Ta_lower)) / delta_lower) #heat produced via radiogenic production Qr = Qr0 * np.exp(-time / Trad) #Calculating energy from mantle secular cooling Qs = Qsurface - Qcmb - Qr #Mantle cooling rate dTm_dt = -Qs / (mass * cp) #Save values to model class model.delta_upper = delta_upper model.delta_lower = delta_lower model.Qr = Qr model.Qs = Qs model.Qcmb = Qcmb model.Qsurface = Qsurface model.dT_dt = dTm_dt model.Tcmb = Tcmb return model
def f(guess, T, Tcen): return T - adiabat(guess, Tcen)
def sl_evolution(model, dt=1e6, core_model=False, mantle_model=False, debugging=False): from thermal_history.core.profiles import conductivity, density, adiabat, adiabat_grad from thermal_history.core.numerical import integrate from thermal_history.stable_layer.functions import evolve_thermal_layer, append_layer, diffusion, initialise_layer from scipy.optimize import bisect from scipy.interpolate import interp1d from scipy.special import erfcinv #Read in values from model time = model.time it = model.it ys = model.ys model.dt = dt * ys t_tot = model.dt r_cmb = model.r_upper k_cmb = conductivity(r_cmb) diffusivity_c = model.D_c[0] diffusivity_T = model.kappa alpha_T = model.alpha_T alpha_c = model.alpha_c[0] n_points = model.n_points tolerance = model.depth_tolerance if mantle_model: Qcmb = mantle_model.Qcmb else: Qcmb = model.Qcmb_func(time) if core_model: Tcen, dTa_dt = core_model.Tcen, core_model.dT_dt else: Tcen, dTa_dt = model.Ta_func(model) #Initialse values on iteration 0: if it == 0: model.r = np.ones(n_points) * r_cmb model.T = adiabat(model.r, Tcen) #for testing purposes #Qcmb = 0.99 * -adiabat_grad(r_cmb,Tcen)*k_cmb*4*np.pi*r_cmb**2 #Calculate one of 2 cases, thermal/chemical: if model.thermal_stratification: time_gone = 0 test = 0 while time_gone < t_tot: test = test + 1 T = model.T r = model.r s = r[0] #TESTING # if time < 3.5e9*ys: # Qcmb = 1.00001-(k_cmb*4*np.pi*r_cmb**2)*adiabat_grad(r_cmb,Tcen) # else: # Qcmb = 0.999*-(k_cmb*4*np.pi*r_cmb**2)*adiabat_grad(r_cmb,Tcen) #Calculate CMB temp gradient dT_dr_cmb = -Qcmb / (k_cmb * 4 * np.pi * r_cmb**2) adiabaticity = float(dT_dr_cmb / adiabat_grad(r_cmb, Tcen)) if adiabaticity >= 1 and s > tolerance: r_new = np.ones(n_points) * r_cmb T_new = adiabat(r_new, Tcen) s_new = r_new[0] ds_dt = (s_new - s) / t_tot time_gone = t_tot else: if s > tolerance: r, T = initialise_layer(tolerance, Tcen) s = r[0] #Variable dt dt = (1 / diffusivity_T) * ((r_cmb - s) / (2 * erfcinv(1e-10)))**2 if time_gone + dt > t_tot: dt = t_tot - time_gone elif dt < 1000 * ys: dt = 1000 * ys lb = adiabat_grad(r[0], Tcen) ub = dT_dr_cmb #Diffuse the solution and cool adiabat # if model.it == 881: # print(test) T_new = diffusion(T, r, dt, diffusivity_T, 1, 1, lb, ub, coord='sph') Tcen = Tcen + dTa_dt * dt assert T_new[0] < adiabat(0, Tcen), "Entire core is stratified" if T_new[0] < adiabat(r_cmb, Tcen): r_new = np.ones(n_points) * r_cmb T_new = adiabat(r_new, Tcen) s_new = r_new[0] ds_dt = (s_new - s) / dt else: #Calculate movement of boundary def f(guess, T, Tcen): return T - adiabat(guess, Tcen) try: s_new = bisect(f, 0, 3480e3, args=(T_new[0], Tcen), maxiter=200) except: pdb.set_trace() ds_dt = (s_new - s) / dt if dt == 0: pdb.set_trace() T_rel = T_new - adiabat(r, Tcen) #Append solution r, T_rel = append_layer(r, s_new, T_rel) T = T_rel + adiabat(r, Tcen) #Interpolate solution r_new = np.linspace(s_new, r_cmb, n_points) T_new = interp1d(r, T, kind='linear')(r_new) time_gone += dt model.r = r_new model.T = T_new #pdb.set_trace() # # # #Check if layer is thin and superadiabatic (destroy layer): # if r_s >= tolerance and adiabaticity >= adiabaticity_limit: # # # r_new = np.ones(n_points)*r_cmb # T_new = np.ones(n_points)*adiabat(r_cmb,Tcen) # Qs = 0 # ds_dt = 0 # # # #layer is thin and sub-adiabatic (initialise values): # elif r_s > tolerance and adiabaticity < adiabaticity_limit: # # r_new = np.linspace(tolerance,r_cmb,n_points) # T_new = adiabat(r_new,Tcen) # Qs = 0 # ds_dt = 0 # # #layer is thick and can be evolved # else: # # #Decide on time step # dt = 100*model.ys*(model.s_layer.layer_thickness/30) # # # r = model.s_layer.r # T = model.s_layer.T # # T_new = evolve_thermal_layer(r, T, Tcen, dT_dr_cmb, dt) # # dT_dt = (T_new-T)/dt # rho = density(r,o_rho) # # Qs = 4*np.pi*integrate(r,dT_dt*rho*cp*r**2) # # Tcen = Tcen + dTa_dt*dt # # r_new, T_new = move_thermal_boundary(r,T_new,Tcen,n_points) # # ds_dt = (r_new[0]-r[0])/dt # # if r_new[0] > tolerance: # model.adiabaticity_limit = adiabaticity # # # #Save those unique to thermal stratification # model.s_layer.Qs = Qs # model.s_layer.T = T_new if model.compositional_stratification: if it == 0: r = np.linspace(tolerance, r_cmb, n_points) c = np.ones(r.size) * conc_l else: r = model.r c = model.c #Barodiffusion # dmu_dc = prm_sl.dmu_dc # dg_dr = 10/3480e3 # g = 10 # a = diffusivity_c*alpha_c/dmu_dc #Gubbins/Davies table 1 # # #baro_cst = -( 2*g*a/r +a*dg_dr #ub = alpha_c*g/dmu_dc baro_cst = 0 ################### #Chemical Diffusion dT_dr = Qcmb / -(k_cmb * 4 * np.pi * r_s**2) super_adiabatic_grad = dT_dr - adiabat_grad(r_s, Tcen) #super_adiabatic_grad = -1/1000 bc_lower = 1 #lower boundary condition type bc_upper = 0 #upper boundary condition type ub = model.ub_func(model) #upper boundary condition lb = -(alpha_T / alpha_c) * super_adiabatic_grad #lower boundary condition if lb < 0: print('Can\'t have this boundary condition!!') pdb.set_trace() c_new = diffusion(c, r, dt, diffusivity_c, bc_lower, bc_upper, lb, ub, cst=baro_cst, coord='sph') dc_dt_sl = (c_new[0] - c[0]) / dt #Calculate movement of boundary ds_dt = (dc_dt - dc_dt_sl) / lb ds = ds_dt * dt s_new = r_s + ds if ds < 0: #Append solution r_append = np.linspace(s_new, r[0], 10)[:-1] c_append = np.ones(9) * conc_l r_appended = np.append(r_append, r) c_appended = np.append(c_append, c_new) r_new = np.linspace(s_new, r[-1], n_points) c_new = np.interp(r_new, r_appended, c_appended) else: r_new = np.linspace(s_new, r[-1], n_points) c_new = np.interp(r_new, r, c_new) #Save those unique to compositional stratification model.c = c_new if debugging: return dict([[key, value] for key, value in locals().items() if type(value) in (int, float, str, np.ndarray) and not key.startswith('_') ]) else: # pdb.set_trace() #Save to model model.Tcen = Tcen model.dTa_dt = dTa_dt model.Qcmb = Qcmb model.r_lower = s_new model.ds_dt = ds_dt model.r = r_new model.T = T_new model.adiabaticity = adiabaticity return model