├── Albedo_module.py ├── Atmo_frac_200_FIX_cold.npy ├── Atmo_frac_200_FIX_flat.npy ├── Baraffe3.txt ├── Baraffe_readme.txt ├── MC_calc.py ├── Main_code_callable.py ├── NEWNASA.TXT ├── NEWNASA_revised.TXT ├── NEWNASA_tiny.TXT ├── OLR_200_FIX_cold.npy ├── OLR_200_FIX_flat.npy ├── Plot_MC_output.py ├── README.txt ├── all_classes.py ├── carbon_cycle_model.py ├── escape_functions.py ├── fH2O_200_FIX_cold.npy ├── fH2O_200_FIX_flat.npy ├── failed_outputs3 └── test.txt ├── fugacityCoefficientVariables.txt ├── numba_nelder_mead.py ├── other_functions.py ├── outgassing_module.py ├── outgassing_module_fast.py ├── radiative_functions.py ├── sprons96_edited2.dat ├── stellar_funs.py ├── switch_garbage └── test.txt ├── thermodynamic_variables.py └── thermodynamics.py /Albedo_module.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def AB_fun(Tsurf,pH2O,volatile_mass,AL,AH): 4 | if pH2O/1e5<1: 5 | TA = 1000.0 6 | else: 7 | TA = 1000 + 200*np.log10(pH2O/1e5)**2 ## Puriel plot approximate 8 | 9 | AB_atmo = 0.5*(AL-AH) * np.tanh((TA-Tsurf)/400.0) + 0.5*(AH+AL) # pluriel 10 | return AB_atmo 11 | 12 | 13 | -------------------------------------------------------------------------------- /Atmo_frac_200_FIX_cold.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshuakt/Venus-evolution/3ea858061eb113cc8bd07f47829a2153d0fd37fa/Atmo_frac_200_FIX_cold.npy -------------------------------------------------------------------------------- /Atmo_frac_200_FIX_flat.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshuakt/Venus-evolution/3ea858061eb113cc8bd07f47829a2153d0fd37fa/Atmo_frac_200_FIX_flat.npy -------------------------------------------------------------------------------- /Baraffe_readme.txt: -------------------------------------------------------------------------------- 1 | 2 | The postcripts and Tables of the following papers are available: 3 | 4 | -------------------------------------------------------------------------------- 5 | - BARAFFE,CHABRIER, BARMAN, ALLARD, HAUSCHILDT, 2003, A&A, 402, 701 6 | 7 | "Evolutionary models for cool brown dwarfs and extrasolar giant 8 | planets. The case of HD 209458" 9 | 10 | Postscript file: - COND03.ps 11 | Isochrones: - COND03_models: isochrones (standard filters) 12 | 13 | -------------------------------------------------------------------------------- 14 | - CHABRIER, BARAFFE, ALLARD, HAUSCHILDT, 2000, ApJ, 542, 464 15 | 16 | "Evolutionary models for very-low-mass stars and 17 | brown dwarfs with dusty atmospheres" 18 | 19 | Postscript file: - DUSTY00.ps 20 | Isochrones: - DUSTY00_models: isochrones (standard filters) 21 | - DUSTY00_models.HSTfilters (WFPC2 22 | and NICMOS filters) 23 | 24 | -------------------------------------------------------------------------------- 25 | - BARAFFE, CHABRIER, ALLARD, HAUSCHILDT, 1998, A&A 337, 403: 26 | 27 | "Evolutionary models for solar metallicity low-mass stars: 28 | mass-magnitude relationships and color-magnitude diagrams" 29 | 30 | Postscript file: - BCAH98.ps 31 | 32 | Tables I-III: [M/H]=0 - BCAH98_models.1: tracks (L_mix= H_P) 33 | - BCAH98_models.2: tracks (L_mix=1.5 H_P) 34 | - BCAH98_models.3: tracks (L_mix=1.9 H_P) 35 | - BCAH98_iso.1,2.3: isochrones 36 | - BCAH98_iso.*HSTfilters (WFPC2 and 37 | NICMOS filters) 38 | 39 | 40 | [M/H]=-0.5 - BCAH98_models.mh05: tracks 41 | - BCAH98_iso.mh05: isochrones 42 | - BCAH98_models.mh05HSTfilters 43 | (WFPC2 and NICMOS filters) 44 | 45 | 46 | Note 1: The tables in BCAH98_models.1, .2 and .3 include more stellar masses 47 | and ages than published. L_mix=1.9 H_P is the value required to make a Sun. 48 | Below 0.6 Msun, models are insensitive to L_mix. 49 | 50 | Note 2: If you have special requests (non-standard filters, Li or D abundance, 51 | specific isochrones, etc...), please contact ibaraffe@ens-lyon.fr 52 | 53 | -------------------------------------------------------------------------------- 54 | - BARAFFE, CHABRIER, ALLARD, HAUSCHILDT, 1997, A&A 327, 1054: 55 | 56 | "Evolutionary Models for Metal-Poor Low-Mass Stars. Lower 57 | Main Sequence of Globular Clusters and Halo Field Stars" 58 | 59 | Postscript file: BCAH97.ps 60 | Tables II-V: BCAH97_models 61 | Tables II-V (extra) : BCAH97_models.HSTflight (models in the 62 | (HST WFPC2 flight system) 63 | 64 | Note: the mass 0.35 Ms has been added in the tables of BCAH97_models 65 | 66 | -------------------------------------------------------------------------------- 67 | 68 | - CHABRIER, BARAFFE, 1997, A&A 327, 1039 69 | 70 | "Structure and Evolution of Low-Mass Stars" 71 | 72 | Postscrip file: CB97.ps 73 | Tables 2-7: CB97_models 74 | Table 2 (extra): CB97_models.mh0 ([M/H]=0 with higher time 75 | resolution) 76 | 77 | -------------------------------------------------------------------------------- 78 | 79 | 80 | 81 | 82 | ***************************************************** 83 | For any problem, please contact: ibaraffe@ens-lyon.fr 84 | ***************************************************** -------------------------------------------------------------------------------- /MC_calc.py: -------------------------------------------------------------------------------- 1 | ##################### 2 | # load modules 3 | import time 4 | #model_run_time = time.time() 5 | #time.sleep(1400) 6 | import numpy as np 7 | import pylab 8 | from joblib import Parallel, delayed 9 | from all_classes import * 10 | from Main_code_callable import forward_model 11 | import sys 12 | import os 13 | import shutil 14 | import contextlib 15 | #################### 16 | 17 | num_runs = 720 # Number of forward model runs 18 | num_cores = 60 # For parallelization, check number of cores with multiprocessing.cpu_count() 19 | os.mkdir('switch_garbage3') 20 | 21 | #Choose planet 22 | #which_planet = "E" # Earth (Earth model is untested for this version of the code) 23 | which_planet = "V" # Venus 24 | 25 | if which_planet=="E": 26 | Earth_inputs = Switch_Inputs(print_switch = "n", speedup_flag = "n", start_speed=15e6 , fin_speed=100e6,heating_switch = 0,C_cycle_switch="y",Start_time=10e6) 27 | Earth_Numerics = Numerics(total_steps = 3 ,step0 = 50.0, step1=10000.0 , step2=1e6, step3=1e5, step4=-999, tfin0=Earth_inputs.Start_time+10000, tfin1=Earth_inputs.Start_time+10e6, tfin2=4.4e9, tfin3=4.5e9, tfin4 = -999) #standard model runs 28 | 29 | ## PARAMETER RANGES ## 30 | #initial volatile inventories 31 | init_water = 10**np.random.uniform(20,22,num_runs) 32 | init_CO2 = 10**np.random.uniform(20,22,num_runs) 33 | init_O = np.random.uniform(2e21,6e21,num_runs) 34 | 35 | #Weathering and ocean chemistry parameters 36 | Tefold = np.random.uniform(5,30,num_runs) 37 | alphaexp = np.random.uniform(0.1,0.5,num_runs) 38 | suplim_ar = 10**np.random.uniform(5,7,num_runs) 39 | ocean_Ca_ar = 10**np.random.uniform(-4,np.log10(0.3),num_runs) 40 | ocean_Omega_ar = np.random.uniform(1.0,10.0,num_runs) 41 | 42 | #impact parameters (not used) 43 | imp_coef = 10**np.random.uniform(11,14.5,num_runs) 44 | tdc = np.random.uniform(0.06,0.14,num_runs) 45 | 46 | #escape parameters 47 | mult_ar = 10**np.random.uniform(-2,2,num_runs) 48 | mix_epsilon_ar = np.random.uniform(0.0,1.0,num_runs) 49 | Epsilon_ar = np.random.uniform(0.01,0.3,num_runs) 50 | Tstrat_array = np.random.uniform(180.5,219.5,num_runs) 51 | 52 | #Albedo parameters 53 | Albedo_C_range = np.random.uniform(0.25,0.35,num_runs) 54 | Albedo_H_range = np.random.uniform(0.20,0.30,num_runs) 55 | for k in range(0,len(Albedo_C_range)): 56 | if Albedo_C_range[k] < Albedo_H_range[k]: 57 | Albedo_H_range[k] = Albedo_C_range[k]-1e-5 58 | 59 | #Stellar evolution parameters 60 | Omega_sun_ar = 10**np.random.uniform(np.log10(1.8),np.log10(45),num_runs) 61 | tsat_sun_ar = (2.9*Omega_sun_ar**1.14)/1000 62 | fsat_sun = 10**(-3.13) 63 | beta_sun_ar = 1.0/(0.35*np.log10(Omega_sun_ar) - 0.98) 64 | beta_sun_ar = 0.86*beta_sun_ar 65 | 66 | # Interior parameters 67 | offset_range = 10**np.random.uniform(1.0,3.0,num_runs) 68 | heatscale_ar = np.random.uniform(0.5,1.5,num_runs) 69 | K_over_U_ar = np.random.uniform(7000.0,15200,num_runs) 70 | Stag_trans_ar = -5+0*np.random.uniform(50e6,4e9,num_runs) 71 | 72 | #Oxidation parameters 73 | MFrac_hydrated_ar = 10**np.random.uniform(np.log10(0.001),np.log10(0.03),num_runs) 74 | dry_ox_frac_ac = 10**np.random.uniform(-4,-1,num_runs) 75 | wet_oxid_eff_ar = 10**np.random.uniform(-3,-1,num_runs) 76 | Mantle_H2O_max_ar = 10**np.random.uniform(np.log10(0.5),np.log10(15.0),num_runs) 77 | surface_magma_frac_array = 10**np.random.uniform(-4,0,num_runs) 78 | 79 | elif which_planet=="V": 80 | Venus_inputs = Switch_Inputs(print_switch = "n", speedup_flag = "n", start_speed=15e6 , fin_speed=100e6,heating_switch = 0,C_cycle_switch="y",Start_time=30e6) 81 | Venus_Numerics = Numerics(total_steps = 2 ,step0 = 50.0, step1=10000.0 , step2=1e6, step3=-999, step4=-999, tfin0=Venus_inputs.Start_time+10000, tfin1=Venus_inputs.Start_time+30e6, tfin2=4.5e9, tfin3=-999, tfin4 = -999) 82 | 83 | ## PARAMETER RANGES ## 84 | #initial volatile inventories 85 | init_water = 10**np.random.uniform(20,22,num_runs) 86 | init_water = 10**np.random.uniform(19.5,21.5,num_runs) 87 | init_CO2 = 10**np.random.uniform(20.4,21.5,num_runs) 88 | init_O = np.random.uniform(2e21,6e21,num_runs) 89 | ##init_O = np.random.uniform(0.5e21,1.5e21,num_runs) #reducing mantle sensitivity test 90 | #init_O = np.random.uniform(0.3e21,1.5e21,num_runs) #extra reducing mantle sensitivity test 91 | 92 | #Weathering and ocean chemistry parameters 93 | Tefold = np.random.uniform(5,30,num_runs) 94 | alphaexp = np.random.uniform(0.1,0.5,num_runs) 95 | suplim_ar = 10**np.random.uniform(5,7,num_runs) 96 | ocean_Ca_ar = 10**np.random.uniform(-4,np.log10(0.3),num_runs) 97 | ocean_Omega_ar = np.random.uniform(1.0,10.0,num_runs) 98 | 99 | #impact parameters (not used) 100 | imp_coef = 10**np.random.uniform(11,14.5,num_runs) 101 | tdc = np.random.uniform(0.06,0.14,num_runs) 102 | 103 | #escape parameters 104 | mult_ar = 10**np.random.uniform(-2,2,num_runs) 105 | mix_epsilon_ar = np.random.uniform(0.0,1.0,num_runs) 106 | Epsilon_ar = np.random.uniform(0.01,0.3,num_runs) 107 | Tstrat_array = np.random.uniform(180.5,219.5,num_runs) 108 | 109 | #Albedo parameters 110 | Albedo_C_range = np.random.uniform(0.2,0.7,num_runs) 111 | Albedo_H_range = np.random.uniform(0.0001,0.3,num_runs) 112 | for k in range(0,len(Albedo_C_range)): 113 | if Albedo_C_range[k] < Albedo_H_range[k]: 114 | Albedo_H_range[k] = Albedo_C_range[k]-1e-5 115 | 116 | #Stellar evolution parameters 117 | Omega_sun_ar = 10**np.random.uniform(np.log10(1.8),np.log10(45),num_runs) 118 | tsat_sun_ar = (2.9*Omega_sun_ar**1.14)/1000 119 | fsat_sun = 10**(-3.13) 120 | beta_sun_ar = 1.0/(0.35*np.log10(Omega_sun_ar) - 0.98) 121 | beta_sun_ar = 0.86*beta_sun_ar 122 | 123 | # Interior parameters 124 | offset_range = 10**np.random.uniform(1.0,3.0,num_runs) 125 | heatscale_ar = np.random.uniform(0.5,2.0,num_runs) 126 | K_over_U_ar = np.random.uniform(6000.0,8440,num_runs) 127 | Stag_trans_ar = np.random.uniform(50e6,4e9,num_runs) 128 | 129 | #Oxidation parameters 130 | MFrac_hydrated_ar = 10**np.random.uniform(np.log10(0.001),np.log10(0.03),num_runs) 131 | dry_ox_frac_ac = 10**np.random.uniform(-4,-1,num_runs) 132 | wet_oxid_eff_ar = 10**np.random.uniform(-3,-1,num_runs) 133 | Mantle_H2O_max_ar = 10**np.random.uniform(np.log10(0.5),np.log10(15.0),num_runs) 134 | surface_magma_frac_array = 10**np.random.uniform(-4,0,num_runs) 135 | 136 | ##Output arrays and parameter inputs to be filled: 137 | output = [] 138 | inputs = range(0,len(init_water)) 139 | 140 | for zzz in inputs: 141 | ii = zzz 142 | 143 | if which_planet=="E": 144 | Earth_Planet_inputs = Planet_inputs(RE = 1.0, ME = 1.0, rc=3.4e6, pm=4000.0, Total_Fe_mol_fraction = 0.06, Planet_sep=1.0, albedoC=Albedo_C_range[ii], albedoH=Albedo_H_range[ii]) 145 | Earth_Init_conditions = Init_conditions(Init_solid_H2O=0.0, Init_fluid_H2O=init_water[ii] , Init_solid_O=0.0, Init_fluid_O=init_O[ii],Init_solid_FeO1_5 = 0.0, Init_solid_FeO=0.0, Init_solid_CO2=0.0, Init_fluid_CO2 = init_CO2[ii]) 146 | Sun_Stellar_inputs = Stellar_inputs(tsat_XUV=tsat_sun_ar[ii], Stellar_Mass=1.0, fsat=fsat_sun, beta0=beta_sun_ar[ii], epsilon=Epsilon_ar[ii] ) 147 | MC_inputs_ar = MC_inputs(esc_a=imp_coef[ii], esc_b=tdc[ii], esc_c = mult_ar[ii],esc_d = mix_epsilon_ar[ii],ccycle_a=Tefold[ii] , ccycle_b=alphaexp[ii], supp_lim =suplim_ar[ii], interiora =offset_range[ii], interiorb=MFrac_hydrated_ar[ii],interiorc=dry_ox_frac_ac[ii],interiord = wet_oxid_eff_ar[ii],interiore = heatscale_ar[ii], interiorf = Mantle_H2O_max_ar[ii],interiorg = Stag_trans_ar[ii],ocean_a=ocean_Ca_ar[ii],ocean_b=ocean_Omega_ar[ii],K_over_U = K_over_U_ar[ii]) 148 | inputs_for_later = [Earth_inputs,Earth_Planet_inputs,Earth_Init_conditions,Earth_Numerics,Sun_Stellar_inputs,MC_inputs_ar] 149 | 150 | elif which_planet=="V": 151 | Venus_Planet_inputs = Planet_inputs(RE = 0.9499, ME = 0.8149, rc=3.11e6, pm=3500.0, Total_Fe_mol_fraction = 0.06, Planet_sep=0.7,albedoC=Albedo_C_range[ii], albedoH=Albedo_H_range[ii]) 152 | Venus_Init_conditions = Init_conditions(Init_solid_H2O=0.0, Init_fluid_H2O=init_water[ii] , Init_solid_O=0.0, Init_fluid_O=init_O[ii], Init_solid_FeO1_5 = 0.0, Init_solid_FeO=0.0, Init_solid_CO2=0.0, Init_fluid_CO2 = init_CO2[ii]) 153 | Sun_Stellar_inputs = Stellar_inputs(tsat_XUV=tsat_sun_ar[ii], Stellar_Mass=1.0, fsat=fsat_sun, beta0=beta_sun_ar[ii], epsilon=Epsilon_ar[ii] ) 154 | MC_inputs_ar = MC_inputs(esc_a=imp_coef[ii], esc_b=tdc[ii], esc_c = mult_ar[ii], esc_d = mix_epsilon_ar[ii],ccycle_a=Tefold[ii] , ccycle_b=alphaexp[ii], supp_lim = suplim_ar[ii], interiora =offset_range[ii], interiorb=MFrac_hydrated_ar[ii],interiorc=dry_ox_frac_ac[ii],interiord = wet_oxid_eff_ar[ii],interiore = heatscale_ar[ii], interiorf = Mantle_H2O_max_ar[ii], interiorg = Stag_trans_ar[ii], ocean_a=ocean_Ca_ar[ii],ocean_b=ocean_Omega_ar[ii],K_over_U = K_over_U_ar[ii],Tstrat=Tstrat_array[ii],surface_magma_frac=surface_magma_frac_array[ii]) 155 | inputs_for_later = [Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar] 156 | 157 | sve_name = 'switch_garbage3/inputs4L%d' %ii 158 | np.save(sve_name,inputs_for_later) 159 | 160 | 161 | def processInput(i): 162 | load_name = 'switch_garbage3/inputs4L%d.npy' %i 163 | try: 164 | if which_planet=="E": 165 | print ('starting ',i) 166 | max_time_attempt = 1.5 167 | [Earth_inputs,Earth_Planet_inputs,Earth_Init_conditions,Earth_Numerics,Sun_Stellar_inputs,MC_inputs_ar] = np.load(load_name,allow_pickle=True) 168 | outs = forward_model(Earth_inputs,Earth_Planet_inputs,Earth_Init_conditions,Earth_Numerics,Sun_Stellar_inputs,MC_inputs_ar,max_time_attempt) 169 | 170 | elif which_planet =="V": 171 | print ('starting ',i) 172 | max_time_attempt = 1.5 173 | [Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar] = np.load(load_name,allow_pickle=True) 174 | outs = forward_model(Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar,max_time_attempt) 175 | 176 | 177 | except: 178 | print ('try again here') # try again with slightly different numerical options 179 | try: 180 | [Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar] = np.load(load_name,allow_pickle=True) 181 | Venus_Numerics.total_steps = 7 182 | max_time_attempt = 0.7 183 | outs = forward_model(Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar,max_time_attempt) 184 | if outs.total_time[-1] < 4e9: 185 | print ("Not enough time!") 186 | raise Exception 187 | except: 188 | print ('Third attempt') # try again with slightly different numerical options 189 | try: 190 | [Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar] = np.load(load_name,allow_pickle=True) 191 | Venus_Numerics.total_steps = 8 192 | max_time_attempt = 0.2 193 | Venus_Init_conditions.Init_fluid_H2O = np.random.uniform(0.98,1.02)*Venus_Init_conditions.Init_fluid_H2O 194 | Venus_Init_conditions.Init_fluid_CO2 = np.random.uniform(0.98,1.02)*Venus_Init_conditions.Init_fluid_CO2 195 | outs = forward_model(Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar,max_time_attempt) 196 | if outs.total_time[-1] < 4e9: 197 | print ("Not enough time!") 198 | raise Exception 199 | except: 200 | print ('Fourth attempt') # try again with slightly different numerical options 201 | try: 202 | [Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar] = np.load(load_name,allow_pickle=True) 203 | max_time_attempt = 0.1 204 | Venus_Numerics.total_steps = 9 205 | Venus_Init_conditions.Init_fluid_H2O = np.random.uniform(0.98,1.02)*Venus_Init_conditions.Init_fluid_H2O 206 | Venus_Init_conditions.Init_fluid_CO2 = np.random.uniform(0.98,1.02)*Venus_Init_conditions.Init_fluid_CO2 207 | outs = forward_model(Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar,max_time_attempt) 208 | if outs.total_time[-1] < 4e9: 209 | raise Exception 210 | except: 211 | print() 212 | print() 213 | print ('didint work ',i) 214 | outs = [] 215 | fail_name = 'failed_outputs3/%d' %i 216 | np.save(fail_name,[Venus_inputs,Venus_Planet_inputs,Venus_Init_conditions,Venus_Numerics,Sun_Stellar_inputs,MC_inputs_ar]) 217 | 218 | 219 | print ('done with ',i) 220 | return outs 221 | 222 | Everything = Parallel(n_jobs=num_cores)(delayed(processInput)(i) for i in inputs) #Run parallelized code 223 | input_mega=[] # Collect input parameters for saving 224 | for kj in range(0,len(inputs)): 225 | print ('saving garbage',kj) 226 | load_name = 'switch_garbage3/inputs4L%d.npy' %kj 227 | input_mega.append(np.load(load_name,allow_pickle=True)) 228 | 229 | np.save('Venus_ouputs_revisions',Everything) 230 | np.save('Venus_inputs_revisions',input_mega) 231 | 232 | shutil.rmtree('switch_garbage3') 233 | 234 | -------------------------------------------------------------------------------- /NEWNASA_tiny.TXT: -------------------------------------------------------------------------------- 1 | 2 | H2 Ref-Elm. Gurvich,1978 pt1 p103 pt2 p31. 3 | 3 tpis78 H 2.00 0.00 0.00 0.00 0.00 0 2.0158800 0.000 4 | 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8468.102 5 | 4.078323210D+04-8.009186040D+02 8.214702010D+00-1.269714457D-02 1.753605076D-05 6 | -1.202860270D-08 3.368093490D-12 0.000000000D+00 2.682484665D+03-3.043788844D+01 7 | 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8468.102 8 | 5.608128010D+05-8.371504740D+02 2.975364532D+00 1.252249124D-03-3.740716190D-07 9 | 5.936625200D-11-3.606994100D-15 0.000000000D+00 5.339824410D+03-2.202774769D+00 10 | 6000.000 20000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8468.102 11 | 4.966884120D+08-3.147547149D+05 7.984121880D+01-8.414789210D-03 4.753248350D-07 12 | -1.371873492D-11 1.605461756D-16 0.000000000D+00 2.488433516D+06-6.695728110D+02 13 | 14 | H2O Hf:Cox,1989. Woolley,1987. TRC(10/88) tuv25. 15 | 2 g 8/89 H 2.00O 1.00 0.00 0.00 0.00 0 18.0152800 -241826.000 16 | 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9904.092 17 | -3.947960830D+04 5.755731020D+02 9.317826530D-01 7.222712860D-03-7.342557370D-06 18 | 4.955043490D-09-1.336933246D-12 0.000000000D+00-3.303974310D+04 1.724205775D+01 19 | 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9904.092 20 | 1.034972096D+06-2.412698562D+03 4.646110780D+00 2.291998307D-03-6.836830480D-07 21 | 9.426468930D-11-4.822380530D-15 0.000000000D+00-1.384286509D+04-7.978148510D+00 22 | 23 | CH4 Gurvich,1991 pt1 p44 pt2 p36. 24 | 2 g 8/99 C 1.00H 4.00 0.00 0.00 0.00 0 16.0424600 -74600.000 25 | 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 10016.202 26 | -1.766850998D+05 2.786181020D+03-1.202577850D+01 3.917619290D-02-3.619054430D-05 27 | 2.026853043D-08-4.976705490D-12 0.000000000D+00-2.331314360D+04 8.904322750D+01 28 | 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 10016.202 29 | 3.730042760D+06-1.383501485D+04 2.049107091D+01-1.961974759D-03 4.727313040D-07 30 | -3.728814690D-11 1.623737207D-15 0.000000000D+00 7.532066910D+04-1.219124889D+02 31 | 32 | CO Gurvich,1979 pt1 p25 pt2 p29. 33 | 3 tpis79 C 1.00O 1.00 0.00 0.00 0.00 0 28.0101000 -110535.196 34 | 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8671.104 35 | 1.489045326D+04-2.922285939D+02 5.724527170D+00-8.176235030D-03 1.456903469D-05 36 | -1.087746302D-08 3.027941827D-12 0.000000000D+00-1.303131878D+04-7.859241350D+00 37 | 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8671.104 38 | 4.619197250D+05-1.944704863D+03 5.916714180D+00-5.664282830D-04 1.398814540D-07 39 | -1.787680361D-11 9.620935570D-16 0.000000000D+00-2.466261084D+03-1.387413108D+01 40 | 6000.000 20000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8671.104 41 | 8.868662960D+08-7.500377840D+05 2.495474979D+02-3.956351100D-02 3.297772080D-06 42 | -1.318409933D-10 1.998937948D-15 0.000000000D+00 5.701421130D+06-2.060704786D+03 43 | 44 | CO2 Gurvich,1991 pt1 p27 pt2 p24. 45 | 3 g 9/99 C 1.00O 2.00 0.00 0.00 0.00 0 44.0095000 -393510.000 46 | 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9365.469 47 | 4.943650540D+04-6.264116010D+02 5.301725240D+00 2.503813816D-03-2.127308728D-07 48 | -7.689988780D-10 2.849677801D-13 0.000000000D+00-4.528198460D+04-7.048279440D+00 49 | 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9365.469 50 | 1.176962419D+05-1.788791477D+03 8.291523190D+00-9.223156780D-05 4.863676880D-09 51 | -1.891053312D-12 6.330036590D-16 0.000000000D+00-3.908350590D+04-2.652669281D+01 52 | 6000.000 20000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9365.469 53 | -1.544423287D+09 1.016847056D+06-2.561405230D+02 3.369401080D-02-2.181184337D-06 54 | 6.991420840D-11-8.842351500D-16 0.000000000D+00-8.043214510D+06 2.254177493D+03 55 | 56 | O2 Ref-Elm. Gurvich,1989 pt1 p94 pt2 p9. 57 | 3 tpis89 O 2.00 0.00 0.00 0.00 0.00 0 31.9988000 0.000 58 | 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8680.104 59 | -3.425563420D+04 4.847000970D+02 1.119010961D+00 4.293889240D-03-6.836300520D-07 60 | -2.023372700D-09 1.039040018D-12 0.000000000D+00-3.391454870D+03 1.849699470D+01 61 | 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8680.104 62 | -1.037939022D+06 2.344830282D+03 1.819732036D+00 1.267847582D-03-2.188067988D-07 63 | 2.053719572D-11-8.193467050D-16 0.000000000D+00-1.689010929D+04 1.738716506D+01 64 | 6000.000 20000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8680.104 65 | 4.975294300D+08-2.866106874D+05 6.690352250D+01-6.169959020D-03 3.016396027D-07 66 | -7.421416600D-12 7.278175770D-17 0.000000000D+00 2.293554027D+06-5.530621610D+02 67 | 68 | SO2 Gurvich,1989 pt1 p288 pt2 p175. 69 | 2 tpis89 S 1.00O 2.00 0.00 0.00 0.00 0 64.0638000 -296810.000 70 | 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 10548.127 71 | -5.310842140D+04 9.090311670D+02-2.356891244D+00 2.204449885D-02-2.510781471D-05 72 | 1.446300484D-08-3.369070940D-12 0.000000000D+00-4.113752080D+04 4.045512519D+01 73 | 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 10548.127 74 | -1.127640116D+05-8.252261380D+02 7.616178630D+00-1.999327610D-04 5.655631430D-08 75 | -5.454316610D-12 2.918294102D-16 0.000000000D+00-3.351308690D+04-1.655776085D+01 76 | 77 | H2S Gurvich,1989 pt1 p298 pt2 p181. 78 | 2 g 4/01 H 2.00S 1.00 0.00 0.00 0.00 0 34.0808800 -20600.000 79 | 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9958.421 80 | 9.543808810D+03-6.875175080D+01 4.054921960D+00-3.014557336D-04 3.768497750D-06 81 | -2.239358925D-09 3.086859108D-13 0.000000000D+00-3.278457280D+03 1.415194691D+00 82 | 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9958.421 83 | 1.430040220D+06-5.284028650D+03 1.016182124D+01-9.703849960D-04 2.154003405D-07 84 | -2.169695700D-11 9.318163070D-16 0.000000000D+00 2.908696214D+04-4.349160391D+01 85 | 86 | H2O_L Liquid. Cox,1989. Haar,1984. Keenan,1984. Stimson,1969. 87 | 2 g 8/01 H 2.00O 1.00 0.00 0.00 0.00 2 18.0152800 -285830.000 88 | 273.150 373.1507 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 13278.000 89 | 1.326371304D+09-2.448295388D+07 1.879428776D+05-7.678995050D+02 1.761556813D+00 90 | -2.151167128D-03 1.092570813D-06 0.000000000D+00 1.101760476D+08-9.779700970D+05 91 | 373.150 600.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 13278.000 92 | 1.263631001D+09-1.680380249D+07 9.278234790D+04-2.722373950D+02 4.479243760D-01 93 | -3.919397430D-04 1.425743266D-07 0.000000000D+00 8.113176880D+07-5.134418080D+05 94 | -------------------------------------------------------------------------------- /OLR_200_FIX_cold.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshuakt/Venus-evolution/3ea858061eb113cc8bd07f47829a2153d0fd37fa/OLR_200_FIX_cold.npy -------------------------------------------------------------------------------- /OLR_200_FIX_flat.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshuakt/Venus-evolution/3ea858061eb113cc8bd07f47829a2153d0fd37fa/OLR_200_FIX_flat.npy -------------------------------------------------------------------------------- /Plot_MC_output.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pylab 3 | from scipy.integrate import * 4 | from scipy.interpolate import interp1d 5 | from scipy import optimize 6 | import scipy.optimize 7 | from all_classes import * 8 | import time 9 | from other_functions import * 10 | import pdb 11 | 12 | new_inputs = [] #actually ouputs 13 | inputs_for_MC=[] 14 | Total_Fe_array = [] 15 | 16 | global g,rp,mp 17 | 18 | def use_one_output(inputs,MCinputs): 19 | k= 0 20 | global g,rp,mp 21 | while k 4.4e9): 23 | top = (inputs[k].total_y[13][0] + inputs[k].total_y[12][0]) 24 | bottom = (inputs[k].total_y[1][0] + inputs[k].total_y[0][0] ) 25 | CO2_H2O_ratio = top/bottom 26 | 27 | ## Choose which outputs to plot: 28 | 29 | if (2>1): #All model outputs 30 | 31 | #if (inputs[k].total_y[4][-1] <1e15)and(np.max(inputs[k].Ocean_depth[-1])==0)and(np.max(inputs[k].Ocean_depth[:])>0)and (inputs[k].total_y[1][-1] <2e16)and(inputs[k].total_y[12][-1]>2e20)and(inputs[k].total_y[12][-1]<1e21): # Recover modern Venus, transient habitable time (reproduces Fig. 3 in main text) 32 | 33 | #if (inputs[k].total_y[4][-1] <1e15)and(np.max(inputs[k].Ocean_depth[:])==0)and (inputs[k].total_y[1][-1] <2e16)and(inputs[k].total_y[12][-1]>2e20)and(inputs[k].total_y[12][-1]<1e21): # Recover modern Venus, never habitable (reproduces Fig. 2 in main text) 34 | 35 | #if (inputs[k].total_y[4][-1] <1e15)and(np.max(inputs[k].Ocean_depth[-1])==0)and(inputs[k].total_y[1][-1] <2e16)and(inputs[k].total_y[12][-1]>2e20): #Recover modern Venus 36 | 37 | 38 | rp = 6371000*MCinputs[k][1].RE 39 | mp = 5.972e24*MCinputs[k][1].ME 40 | g = 6.67e-11*mp/(rp**2) 41 | Pbase = (inputs[k].Pressre_H2O[-1] + inputs[k].CO2_Pressure_array[-1] + inputs[k].total_y[22][-1]) 42 | Mvolatiles = (Pbase*4*np.pi*rp**2)/g 43 | 44 | new_inputs.append(inputs[k]) 45 | inputs_for_MC.append(MCinputs[k]) 46 | Total_Fe_array.append(MCinputs[k][1].Total_Fe_mol_fraction) 47 | rp = 6371000*MCinputs[k][1].RE 48 | mp = 5.972e24*MCinputs[k][1].ME 49 | g = 6.67e-11*mp/(rp**2) 50 | 51 | k= k+1 52 | 53 | N2_Pressure = 1e5 # Do not change 54 | 55 | ### Load outputs and inputs. Note it is possible to load multiple output files and process them all at once 56 | inputs = np.load('Venus_ouputs_revisions.npy',allow_pickle = True) 57 | MCinputs = np.load('Venus_inputs_revisions.npy',allow_pickle = True) 58 | use_one_output(inputs,MCinputs) 59 | #inputs = np.load('Venus_ouputs_revisions3.npy',allow_pickle = True) 60 | #MCinputs = np.load('Venus_inputs_revisions3.npy',allow_pickle = True) 61 | #use_one_output(inputs,MCinputs) 62 | 63 | #Pause here to check number of successful model runs etc. Type 'c' to continue. 64 | pdb.set_trace() 65 | inputs = np.array(new_inputs) 66 | 67 | def interpolate_class(saved_outputs): 68 | outs=[] 69 | for i in range(0,len(saved_outputs)): 70 | 71 | time_starts = np.min([np.where(saved_outputs[i].total_time>1)])-1 72 | time = saved_outputs[i].total_time[time_starts:] 73 | num_t_elements = 1000 74 | new_time = np.logspace(np.log10(np.min(time[:])),np.max([np.log10(time[:-1])]),num_t_elements) 75 | num_y = 56 76 | new_total_y = np.zeros(shape=(num_y,len(new_time))) 77 | for k in range(0,num_y): 78 | f1 = interp1d(time,saved_outputs[i].total_y[k][time_starts:]) 79 | new_total_y[k] = f1(new_time) 80 | 81 | f1 = interp1d(time,saved_outputs[i].FH2O_array[time_starts:]) 82 | new_FH2O_array = f1(new_time) 83 | 84 | f1 = interp1d(time,saved_outputs[i].FCO2_array[time_starts:]) 85 | new_FCO2_array = f1(new_time) 86 | 87 | f1 = interp1d(time,saved_outputs[i].MH2O_liq[time_starts:]) 88 | new_MH2O_liq = f1(new_time) 89 | 90 | f1 = interp1d(time,saved_outputs[i].MH2O_crystal[time_starts:]) 91 | new_MH2O_crystal = f1(new_time) 92 | 93 | f1 = interp1d(time,saved_outputs[i].MCO2_liq[time_starts:]) 94 | new_MCO2_liq = f1(new_time) 95 | 96 | f1 = interp1d(time,saved_outputs[i].Pressre_H2O[time_starts:]) 97 | new_Pressre_H2O = f1(new_time) 98 | 99 | f1 = interp1d(time,saved_outputs[i].CO2_Pressure_array[time_starts:]) 100 | new_CO2_Pressure_array = f1(new_time) 101 | 102 | f1 = interp1d(time,saved_outputs[i].fO2_array[time_starts:]) 103 | new_fO2_array = f1(new_time) 104 | 105 | f1 = interp1d(time,saved_outputs[i].Mass_O_atm[time_starts:]) 106 | new_Mass_O_atm = f1(new_time) 107 | 108 | f1 = interp1d(time,saved_outputs[i].Mass_O_atm[time_starts:]) 109 | new_Mass_O_atm = f1(new_time) 110 | 111 | f1 = interp1d(time,saved_outputs[i].Mass_O_dissolved[time_starts:]) 112 | new_Mass_O_dissolved = f1(new_time) 113 | 114 | f1 = interp1d(time,saved_outputs[i].water_frac[time_starts:]) 115 | new_water_frac = f1(new_time) 116 | 117 | f1 = interp1d(time,saved_outputs[i].Ocean_depth[time_starts:]) 118 | new_Ocean_depth = f1(new_time) 119 | 120 | f1 = interp1d(time,saved_outputs[i].Max_depth[time_starts:]) 121 | new_Max_depth = f1(new_time) 122 | 123 | f1 = interp1d(time,saved_outputs[i].Ocean_fraction[time_starts:]) 124 | new_Ocean_fraction = f1(new_time) 125 | 126 | output_class = Model_outputs(new_time,new_total_y,new_FH2O_array,new_FCO2_array,new_MH2O_liq,new_MH2O_crystal,new_MCO2_liq,new_Pressre_H2O,new_CO2_Pressure_array,new_fO2_array,new_Mass_O_atm,new_Mass_O_dissolved,new_water_frac,new_Ocean_depth,new_Max_depth,new_Ocean_fraction) 127 | outs.append(output_class) 128 | 129 | return outs 130 | 131 | 132 | 133 | interp_outputs = interpolate_class(inputs) 134 | inputs = interp_outputs 135 | 136 | ################################################################################ 137 | ## Post-processing of outputs: 138 | # Oxygen fugacity and mantle redox functions (for post-processing) 139 | def buffer_fO2(T,Press,redox_buffer): # T in K, P in bar 140 | if redox_buffer == 'FMQ': 141 | [A,B,C] = [25738.0, 9.0, 0.092] 142 | elif redox_buffer == 'IW': 143 | [A,B,C] = [27215 ,6.57 ,0.0552] 144 | elif redox_buffer == 'MH': 145 | [A,B,C] = [25700.6,14.558,0.019] 146 | else: 147 | print ('error, no such redox buffer') 148 | return -999 149 | return 10**(-A/T + B + C*(Press-1)/T) 150 | 151 | def get_fO2(XFe2O3_over_XFeO,P,T,Total_Fe): ## Total_Fe is a mole fraction of iron minerals XFeO + XFeO1.5 = Total_Fe, and XFe2O3 = 0.5*XFeO1.5, xo XFeO + 2XFe2O3 = Total_Fe 152 | XAl2O3 = 0.022423 153 | XCaO = 0.0335 154 | XNa2O = 0.0024 155 | XK2O = 0.0001077 156 | terms1 = 11492.0/T - 6.675 - 2.243*XAl2O3 157 | terms2 = 3.201*XCaO + 5.854 * XNa2O 158 | terms3 = 6.215*XK2O - 3.36 * (1 - 1673.0/T - np.log(T/1673.0)) 159 | terms4 = -7.01e-7 * P/T - 1.54e-10 * P * (T - 1673)/T + 3.85e-17 * P**2 / T 160 | fO2 = np.exp( (np.log(XFe2O3_over_XFeO) + 1.828 * Total_Fe -(terms1+terms2+terms3+terms4) )/0.196) 161 | return fO2 162 | 163 | total_time = [] 164 | total_y = [] 165 | FH2O_array= [] 166 | FCO2_array= [] 167 | MH2O_liq = [] 168 | MCO2_liq = [] 169 | Pressre_H2O = [] 170 | CO2_Pressure_array = [] 171 | fO2_array = [] 172 | Mass_O_atm = [] 173 | Mass_O_dissolved = [] 174 | water_frac = [] 175 | Ocean_depth = [] 176 | Max_depth = [] 177 | Ocean_fraction = [] 178 | 179 | for k in range(0,len(inputs)): 180 | total_time.append( inputs[k].total_time ) 181 | total_y.append(inputs[k].total_y) 182 | FH2O_array.append(inputs[k].FH2O_array ) 183 | FCO2_array.append(inputs[k].FCO2_array ) 184 | MH2O_liq.append(inputs[k].MH2O_liq ) 185 | MCO2_liq.append(inputs[k].MCO2_liq ) 186 | Pressre_H2O.append(inputs[k].Pressre_H2O) 187 | CO2_Pressure_array.append(inputs[k].CO2_Pressure_array ) 188 | fO2_array.append(inputs[k].fO2_array ) 189 | Mass_O_atm.append(inputs[k].Mass_O_atm ) 190 | Mass_O_dissolved.append(inputs[k].Mass_O_dissolved ) 191 | water_frac.append(inputs[k].water_frac ) 192 | Ocean_depth.append(inputs[k].Ocean_depth ) 193 | Max_depth.append(inputs[k].Max_depth ) 194 | Ocean_fraction.append(inputs[k].Ocean_fraction ) 195 | 196 | [int1,int2,int3] = [2.5,50,97.5] 197 | 198 | import scipy.stats 199 | confidence_y=scipy.stats.scoreatpercentile(total_y ,[int1,int2,int3], interpolation_method='fraction',axis=0) 200 | confidence_FH2O = scipy.stats.scoreatpercentile(FH2O_array ,[int1,int2,int3], interpolation_method='fraction',axis=0) 201 | confidence_FCO2 = scipy.stats.scoreatpercentile(FCO2_array ,[int1,int2,int3], interpolation_method='fraction',axis=0) 202 | confidence_MH2O_liq = scipy.stats.scoreatpercentile(MH2O_liq ,[int1,int2,int3], interpolation_method='fraction',axis=0) 203 | confidence_MCO2_liq = scipy.stats.scoreatpercentile(MCO2_liq ,[int1,int2,int3], interpolation_method='fraction',axis=0) 204 | confidence_Pressre_H2O = scipy.stats.scoreatpercentile(Pressre_H2O ,[int1,int2,int3], interpolation_method='fraction',axis=0) 205 | confidence_CO2_Pressure_array = scipy.stats.scoreatpercentile(CO2_Pressure_array ,[int1,int2,int3], interpolation_method='fraction',axis=0) 206 | confidence_fO2_array = scipy.stats.scoreatpercentile(fO2_array ,[int1,int2,int3], interpolation_method='fraction',axis=0) 207 | confidence_Mass_O_atm = scipy.stats.scoreatpercentile(Mass_O_atm ,[int1,int2,int3], interpolation_method='fraction',axis=0) 208 | confidence_Mass_O_dissolved = scipy.stats.scoreatpercentile(Mass_O_dissolved ,[int1,int2,int3], interpolation_method='fraction',axis=0) 209 | confidence_water_frac = scipy.stats.scoreatpercentile(water_frac ,[int1,int2,int3], interpolation_method='fraction',axis=0) 210 | confidence_Ocean_depth = scipy.stats.scoreatpercentile(Ocean_depth ,[5,50,95], interpolation_method='fraction',axis=0) 211 | confidence_Max_depth = scipy.stats.scoreatpercentile(Max_depth ,[int1,int2,int3], interpolation_method='fraction',axis=0) 212 | confidence_Ocean_fraction = scipy.stats.scoreatpercentile(Ocean_fraction ,[int1,int2,int3], interpolation_method='fraction',axis=0) 213 | 214 | f_O2_FMQ = [] 215 | f_O2_IW = [] 216 | f_O2_MH = [] 217 | f_O2_mantle = [] 218 | iron_ratio = [] 219 | total_iron = [] 220 | iron2_array = [] 221 | iron3_array = [] 222 | actual_phi_surf_melt_ar = [] 223 | XH2O_melt = [] 224 | XCO2_melt = [] 225 | Max_runaway_temperatures = [] 226 | 227 | rc = MCinputs[0][1].rc 228 | mantle_mass = 0.0 229 | 230 | x_low = 1.0 #Start time for plotting (years) 231 | x_high =np.max(total_time[0]) #Finish time for plotting (years) 232 | 233 | rp = 6371000*MCinputs[0][1].RE 234 | ll = rp - rc 235 | alpha = 2e-5 236 | k = 4.2 237 | kappa = 1e-6 238 | Racr = 1.1e3 239 | 240 | Melt_volume = np.copy(total_time) 241 | Plate_velocity = np.copy(total_time) 242 | total_Ar40 = np.copy(total_time) 243 | total_K40 = np.copy(total_time) 244 | HTmin = [] 245 | HTmax = [] 246 | HT_duration = [] 247 | 248 | DH_atmo = np.copy(total_time) 249 | DH_solid = np.copy(total_time) 250 | 251 | F_H2O_new = [] 252 | F_CO2_new = [] 253 | F_CO_new = [] 254 | F_H2_new = [] 255 | F_CH4_new = [] 256 | F_SO2_new = [] 257 | F_H2S_new = [] 258 | F_S2_new = [] 259 | O2_consumption_new = [] 260 | Late_melt_production = [] 261 | 262 | mantle_CO2_fraction = [] 263 | mantle_H2O_fraction=[] 264 | from outgassing_module import * 265 | 266 | for k in range(0,len(inputs)): 267 | f_O2_FMQ.append(inputs[k].Ocean_fraction*0 ) 268 | f_O2_IW.append(inputs[k].Ocean_fraction*0 ) 269 | f_O2_MH.append(inputs[k].Ocean_fraction*0 ) 270 | f_O2_mantle.append(inputs[k].Ocean_fraction*0 ) 271 | iron_ratio.append(inputs[k].Ocean_fraction*0 ) 272 | total_iron.append(inputs[k].Ocean_fraction*0 ) 273 | iron2_array.append(inputs[k].Ocean_fraction*0 ) 274 | iron3_array.append(inputs[k].Ocean_fraction*0 ) 275 | actual_phi_surf_melt_ar.append(inputs[k].Ocean_fraction*0 ) 276 | XH2O_melt.append(inputs[k].Ocean_fraction*0 ) 277 | XCO2_melt.append(inputs[k].Ocean_fraction*0 ) 278 | F_H2O_new.append(inputs[k].Ocean_fraction*0 ) 279 | F_CO2_new.append(inputs[k].Ocean_fraction*0 ) 280 | F_CO_new.append(inputs[k].Ocean_fraction*0 ) 281 | F_H2_new.append(inputs[k].Ocean_fraction*0 ) 282 | F_CH4_new.append(inputs[k].Ocean_fraction*0 ) 283 | F_SO2_new.append(inputs[k].Ocean_fraction*0 ) 284 | F_H2S_new.append(inputs[k].Ocean_fraction*0 ) 285 | F_S2_new.append(inputs[k].Ocean_fraction*0 ) 286 | O2_consumption_new.append(inputs[k].Ocean_fraction*0 ) 287 | 288 | mantle_CO2_fraction.append(inputs[k].Ocean_fraction*0 ) 289 | mantle_H2O_fraction.append(inputs[k].Ocean_fraction*0 ) 290 | 291 | try: 292 | ocean_start_index = np.min(np.where(inputs[k].Ocean_depth>0)) 293 | max_T_runaway = np.max(total_y[k][8][ocean_start_index:]) 294 | Max_runaway_temperatures.append(max_T_runaway) 295 | except: 296 | abc = 1 + 2 297 | 298 | for i in range(0,len(total_time[k])): 299 | mantle_CO2_fraction[k][i] = total_y[k][13][i]/(total_y[k][13][i]+total_y[k][12][i]) 300 | mantle_H2O_fraction[k][i] = total_y[k][0][i]/(total_y[k][0][i]+total_y[k][1][i]) 301 | Pressure_surface =fO2_array[k][i] + Pressre_H2O[k][i]*water_frac[k][i] + CO2_Pressure_array[k][i] + N2_Pressure 302 | 303 | f_O2_FMQ[k][i] = buffer_fO2(total_y[k][7][i],Pressure_surface/1e5,'FMQ') 304 | f_O2_IW[k][i] = buffer_fO2(total_y[k][7][i],Pressure_surface/1e5,'IW') 305 | f_O2_MH[k][i] = buffer_fO2(total_y[k][7][i],Pressure_surface/1e5,'MH') 306 | iron3 = total_y[k][5][i]*56/(56.0+1.5*16.0) 307 | iron2 = total_y[k][6][i]*56/(56.0+16.0) 308 | iron2_array[k][i] = iron2 309 | iron3_array[k][i] = iron3 310 | total_iron[k][i] = iron3+iron2 311 | iron_ratio[k][i] = iron3/iron2 312 | f_O2_mantle[k][i] = get_fO2(0.5*iron3/iron2,Pressure_surface,total_y[k][7][i],Total_Fe_array[k]) 313 | T_for_melting = float(total_y[k][7][i]) 314 | Poverburd = fO2_array[k][i] + Pressre_H2O[k][i] + CO2_Pressure_array[k][i] + N2_Pressure 315 | alpha = 2e-5 316 | cp = 1.2e3 317 | pm = 4000.0 318 | rdck = optimize.minimize(find_r,x0=float(total_y[k][2][i]),args = (T_for_melting,alpha,g,cp,pm,rp,float(Poverburd),0,0.0)) 319 | rad_check = float(rdck.x[0]) 320 | if rad_check>rp: 321 | rad_check = rp 322 | rlid = rp - total_y[k][26][i] 323 | [actual_phi_surf_melt,actual_visc,Va] = temp_meltfrac(0.99998*rad_check,rp,alpha,pm,T_for_melting,cp,g,Poverburd,0,rlid) 324 | actual_phi_surf_melt_ar[k][i]= actual_phi_surf_melt 325 | F = actual_phi_surf_melt_ar[k][i] 326 | x = 0.01550152865954013 327 | M_H2O = 18.01528 328 | M_CO2 = 44.01 329 | mantle_mass = (4./3. * np.pi * pm * (rp**3 - rc**3)) 330 | XH2O_melt_max = x*M_H2O*0.499 # half of mol fraction allowed to be H2O 331 | XCO2_melt_max = x*M_CO2*0.499 # half of mol fraction allowed to be CO2 332 | if F >0: 333 | XH2O_melt[k][i] = np.min([0.99*XH2O_melt_max,(1- (1-F)**(1/0.01)) * (total_y[k][0][i]/mantle_mass)/F ]) # mass frac, ensures mass frac never implies all moles volatile! 334 | XCO2_melt[k][i] = np.min([0.99*XCO2_melt_max,(1- (1-F)**(1/2e-3)) * (total_y[k][13][i]/mantle_mass)/F ])# mass frac 335 | else: 336 | XH2O_melt[k][i] = 0.0 337 | XCO2_melt[k][i] = 0.0 338 | 339 | Late_melt_production.append(np.mean(total_y[k][25][994:])) 340 | 341 | 342 | Q = total_y[k][11] 343 | Aoc = 4*np.pi*rp**2 344 | for i in range(0,len(Q)): 345 | if total_y[k][16][i]/1000 < 1e-11: 346 | total_y[k][16][i] = 0.0 347 | 348 | Melt_volume[k] = total_y[k][25] 349 | Plate_velocity[k] = 365*24*60*60*Melt_volume[k]/(total_y[k][16] * 3 * np.pi*rp) 350 | 351 | min_HabTime = 0 352 | hab_counter = 0 353 | max_HabTime = 0 354 | for i in range(0,len(Q)): 355 | if total_y[k][16][i]/1000 < 1e-11: 356 | Plate_velocity[k][i] = 0 357 | total_Ar40[k][i] = total_y[k][38][i]/(total_y[k][38][i] + total_y[k][36][i]) 358 | total_K40[k][i] = total_y[k][35][i] + total_y[k][37][i] 359 | 360 | DH_atmo[k][i] = 0.5*total_y[k][41][i]/total_y[k][1][i] 361 | DH_solid[k][i] = 0.5*total_y[k][42][i]/total_y[k][0][i] 362 | if (inputs[k].Ocean_depth[i]>0.0): 363 | if hab_counter == 0: 364 | min_HabTime = total_time[k][i] 365 | hab_counter = 1.0 366 | max_HabTime = total_time[k][i] 367 | 368 | #### outgassing aside 369 | Pressure_surface =fO2_array[k][i] + Pressre_H2O[k][i]*water_frac[k][i] + CO2_Pressure_array[k][i] + N2_Pressure 370 | melt_mass = total_y[k][25][i]*pm*1000 371 | Tsolidus = sol_liq(rp,g,pm,rp,0.0,0.0) 372 | if (0.5*iron_ratio[k][i]>0)and(melt_mass>0)and(actual_phi_surf_melt_ar[k][i])>0: 373 | try: 374 | [F_H2O,F_CO2,F_H2,F_CO,F_CH4,F_SO2,F_H2S,F_S2,O2_consumption] = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0] 375 | except: 376 | [F_H2O,F_CO2,F_H2,F_CO,F_CH4,F_SO2,F_H2S,F_S2,O2_consumption] = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0] 377 | else: 378 | [F_H2O,F_CO2,F_H2,F_CO,F_CH4,F_SO2,F_H2S,F_S2,O2_consumption] = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0] 379 | 380 | F_H2O_new[k][i] = F_H2O*365*24*60*60/(1e12) 381 | F_CO2_new[k][i] = F_CO2*365*24*60*60/(1e12) 382 | F_CO_new[k][i] = F_CO*365*24*60*60/(1e12) 383 | F_H2_new[k][i] = F_H2*365*24*60*60/(1e12) 384 | F_CH4_new[k][i] = F_CH4*365*24*60*60/(1e12) 385 | F_SO2_new[k][i] = F_SO2*365*24*60*60/(1e12) 386 | F_H2S_new[k][i] = F_H2S*365*24*60*60/(1e12) 387 | F_S2_new[k][i] = F_S2*365*24*60*60/(1e12) 388 | O2_consumption_new[k][i] = O2_consumption*365*24*60*60/(1e12) 389 | 390 | HTmin.append(min_HabTime) 391 | HTmax.append(max_HabTime) 392 | HT_duration.append( max_HabTime - min_HabTime) 393 | 394 | 395 | HTmin= np.array(HTmin) 396 | HTmax = np.array(HTmax) 397 | HT_duration = np.array(HT_duration) 398 | Late_melt_production = np.array(Late_melt_production) 399 | ## End post-processing of outputs: 400 | ################################################################################ 401 | 402 | ################################################################################ 403 | ## Plotting of results: 404 | conf_DH_atmo = scipy.stats.scoreatpercentile(DH_atmo ,[int1,int2,int3], interpolation_method='fraction',axis=0) 405 | conf_DH_solid = scipy.stats.scoreatpercentile(DH_solid ,[int1,int2,int3], interpolation_method='fraction',axis=0) 406 | 407 | conf_HT_duration = scipy.stats.scoreatpercentile(HT_duration ,[int1,int2,int3], interpolation_method='fraction',axis=0) 408 | conf_HTmax = scipy.stats.scoreatpercentile(HTmax ,[int1,int2,int3], interpolation_method='fraction',axis=0) 409 | conf_HTmin = scipy.stats.scoreatpercentile(HTmin ,[int1,int2,int3], interpolation_method='fraction',axis=0) 410 | 411 | conf_Late_melt_production = scipy.stats.scoreatpercentile(365*24*60*60*Late_melt_production/1e9 ,[int1,int2,int3], interpolation_method='fraction',axis=0) 412 | 413 | four_percentilesa = scipy.stats.percentileofscore(Max_runaway_temperatures ,700) 414 | four_percentilesb = scipy.stats.percentileofscore(Max_runaway_temperatures ,800) 415 | four_percentilesc = scipy.stats.percentileofscore(Max_runaway_temperatures ,900) 416 | four_percentilesd = scipy.stats.percentileofscore(Max_runaway_temperatures,1000) 417 | 418 | print ("Hab duration", conf_HT_duration[0],conf_HT_duration[1],conf_HT_duration[2]) 419 | print ("Hab max", conf_HTmax[0],conf_HTmax[1],conf_HTmax[2]) 420 | print ("Hab min", conf_HTmin[0],conf_HTmin[1],conf_HTmin[2]) 421 | print ("Late_melt_production", conf_Late_melt_production[0],conf_Late_melt_production[1],conf_Late_melt_production[2]) 422 | print ("four_percentiles",four_percentilesa,four_percentilesb,four_percentilesc,four_percentilesd) 423 | 424 | try: 425 | confidence_Max_runaway_temperatures = scipy.stats.scoreatpercentile(Max_runaway_temperatures ,[int1,int2,int3], interpolation_method='fraction',axis=0) 426 | print('confidence_Max_runaway_temperatures') 427 | print(confidence_Max_runaway_temperatures) 428 | except: 429 | print ('no confidence max runaway') 430 | 431 | ## Mantle and magma ocean redox relative to FMQ: 432 | confidence_mantle_CO2_fraction = scipy.stats.scoreatpercentile(mantle_CO2_fraction ,[int1,int2,int3], interpolation_method='fraction',axis=0) 433 | confidence_mantle_H2O_fraction = scipy.stats.scoreatpercentile(mantle_H2O_fraction ,[int1,int2,int3], interpolation_method='fraction',axis=0) 434 | confidence_f_O2_FMQ = scipy.stats.scoreatpercentile(f_O2_FMQ ,[int1,int2,int3], interpolation_method='fraction',axis=0) 435 | confidence_f_O2_IW = scipy.stats.scoreatpercentile(f_O2_IW ,[int1,int2,int3], interpolation_method='fraction',axis=0) 436 | confidence_f_O2_MH = scipy.stats.scoreatpercentile(f_O2_MH ,[int1,int2,int3], interpolation_method='fraction',axis=0) 437 | confidence_f_O2_mantle = scipy.stats.scoreatpercentile(f_O2_mantle ,[int1,int2,int3], interpolation_method='fraction',axis=0) 438 | confidence_iron_ratio = scipy.stats.scoreatpercentile(iron_ratio ,[int1,int2,int3], interpolation_method='fraction',axis=0) 439 | confidence_f_O2_relative_FMQ = scipy.stats.scoreatpercentile(np.log10(f_O2_mantle) - np.log10(f_O2_FMQ) ,[int1,int2,int3], interpolation_method='fraction',axis=0) 440 | 441 | 442 | Melt_volume = 365*24*60*60*Melt_volume/1e9 443 | Melt_volumeCOPY = np.copy(Melt_volume) 444 | confidence_melt=scipy.stats.scoreatpercentile(Melt_volume ,[int1,int2,int3], interpolation_method='fraction',axis=0) 445 | confidence_velocity = scipy.stats.scoreatpercentile(Plate_velocity ,[int1,int2,int3], interpolation_method='fraction',axis=0) 446 | 447 | #### Plotting individual model runs: 448 | pylab.figure() 449 | for k in range(0,len(inputs)): 450 | pylab.semilogx(total_time[k],total_y[k][0]+total_y[k][1],'b') 451 | pylab.semilogx(total_time[k],total_y[k][1],'r--') 452 | pylab.ylabel("Total water (kg)") 453 | pylab.xlabel("Time (yrs)") 454 | pylab.xticks([1e0,1e1,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9]) 455 | pylab.xlim([x_low, x_high]) 456 | pylab.legend(frameon=False) 457 | 458 | pylab.figure() 459 | pylab.subplot(2,1,1) 460 | for k in range(0,len(inputs)): 461 | pylab.semilogx(total_time[k],total_y[k][52],'b') 462 | pylab.ylabel("depletion_fraction") 463 | pylab.xlabel("Time (yrs)") 464 | pylab.xticks([1e0,1e1,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9]) 465 | pylab.xlim([x_low, x_high]) 466 | pylab.legend(frameon=False) 467 | pylab.subplot(2,1,2) 468 | Vmantle0 = ((4.0*np.pi/3.0) * (rp**3 - rc**3))/1e9 469 | for k in range(0,len(inputs)): 470 | for i in range(0,len(Melt_volume[k])): 471 | if (total_time[k][i]0): #weathering only occurs if below critical point 10 | infront= 4000 #coefficient chosen to reproduce approximate Earth fluxes 11 | Supply_limit = supp_lim 12 | Te = T_efold 13 | alpha = alpha_exp 14 | Ocean_depth = (0.018/MMW)*(1-water_frac) * H2O_Pressure_surface / (g*1000) ## max ocean depth continents 11.4 * gEarth/gplanet (COwan and Abbot 2014) 15 | Max_depth = 11400 * (9.8 / g) 16 | 17 | 18 | if Ocean_depth > Max_depth: 19 | Ocean_fraction = 1.0 20 | Land_fraction = 0.0 21 | LF = 0.0 22 | else: 23 | Ocean_fraction = (Ocean_depth/Max_depth)**0.25 ## Approximation to Earth hypsometric curve 24 | Land_fraction = 1 - Ocean_fraction 25 | Land_fraction0 = 1 - (2.5/11.4)**0.25 26 | LF = Land_fraction / Land_fraction0 27 | 28 | Tdeep = SurfT + Ocean_depth * 5e-4 * g * SurfT / 4000.0 # adiabatic lapse rate, won't make much difference 29 | 30 | Crustal_production = CP/360.0 31 | Seafloor_weathering =(infront/4.0) * 10**(-0.3*(ocean_pH-7.727)) * Crustal_production * np.exp(-(90000.0/8.314)*(1.0/Tdeep-1.0/285)) 32 | 33 | Continental_weathering =infront * LF * ((CO2_Pressure_surface/1e5)/(350e-6) )**alpha * np.exp((SurfT-285)/Te) 34 | Weathering = (Continental_weathering + Seafloor_weathering) * (1-water_frac) #water frac to stop weathering as dry out 35 | 36 | if Weathering>Supply_limit: #Weathering flux never exceeds supply limit 37 | return Supply_limit 38 | else: 39 | Weathering = 0.0 40 | 41 | return Weathering 42 | 43 | -------------------------------------------------------------------------------- /escape_functions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pylab 3 | from scipy.integrate import * 4 | from scipy.interpolate import interp1d 5 | from scipy import optimize 6 | import pdb 7 | import scipy.interpolate 8 | from numba import jit 9 | 10 | mH = 1.67e-27 11 | G = 6.67e-11 12 | kB = 1.38e-23 13 | mO = 16*mH 14 | 15 | @jit(nopython=True) 16 | def better_diffusion(fH2O,T,g,fCO2,fO2,fN2): # diffusion H2O thrugh CO2 and N2 17 | pO2 = fO2 18 | pN2 = fN2 19 | pCO2 = fCO2 20 | ## Units conversion 21 | ## H2O_CO2: 9.24e-5 * T**1.5 / np.exp(307.9/T) = 9.24e-5/(10*1.38e-23) * T**0.5 / np.exp(307.9/T) 22 | ## H2O_N2: 0.187e-5 * T **2.072 = 0.187e-5/(10*1.38e-23) * T**1.072 23 | ## H2O_O2: 0.189e-5 * T **2.072 = 0.189e-5/(10*1.38e-23) * T**1.072 24 | bH2O_CO2 = 100 * 9.24e-5/(1.38e-22) * T**0.5 / np.exp(307.9/T) # conversion m 25 | bH2O_N2 = 100 * 0.187e-5/(1.38e-22) * T**1.072 # conversion m 26 | bH2O_O2 = 100 * 0.189e-5/(10*1.38e-23) * T**1.072 # conversion m 27 | bH2O = (bH2O_CO2 * pCO2 + bH2O_N2 * pN2 + bH2O_O2*pO2) / (pCO2 + pN2 + pO2) 28 | Mn = (0.044 * pCO2 + 0.028 * pN2 + 0.032 * pO2)/(pCO2 + pN2 + pO2) #kg/mol 29 | 30 | Hn = (8.314*T)/(Mn*g) 31 | H_H2O = (8.314*T)/(0.018*g) 32 | phi = bH2O * fH2O * (1.0/Hn - 1.0/H_H2O) 33 | return phi/6.022e23 #mol H2O/m2/s I think 34 | 35 | @jit(nopython=True) 36 | def better_atomic_diffusion(fH2O,T,g,fCO2,fO2,fN2): # diffusion H thrugh CO2 and N2 and O 37 | pO = (2*fO2+fH2O)/(2*fO2 + fN2 + fCO2 + 3*fH2O) 38 | pN2 = fN2/(2*fO2 + fN2 + fCO2 + 3*fH2O) 39 | pCO2 = fCO2/(2*fO2 + fN2 + fCO2 + 3*fH2O) 40 | pH = 2*fH2O/(2*fO2 + fN2 + fCO2 + 3*fH2O) 41 | ## Units conversion 42 | bH_CO2 = 100 * 8.4e17 * T**0.6 43 | bH_N2 = 100 * 6.5e17 * T**0.7 44 | bH_O = 100 * 4.8e17 * T**0.75 45 | bH = (bH_CO2 * pCO2 + bH_N2 * pN2 + bH_O*pO) / (pCO2 + pN2 + pO) 46 | Mn = (0.044 * pCO2 + 0.028 * pN2 + 0.016 * pO)/(pCO2 + pN2 + pO) #kg/mol 47 | Hn = (8.314*T)/(Mn*g) 48 | H_H = (8.314*T)/(0.001*g) 49 | phi = bH * fH2O * (1.0/Hn - 1.0/H_H) 50 | return phi/6.022e23 #mol H2O/m2/s I think 51 | 52 | @jit(nopython=True) 53 | def find_epsilon(T,RE,ME,FXUV, XO, XH, XC,epsilon_init,mixparam): 54 | 55 | Rp = RE * 6.371e6 56 | Mp = ME * 5.972e24 57 | g = G* Mp / (Rp**2) 58 | delPHI = G*Mp/Rp 59 | mass_loss = epsilon_init * FXUV / (4.0*delPHI) 60 | mi = mH 61 | mj = 16.0*mH 62 | 63 | bij = 100.0*4.8e17*(T**0.75) #100 * cm-1 s-1 bH,O 64 | fj = XO 65 | 66 | Fi_limit = g*(mj - mi) * bij / ( kB * T * (1+fj)) 67 | true_flux = Fi_limit * mi 68 | ratio = true_flux/mass_loss 69 | if ratio > 1.0: 70 | return epsilon_init 71 | else: 72 | epsilon = epsilon_init*true_flux/ mass_loss 73 | return ((1-mixparam)*epsilon + mixparam*epsilon_init) # mix param = 0.9 -> only 10% energy about xj=1 goes into extra escape 74 | 75 | @jit(nopython=True) 76 | def Odert_three(T,RE,ME,epsilon,FXUV, XO, XH, XC): 77 | 78 | Rp = RE * 6.371e6 79 | Mp = ME * 5.972e24 80 | g = G* Mp / (Rp**2) 81 | delPHI = G*Mp/Rp 82 | mass_loss = epsilon * FXUV / (4.0*delPHI) 83 | 84 | # carbon 85 | #mk = 12.0*mH # C atoms 86 | mk = 44.0*mH # CO2 molecules 87 | fk = XC 88 | 89 | mi = mH 90 | mj = 16.0*mH 91 | 92 | bij = 100.0*4.8e17*(T**0.75) #100 * cm-1 s-1 bH,O 93 | fj = XO 94 | 95 | bik =100* 8.4e17*T**0.6 #k is CO2 96 | bjk = 100* 7.86e16 * T**0.776 # O in CO2 97 | 98 | ### carefully verified 99 | LHS = mass_loss + mj * fj * g * (mj - mi) * bij / (kB*T * (1 + fj)) + (mk*fk*g * (mk - mi) * bik / (kB*T) )/ (1.0 + bik*fj/bjk) - mk*fk * bik *fj * g * (mj - mi) / ((1+bik*fj/bjk)* kB*T*(1+fj)) + mk*fk*bik*fj*g*(mj-mi)*bij / ((1+bik*fj/bjk )* bjk *kB*T* (1+fj)) 100 | RHS = ( mi + mj*fj + mk*fk / (1.0 + bik*fj/bjk) + mk*fk * (bik*fj/bjk) / (1.0 + bik*fj/bjk)) # verified 101 | Fi = LHS/RHS 102 | 103 | #Fi = (mass_loss + mj * fj *g * (mj - mi) * bij / (kB*T*(1+fi))) / (mi+mj*fj) 104 | xj = 1 - g*(mj - mi) * bij / (Fi * kB * T * (1+fj)) 105 | ### need better criteria for zeroing out k flux, recalculating xk 106 | if xj<0: 107 | xj = 0.0 108 | 109 | # checked 110 | RHS = mass_loss + bik*(mk*fk*g*(mk - mi)/ (kB*T))/ (1.0 + bik*fj/bjk) 111 | LHS = mi + mk*fk / (1.0 + bik*fj/bjk) + mk*fk*bik* fj / (bij*(1 + fj*bik/bjk)) 112 | Fi = RHS/LHS #new Fi assuming zero j 113 | 114 | xk_top = 1 - g*(mk - mi) * bik / (Fi * kB * T ) + (bik/bij)*fj * (1 - xj) + (bik/bjk) * fj *xj 115 | xk_bot = 1 + (bik/bjk) * fj 116 | xk = xk_top/ xk_bot 117 | 118 | if xk< 0.0: #no O escape no C escape 119 | xk = 0.0 120 | Fk = 0.0 121 | Fj = 0.0 122 | Fi = mass_loss / (mi) 123 | else: # no O escape, but yes C escape 124 | Fj = 0.0 125 | Fk = Fi * fk * xk 126 | 127 | else: 128 | xk_top = 1 - g*(mk - mi) * bik / (Fi * kB * T ) + (bik/bij)*fj * (1 - xj) + (bik/bjk) * fj *xj 129 | xk_bot = 1 + (bik/bjk) * fj 130 | xk = xk_top / xk_bot 131 | if xk>0: # O escape and C escape 132 | Fj = Fi * fj *xj 133 | Fk = Fi * fk * xk 134 | else: # O escape, no C escape 135 | xk = 0.0 136 | Fk = 0.0 137 | Fi = mass_loss / (mi + mj*fj*xj) 138 | Fj = (mass_loss - mi*Fi)/mj 139 | 140 | F_down_O = 0.5* Fi - Fj + 2*Fk 141 | 142 | return [Fi*mi, Fj*mj ,F_down_O * mj, Fk*mk] 143 | 144 | 145 | -------------------------------------------------------------------------------- /fH2O_200_FIX_cold.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshuakt/Venus-evolution/3ea858061eb113cc8bd07f47829a2153d0fd37fa/fH2O_200_FIX_cold.npy -------------------------------------------------------------------------------- /fH2O_200_FIX_flat.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshuakt/Venus-evolution/3ea858061eb113cc8bd07f47829a2153d0fd37fa/fH2O_200_FIX_flat.npy -------------------------------------------------------------------------------- /failed_outputs3/test.txt: -------------------------------------------------------------------------------- 1 | Redundant. Can be deleted. 2 | -------------------------------------------------------------------------------- /fugacityCoefficientVariables.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshuakt/Venus-evolution/3ea858061eb113cc8bd07f47829a2153d0fd37fa/fugacityCoefficientVariables.txt -------------------------------------------------------------------------------- /numba_nelder_mead.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implements the Nelder-Mead algorithm for maximizing a function with one or more 3 | variables. 4 | 5 | """ 6 | 7 | import numpy as np 8 | from numba import njit 9 | from collections import namedtuple 10 | results = namedtuple('results', 'x fun success nit final_simplex') 11 | 12 | 13 | @njit 14 | def nelder_mead(fun, x0, bounds=np.array([[], []]).T, args=(), tol_f=1e-10, 15 | tol_x=1e-10, max_iter=1000): 16 | """ 17 | .. highlight:: none 18 | 19 | Maximize a scalar-valued function with one or more variables using the 20 | Nelder-Mead method. 21 | 22 | This function is JIT-compiled in `nopython` mode using Numba. 23 | 24 | Parameters 25 | ---------- 26 | fun : callable 27 | The objective function to be maximized: `fun(x, *args) -> float` 28 | where x is an 1-D array with shape (n,) and args is a tuple of the 29 | fixed parameters needed to completely specify the function. This 30 | function must be JIT-compiled in `nopython` mode using Numba. 31 | 32 | x0 : ndarray(float, ndim=1) 33 | Initial guess. Array of real elements of size (n,), where ‘n’ is the 34 | number of independent variables. 35 | 36 | bounds: ndarray(float, ndim=2), optional 37 | Bounds for each variable for proposed solution, encoded as a sequence 38 | of (min, max) pairs for each element in x. The default option is used 39 | to specify no bounds on x. 40 | 41 | args : tuple, optional 42 | Extra arguments passed to the objective function. 43 | 44 | tol_f : scalar(float), optional(default=1e-10) 45 | Tolerance to be used for the function value convergence test. 46 | 47 | tol_x : scalar(float), optional(default=1e-10) 48 | Tolerance to be used for the function domain convergence test. 49 | 50 | max_iter : scalar(float), optional(default=1000) 51 | The maximum number of allowed iterations. 52 | 53 | Returns 54 | ---------- 55 | results : namedtuple 56 | A namedtuple containing the following items: 57 | :: 58 | 59 | "x" : Approximate local maximizer 60 | "fun" : Approximate local maximum value 61 | "success" : 1 if the algorithm successfully terminated, 0 otherwise 62 | "nit" : Number of iterations 63 | "final_simplex" : Vertices of the final simplex 64 | 65 | Examples 66 | -------- 67 | >>> @njit 68 | ... def rosenbrock(x): 69 | ... return -(100 * (x[1] - x[0] ** 2) ** 2 + (1 - x[0])**2) 70 | ... 71 | >>> x0 = np.array([-2, 1]) 72 | >>> qe.optimize.nelder_mead(rosenbrock, x0) 73 | results(x=array([0.99999814, 0.99999756]), fun=-1.6936258239463265e-10, 74 | success=True, nit=110, 75 | final_simplex=array([[0.99998652, 0.9999727], 76 | [1.00000218, 1.00000301], 77 | [0.99999814, 0.99999756]])) 78 | 79 | Notes 80 | -------- 81 | This algorithm has a long history of successful use in applications, but it 82 | will usually be slower than an algorithm that uses first or second 83 | derivative information. In practice, it can have poor performance in 84 | high-dimensional problems and is not robust to minimizing complicated 85 | functions. Additionally, there currently is no complete theory describing 86 | when the algorithm will successfully converge to the minimum, or how fast 87 | it will if it does. 88 | 89 | References 90 | ---------- 91 | 92 | .. [1] J. C. Lagarias, J. A. Reeds, M. H. Wright and P. E. Wright, 93 | Convergence Properties of the Nelder–Mead Simplex Method in Low 94 | Dimensions, SIAM. J. Optim. 9, 112–147 (1998). 95 | 96 | .. [2] S. Singer and S. Singer, Efficient implementation of the Nelder–Mead 97 | search algorithm, Appl. Numer. Anal. Comput. Math., vol. 1, no. 2, 98 | pp. 524–534, 2004. 99 | 100 | .. [3] J. A. Nelder and R. Mead, A simplex method for function 101 | minimization, Comput. J. 7, 308–313 (1965). 102 | 103 | .. [4] Gao, F. and Han, L., Implementing the Nelder-Mead simplex algorithm 104 | with adaptive parameters, Comput Optim Appl (2012) 51: 259. 105 | 106 | .. [5] http://www.scholarpedia.org/article/Nelder-Mead_algorithm 107 | 108 | .. [6] http://www.brnt.eu/phd/node10.html#SECTION00622200000000000000 109 | 110 | .. [7] Chase Coleman's tutorial on Nelder Mead 111 | 112 | .. [8] SciPy's Nelder-Mead implementation 113 | 114 | """ 115 | vertices = _initialize_simplex(x0, bounds) 116 | 117 | results = _nelder_mead_algorithm(fun, vertices, bounds, args=args, 118 | tol_f=tol_f, tol_x=tol_x, 119 | max_iter=max_iter) 120 | 121 | return results 122 | 123 | 124 | 125 | @njit 126 | def _nelder_mead_algorithm(fun, vertices, bounds=np.array([[], []]).T, 127 | args=(), ρ=1., χ=2., γ=0.5, σ=0.5, tol_f=1e-8, 128 | tol_x=1e-8, max_iter=1000): 129 | """ 130 | .. highlight:: none 131 | 132 | Implements the Nelder-Mead algorithm described in Lagarias et al. (1998) 133 | modified to maximize instead of minimizing. JIT-compiled in `nopython` 134 | mode using Numba. 135 | 136 | Parameters 137 | ---------- 138 | fun : callable 139 | The objective function to be maximized. 140 | `fun(x, *args) -> float` 141 | where x is an 1-D array with shape (n,) and args is a tuple of the 142 | fixed parameters needed to completely specify the function. This 143 | function must be JIT-compiled in `nopython` mode using Numba. 144 | 145 | vertices : ndarray(float, ndim=2) 146 | Initial simplex with shape (n+1, n) to be modified in-place. 147 | 148 | args : tuple, optional 149 | Extra arguments passed to the objective function. 150 | 151 | ρ : scalar(float), optional(default=1.) 152 | Reflection parameter. Must be strictly greater than 0. 153 | 154 | χ : scalar(float), optional(default=2.) 155 | Expansion parameter. Must be strictly greater than max(1, ρ). 156 | 157 | γ : scalar(float), optional(default=0.5) 158 | Contraction parameter. Must be stricly between 0 and 1. 159 | 160 | σ : scalar(float), optional(default=0.5) 161 | Shrinkage parameter. Must be strictly between 0 and 1. 162 | 163 | tol_f : scalar(float), optional(default=1e-10) 164 | Tolerance to be used for the function value convergence test. 165 | 166 | tol_x : scalar(float), optional(default=1e-10) 167 | Tolerance to be used for the function domain convergence test. 168 | 169 | max_iter : scalar(float), optional(default=1000) 170 | The maximum number of allowed iterations. 171 | 172 | Returns 173 | ---------- 174 | results : namedtuple 175 | A namedtuple containing the following items: 176 | :: 177 | 178 | "x" : Approximate solution 179 | "fun" : Approximate local maximum 180 | "success" : 1 if successfully terminated, 0 otherwise 181 | "nit" : Number of iterations 182 | "final_simplex" : The vertices of the final simplex 183 | 184 | """ 185 | n = vertices.shape[1] 186 | _check_params(ρ, χ, γ, σ, bounds, n) 187 | 188 | nit = 0 189 | 190 | ργ = ρ * γ 191 | ρχ = ρ * χ 192 | σ_n = σ ** n 193 | 194 | f_val = np.empty(n+1, dtype=np.float64) 195 | for i in range(n+1): 196 | f_val[i] = _neg_bounded_fun(fun, bounds, vertices[i], args=args) 197 | 198 | # Step 1: Sort 199 | sort_ind = f_val.argsort() 200 | LV_ratio = 1 201 | 202 | # Compute centroid 203 | x_bar = vertices[sort_ind[:n]].sum(axis=0) / n 204 | 205 | while True: 206 | shrink = False 207 | 208 | # Check termination 209 | fail = nit >= max_iter 210 | 211 | best_val_idx = sort_ind[0] 212 | worst_val_idx = sort_ind[n] 213 | 214 | term_f = f_val[worst_val_idx] - f_val[best_val_idx] < tol_f 215 | 216 | # Linearized volume ratio test (see [2]) 217 | term_x = LV_ratio < tol_x 218 | 219 | if term_x or term_f or fail: 220 | break 221 | 222 | # Step 2: Reflection 223 | # https://github.com/QuantEcon/QuantEcon.py/issues/530 224 | temp = ρ * (x_bar - vertices[worst_val_idx]) 225 | x_r = x_bar + temp 226 | f_r = _neg_bounded_fun(fun, bounds, x_r, args=args) 227 | 228 | if f_r >= f_val[best_val_idx] and f_r < f_val[sort_ind[n-1]]: 229 | # Accept reflection 230 | vertices[worst_val_idx] = x_r 231 | LV_ratio *= ρ 232 | 233 | # Step 3: Expansion 234 | elif f_r < f_val[best_val_idx]: 235 | # https://github.com/QuantEcon/QuantEcon.py/issues/530 236 | temp = χ * (x_r - x_bar) 237 | x_e = x_bar + temp 238 | f_e = _neg_bounded_fun(fun, bounds, x_e, args=args) 239 | if f_e < f_r: # Greedy minimization 240 | vertices[worst_val_idx] = x_e 241 | LV_ratio *= ρχ 242 | else: 243 | vertices[worst_val_idx] = x_r 244 | LV_ratio *= ρ 245 | 246 | # Step 4 & 5: Contraction and Shrink 247 | else: 248 | # Step 4: Contraction 249 | # https://github.com/QuantEcon/QuantEcon.py/issues/530 250 | temp = γ * (x_r - x_bar) 251 | if f_r < f_val[worst_val_idx]: # Step 4.a: Outside Contraction 252 | x_c = x_bar + temp 253 | LV_ratio_update = ργ 254 | else: # Step 4.b: Inside Contraction 255 | x_c = x_bar - temp 256 | LV_ratio_update = γ 257 | 258 | f_c = _neg_bounded_fun(fun, bounds, x_c, args=args) 259 | if f_c < min(f_r, f_val[worst_val_idx]): # Accept contraction 260 | vertices[worst_val_idx] = x_c 261 | LV_ratio *= LV_ratio_update 262 | 263 | # Step 5: Shrink 264 | else: 265 | shrink = True 266 | for i in sort_ind[1:]: 267 | vertices[i] = vertices[best_val_idx] + σ * \ 268 | (vertices[i] - vertices[best_val_idx]) 269 | f_val[i] = _neg_bounded_fun(fun, bounds, vertices[i], 270 | args=args) 271 | 272 | sort_ind[1:] = f_val[sort_ind[1:]].argsort() + 1 273 | 274 | x_bar = vertices[best_val_idx] + σ * \ 275 | (x_bar - vertices[best_val_idx]) + \ 276 | (vertices[worst_val_idx] - vertices[sort_ind[n]]) / n 277 | 278 | LV_ratio *= σ_n 279 | 280 | if not shrink: # Nonshrink ordering rule 281 | f_val[worst_val_idx] = _neg_bounded_fun(fun, bounds, 282 | vertices[worst_val_idx], 283 | args=args) 284 | 285 | for i, j in enumerate(sort_ind): 286 | if f_val[worst_val_idx] < f_val[j]: 287 | sort_ind[i+1:] = sort_ind[i:-1] 288 | sort_ind[i] = worst_val_idx 289 | break 290 | 291 | x_bar += (vertices[worst_val_idx] - vertices[sort_ind[n]]) / n 292 | 293 | nit += 1 294 | 295 | return results(vertices[sort_ind[0]], -f_val[sort_ind[0]], not fail, nit, 296 | vertices) 297 | 298 | 299 | @njit 300 | def _initialize_simplex(x0, bounds): 301 | """ 302 | Generates an initial simplex for the Nelder-Mead method. JIT-compiled in 303 | `nopython` mode using Numba. 304 | 305 | Parameters 306 | ---------- 307 | x0 : ndarray(float, ndim=1) 308 | Initial guess. Array of real elements of size (n,), where ‘n’ is the 309 | number of independent variables. 310 | 311 | bounds: ndarray(float, ndim=2) 312 | Sequence of (min, max) pairs for each element in x0. 313 | 314 | Returns 315 | ---------- 316 | vertices : ndarray(float, ndim=2) 317 | Initial simplex with shape (n+1, n). 318 | 319 | """ 320 | n = x0.size 321 | 322 | vertices = np.empty((n + 1, n), dtype=np.float64) 323 | 324 | # Broadcast x0 on row dimension 325 | vertices[:] = x0 326 | 327 | nonzdelt = 0.05 328 | zdelt = 0.00025 329 | 330 | for i in range(n): 331 | # Generate candidate coordinate 332 | if vertices[i + 1, i] != 0.: 333 | vertices[i + 1, i] *= (1 + nonzdelt) 334 | else: 335 | vertices[i + 1, i] = zdelt 336 | 337 | return vertices 338 | 339 | 340 | @njit 341 | def _check_params(ρ, χ, γ, σ, bounds, n): 342 | """ 343 | Checks whether the parameters for the Nelder-Mead algorithm are valid. 344 | JIT-compiled in `nopython` mode using Numba. 345 | 346 | Parameters 347 | ---------- 348 | ρ : scalar(float) 349 | Reflection parameter. Must be strictly greater than 0. 350 | 351 | χ : scalar(float) 352 | Expansion parameter. Must be strictly greater than max(1, ρ). 353 | 354 | γ : scalar(float) 355 | Contraction parameter. Must be stricly between 0 and 1. 356 | 357 | σ : scalar(float) 358 | Shrinkage parameter. Must be strictly between 0 and 1. 359 | 360 | bounds: ndarray(float, ndim=2) 361 | Sequence of (min, max) pairs for each element in x. 362 | 363 | n : scalar(int) 364 | Number of independent variables. 365 | 366 | """ 367 | if ρ < 0: 368 | raise ValueError("ρ must be strictly greater than 0.") 369 | if χ < 1: 370 | raise ValueError("χ must be strictly greater than 1.") 371 | if χ < ρ: 372 | raise ValueError("χ must be strictly greater than ρ.") 373 | if γ < 0 or γ > 1: 374 | raise ValueError("γ must be strictly between 0 and 1.") 375 | if σ < 0 or σ > 1: 376 | raise ValueError("σ must be strictly between 0 and 1.") 377 | 378 | if not (bounds.shape == (0, 2) or bounds.shape == (n, 2)): 379 | raise ValueError("The shape of `bounds` is not valid.") 380 | if (np.atleast_2d(bounds)[:, 0] > np.atleast_2d(bounds)[:, 1]).any(): 381 | raise ValueError("Lower bounds must be greater than upper bounds.") 382 | 383 | 384 | @njit 385 | def _check_bounds(x, bounds): 386 | """ 387 | Checks whether `x` is within `bounds`. JIT-compiled in `nopython` mode 388 | using Numba. 389 | 390 | Parameters 391 | ---------- 392 | x : ndarray(float, ndim=1) 393 | 1-D array with shape (n,) of independent variables. 394 | 395 | bounds: ndarray(float, ndim=2) 396 | Sequence of (min, max) pairs for each element in x. 397 | 398 | Returns 399 | ---------- 400 | bool 401 | `True` if `x` is within `bounds`, `False` otherwise. 402 | 403 | """ 404 | if bounds.shape == (0, 2): 405 | return True 406 | else: 407 | return ((np.atleast_2d(bounds)[:, 0] <= x).all() and 408 | (x <= np.atleast_2d(bounds)[:, 1]).all()) 409 | 410 | 411 | @njit 412 | def _neg_bounded_fun(fun, bounds, x, args=()): 413 | """ 414 | Wrapper for bounding and taking the negative of `fun` for the 415 | Nelder-Mead algorithm. JIT-compiled in `nopython` mode using Numba. 416 | 417 | Parameters 418 | ---------- 419 | fun : callable 420 | The objective function to be minimized. 421 | `fun(x, *args) -> float` 422 | where x is an 1-D array with shape (n,) and args is a tuple of the 423 | fixed parameters needed to completely specify the function. This 424 | function must be JIT-compiled in `nopython` mode using Numba. 425 | 426 | bounds: ndarray(float, ndim=2) 427 | Sequence of (min, max) pairs for each element in x. 428 | 429 | x : ndarray(float, ndim=1) 430 | 1-D array with shape (n,) of independent variables at which `fun` is 431 | to be evaluated. 432 | 433 | args : tuple, optional 434 | Extra arguments passed to the objective function. 435 | 436 | Returns 437 | ---------- 438 | scalar 439 | `-fun(x, *args)` if x is within `bounds`, `np.inf` otherwise. 440 | 441 | """ 442 | if _check_bounds(x, bounds): 443 | return -fun(x, *args) 444 | else: 445 | return np.inf 446 | 447 | 448 | 449 | ''' 450 | 451 | @njit # function for finding surface temperature that balances ASR and interior heatflow 452 | def funTs_general(Ts,Tp,ll,visc,beta,Te_input,ASR_input,H2O_Pressure_surface,CO2_Pressure_surface,ocean_CO3,MMW,PO2_surf): 453 | if Ts[0] UpperT+0.000: 29 | visc = visc_liq 30 | else: 31 | v1 = np.log10(visc_rock) 32 | v2 = np.log10(visc_liq) 33 | logvisc = ( v2 * 0.2*(Tp - (LowerT))**5 + v1 * 0.8*(UpperT - Tp)**5) / ( 0.2*(Tp - (LowerT))**5 + 0.8*(UpperT - Tp)**5) 34 | visc = 10**logvisc 35 | return visc 36 | 37 | @jit(nopython=True) 38 | def f_for_optim(x,kH2O,Mcrystal,Mliq,rp,yH2O_liq,g,MMW): 39 | result = (kH2O * x * Mcrystal + x * (Mliq-Mcrystal) + 4.0 *(0.018/MMW)* np.pi * (rp**2/g) * (x / 3.44e-8)**(1.0/0.74) - yH2O_liq) 40 | return result 41 | 42 | #@jit(nopython=True) 43 | def H2O_partition_function( yH2O_liq,Mliq,Mcrystal,rp,g,kH2O,MMW): #partition H2O between magma ocean and atmosphere 44 | if (Mliq >0)or(Mliq>0): 45 | FH2O = optimize.newton(f_for_optim,0.5,args=(kH2O,Mcrystal,Mliq,rp,yH2O_liq,g,MMW)) 46 | Pressure_surface = (FH2O / 3.44e-8)**(1.0/0.74) 47 | else: 48 | FH2O = 3.44e-8*( yH2O_liq/(4 * (0.018/MMW) * np.pi * (rp**2/g)) ) ** (0.74) 49 | Pressure_surface = (FH2O / 3.44e-8)**(1.0/0.74) 50 | return [FH2O,Pressure_surface] 51 | 52 | @jit(nopython=True) 53 | def CO2_partition_function( yCO2_liq,Mliq,Mcrystal,rp,g,kCO2,MMW): #partition CO2 between magma ocean and atmosphere 54 | if (Mliq>0)or(Mcrystal>0): 55 | FCO2 = yCO2_liq / (kCO2 * Mcrystal + (Mliq-Mcrystal) + 4 * (0.044/MMW) * np.pi * (rp**2/g) * (1 /4.4e-12)) 56 | Pressure_surface = (FCO2 /4.4e-12) 57 | else: 58 | FCO2 = 0.0 59 | Pressure_surface = (yCO2_liq*g)/(4.0 *(0.044/MMW)* np.pi * (rp**2)) 60 | return [FCO2,Pressure_surface] 61 | 62 | @jit(nopython=True) 63 | def Mliq_fun(y2,rp,rs,pm): #calculate liquid mass of mantle 64 | if rs < rp: 65 | Mliq = pm * 4./3. * np.pi * (rp**3 - rs**3) 66 | else: 67 | Mliq = 0.0 68 | return Mliq 69 | 70 | @jit(nopython=True) 71 | def rs_term_fun(r,a1,b1,a2,b2,g,alpha,cp,pm,rp,Tp,Poverburd): #controls solidification radius evolution 72 | numerator = 1 + alpha*(g/cp) * (rp - r) 73 | e1 = np.exp(1e-5*(-rp+r+100000)) 74 | e2 = np.exp(1e-5*(rp-r-100000)) 75 | sub_denom = (e1+e2)**2 76 | T1 = (b1+a1*g*pm*(rp-r)+a1*Poverburd) 77 | T2 = (b2+a2*g*pm*(rp-r)+a2*Poverburd) 78 | sub_num1 = ((-a1*g*pm*e1 + T1*1e-5*e1) + (-a2*g*pm*e2 - T2*1e-5*e2))*(e1+e2) 79 | sub_num2 = (T1*e1+T2*e2)*(1e-5*e1-1e-5*e2) 80 | everything = (sub_num1 - sub_num2)/sub_denom 81 | if r>rp: 82 | return 0 83 | else: 84 | return numerator /(alpha*g*Tp/cp + everything) 85 | 86 | @jit(nopython=True) 87 | def adiabat(radius,Tp,alpha,g,cp,rp): #mantle adiabat 88 | Tr = Tp*(1 + alpha*(g/cp)*(rp-radius)) 89 | return Tr 90 | 91 | @jit(nopython=True) 92 | def sol_liq(radius,g,pm,rp,Poverburd,mH2O): #For calculating solidus 93 | a1 = 104.42e-9 94 | b1 = 1420+0.000-80.0 95 | TS1 = b1 + a1 * g * pm * (rp - radius) + a1 * Poverburd - 4.7e4 * mH2O**0.75 96 | 97 | if TS1 < 1170.0: 98 | T_sol1 = 1170.0+0*TS1 99 | else: 100 | T_sol1 = TS1 101 | a2 = 26.53e-9 102 | b2 = 1825+0.000 103 | TS2 = b2 + a2 * g * pm * (rp -radius) + a2 * Poverburd - 4.7e4 * mH2O**0.75 104 | if TS2 < 1170.0: 105 | T_sol2 = 1170.0+0*TS2 106 | else: 107 | T_sol2 = TS2 108 | T_sol = (T_sol1 * np.exp(1e-5*( -rp + radius + 100000)) + T_sol2 * np.exp(1e-5*(rp - radius - 100000)))/ (np.exp(1e-5*(-rp + radius + 100000)) + np.exp(1e-5*(rp - radius - 100000))) 109 | return T_sol 110 | 111 | 112 | #@jit(nopython=True) 113 | def find_r(r,Tp,alpha,g,cp,pm,rp,Poverburd,mH2O,delta): ## used for finding the solidus radius numerically 114 | Tr = adiabat(r,Tp,alpha,g,cp,rp-delta) 115 | rr = float(r) 116 | T_sol = sol_liq(rr,g,pm,rp,Poverburd,mH2O) 117 | return (Tr-T_sol)**2.0 118 | 119 | def find_r2(r,Tp,alpha,g,cp,pm,rp,Poverburd,mH2O,delta,depletionfraction): ## used for finding the solidus radius accounting for depletion fraction 120 | Tr = adiabat(r,Tp,alpha,g,cp,rp-delta) 121 | rr = float(r) 122 | T_sol = sol_liq(rr,g,pm,rp,Poverburd,mH2O)+600*depletionfraction 123 | return (Tr-T_sol)**2.0 124 | 125 | @jit(nopython=True) 126 | def viscosity_melt(Tp,pm,Tsol,Tliq,melt_frac): 127 | visc_rock = (80e9 / (2*5.3e15)) * (1e-3 / 0.5e-9)**2.5 * np.exp((240e3+0*100e3)/(8.314*Tp)) * np.exp( - 26 * melt_frac)/pm 128 | if melt_frac <= 0.4: 129 | visc = visc_rock 130 | else: 131 | visc_liq = 0.00024*np.exp(4600.0 / (Tp - 1000.0))/pm 132 | visc_liq = visc_liq / (1 - (1-melt_frac)/(1-0.4))**2.5 133 | if float(visc_liq) < float(visc_rock): 134 | visc = float(visc_liq) 135 | else: 136 | visc = float(visc_rock) 137 | return visc 138 | 139 | @jit(nopython=True) 140 | def temp_meltfrac(rc,rp,alpha,pm,Tp,cp,g,Poverburd,mH2O,rlid): #for calculating melt fraction 141 | rad = np.linspace(rc,rlid,1000) 142 | melt_r = np.copy(rad) 143 | visc_r = np.copy(rad) 144 | vol_r = np.copy(rad)*0 + 4.0*np.pi*(rad[1]-rad[0]) 145 | for j in range(0,len(rad)): 146 | Tsol = sol_liq(float(rad[j]),g,pm,rp,Poverburd,mH2O) 147 | Tliq = Tsol + 600.0 148 | T_r = adiabat(rad[j],Tp,alpha,g,cp,rlid) 149 | if T_r>Tliq: 150 | melt_r[j] = 1.0 151 | visc_r[j] = 1 152 | vol_r[j] = vol_r[j]*rad[j]**2 153 | elif T_rTliq: 181 | melt_r[j] = 1.0 182 | visc_r[j] = 1 183 | vol_r[j] = vol_r[j]*rad[j]**2 184 | elif T_r 1.2*Plim: 212 | #print ('Pressure exceeds analytic solubility limit - no outgassing') 213 | #print (Plim,P,xCO2tot,xH2Otot,mCO2tot,mH2Otot) 214 | return (0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 ) 215 | 216 | # Now solve simple system 217 | find = np.logspace(np.log10(P),-15,1000) 218 | find2 = np.logspace(-15,np.log10(P),1000) 219 | for i in range(0,len(find)): 220 | 221 | #print (i,find[i],P) 222 | #print (C1,C2,C3) 223 | #print (f_O2) 224 | if np.isnan(equation(find[i],F1,a_CO2,P,C1,C2,C3,xCO2tot,F2,a_H2O,xH2Otot))==False: 225 | found_high = find[i] 226 | #print ('yikes') 227 | break 228 | for i in range(0,len(find2)): 229 | if np.isnan(equation(find2[i],F1,a_CO2,P,C1,C2,C3,xCO2tot,F2,a_H2O,xH2Otot))==False: 230 | found_low = find2[i] 231 | #print ('here wat') 232 | break 233 | try: 234 | sol = optimize.root(equation,found_high,args=(F1,a_CO2,P,C1,C2,C3,xCO2tot,F2,a_H2O,xH2Otot),method='lm') 235 | if sol['success']==False: 236 | #print ('here tho') 237 | sys.exit() 238 | except: 239 | sol = optimize.root(equation,found_low,args=(F1,a_CO2,P,C1,C2,C3,xCO2tot,F2,a_H2O,xH2Otot),method='lm') 240 | if sol['success']==False: 241 | #print ('con oops') 242 | sys.exit('Convergence issues! First optimization.') 243 | 244 | P_CO2 = sol['x'][0] 245 | # Solve for the rest of the variables in the simple system 246 | P_CO = C2*P_CO2 247 | x_CO2 = np.exp(F1+a_CO2*np.log(P_CO2)) 248 | alphaG = P*(x_CO2-xCO2tot)/(-P_CO2+P*x_CO2) 249 | P_H2O = (P-P_CO2-C2*P_CO2)/(1+C1+C3*P_CO2) 250 | P_H2 = C1*P_H2O 251 | P_CH4 = C3*P_CO2*P_H2O**2 252 | x_H2O = np.exp(F2+a_H2O*np.log(P_H2O)) 253 | # use different alphaG as inital guess 254 | alphaG = .1 255 | 256 | # now use the solution of the simple system to solve the 257 | # harder problem. I will try to solve it two different ways to 258 | # make sure I avoid errors. 259 | 260 | # error tolerance 261 | tol = 1e-7 262 | 263 | try: 264 | init_cond = [np.log(x_H2O),np.log(x_CO2),np.log(P_H2O),np.log(P_CO2),alphaG,np.log(P_H2),np.log(P_CH4),np.log(P_CO)] 265 | sol = optimize.root(system,init_cond,args = (d_H2O,a_H2O,C1,C2,C3,P,a_CO2,F1,F2,xH2Otot,xCO2tot),method='lm',options={'maxiter': 10000})#,jac=jacob) 266 | error = np.linalg.norm(system(sol['x'],d_H2O,a_H2O,C1,C2,C3,P,a_CO2,F1,F2,xH2Otot,xCO2tot)) 267 | if error>tol or sol['success']==False: 268 | #print ('con here') 269 | sys.exit('Convergence issues!') 270 | 271 | ln_x_H2O,ln_x_CO2,ln_P_H2O,ln_P_CO2,alphaG,ln_P_H2,ln_P_CH4,ln_P_CO = sol['x'] 272 | 273 | if alphaG<0: 274 | #print ('con there') 275 | sys.exit('alphaG is negative') 276 | except: 277 | alphaG = .1 278 | init_cond1 = [np.log(x_H2O),np.log(x_CO2),np.log(P_H2O),np.log(P_CO2),np.log(alphaG),np.log(P_H2),np.log(P_CH4),np.log(P_CO)] 279 | sol1 = optimize.root(system1,init_cond1,args = (d_H2O,a_H2O,C1,C2,C3,P,a_CO2,F1,F2,xH2Otot,xCO2tot),method='lm',options={'maxiter': 10000})#,jac=jacob1) 280 | error1 = np.linalg.norm(system1(sol1['x'],d_H2O,a_H2O,C1,C2,C3,P,a_CO2,F1,F2,xH2Otot,xCO2tot)) 281 | ln_x_H2O,ln_x_CO2,ln_P_H2O,ln_P_CO2,ln_alphaG,ln_P_H2,ln_P_CH4,ln_P_CO = sol1['x'] 282 | alphaG = np.exp(ln_alphaG) 283 | #print (alphaG) 284 | if (error1>tol and alphaG>1e-4 and alphaG<1.0) or sol1['success']==False: 285 | #print(error1) 286 | #print(sol1) 287 | sys.exit('Convergence issues!') 288 | if error1>tol: 289 | #print('warning: outgassing equations not solved to high tolerance') 290 | return (0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 ) 291 | 292 | return (np.exp(ln_P_H2O),np.exp(ln_P_H2),np.exp(ln_P_CO2),np.exp(ln_P_CO),\ 293 | np.exp(ln_P_CH4),alphaG,np.exp(ln_x_CO2),np.exp(ln_x_H2O),np.exp(ln_P_H2O)/P,np.exp(ln_P_H2)/P,np.exp(ln_P_CO2)/P,np.exp(ln_P_CO)/P,\ 294 | np.exp(ln_P_CH4)/P,f_O2/P ) 295 | 296 | 297 | def outgas_flux_cal(Temp,Pressure,mantle_ratio,mantle_mass,mantle_CO2_mass,mantle_H2O_mass,M_MELT,Total_Fe,F): #M_MELT is g/s melt production, Pressure in Pa, Temp in KS 298 | pO2 = get_fO2(mantle_ratio,Pressure,Temp,Total_Fe) 299 | if (mantle_CO2_mass<0.0)or(mantle_H2O_mass<0)or(Pressure<0): 300 | print ('Nothing in the mantle!') 301 | return [0.0,0.0,0.0,0.0,0.0,0.0] 302 | 303 | 304 | x = 0.01550152865954013 305 | M_H2O = 18.01528 306 | M_CO2 = 44.01 307 | 308 | XH2O_melt_max = x*M_H2O*0.499 # half of mol fraction allowed to be H2O 309 | XCO2_melt_max = x*M_CO2*0.499 # half of mol fraction allowed to be CO2 310 | #if (0.95*XH2O_melt_max<(1- (1-F)**(1/0.01)) * (mantle_H2O_mass/mantle_mass)/F)or(0.95*XCO2_melt_max<(1- (1-F)**(1/2e-3)) * (mantle_CO2_mass/mantle_mass)/F ): 311 | # print ('would have gone alpha>1') 312 | XH2O_melt = np.min([0.99*XH2O_melt_max,(1- (1-F)**(1/0.01)) * (mantle_H2O_mass/mantle_mass)/F ]) # mass frac, ensures mass frac never implies all moles volatile! 313 | XCO2_melt = np.min([0.99*XCO2_melt_max,(1- (1-F)**(1/2e-3)) * (mantle_CO2_mass/mantle_mass)/F ])# mass frac 314 | 315 | 316 | # do we make graphite? 317 | graph_on = "n" 318 | if graph_on == "y": 319 | log10_K1 = 40.07639 - 2.53932e-2 * Temp + 5.27096e-6*Temp**2 + 0.0267 * (Pressure/1e5 - 1 )/Temp 320 | log10_K2 = - 6.24763 - 282.56/Temp - 0.119242 * (Pressure/1e5 - 1000)/Temp 321 | gXCO3_melt = ((10**log10_K1)*(10**log10_K2)*pO2)/(1+(10**log10_K1)*(10**log10_K2)*pO2) #oughly matches Fig. 1b in Scientific Reports 322 | gXCO2_melt = (44/36.594)*gXCO3_melt / (1 - (1 - 44/36.594)*gXCO3_melt) 323 | 324 | XCO2_melt = np.min([XCO2_melt,gXCO2_melt]) 325 | ## XCO3 = (wcO2/44) / [(100-wCO2)/fwm + wCO2/44] Holloway et al. 1992 326 | ## XCO3 * [(100-wCO2)/fwm + wCO2/44] = (wcO2/44) 327 | ## XCO3 * wCO2 [(100/wCO2 - 1)/fwm + 1/44] = (wcO2/44) 328 | ## [(100/wCO2 - 1)/fwm + 1/44] = 1/(XCO3 * 44) 329 | ## 100/wCO2 = fwm * [1/(XCO3 * 44) - 1/44] + 1 330 | ## wCO2 =(44/fwm) * 100*XCO3 / ( [1 - XCO3] + 44/fwm*XCO3) = (44/fwm) * 100*XCO3 / (1-XCO3*(1 - 44/fwm)) 331 | ## wCO2 = (44/fwm) * XCO3 / (1-XCO3*(1 - 44/fwm)) #not wt Percent, matches Scientific Reports with noack 332 | #print (mantle_H2O_mass/mantle_mass,mantle_CO2_mass/mantle_mass) 333 | #print (XH2O_melt,XCO2_melt) 334 | 335 | 336 | #print (XH2O_melt,'vs',mantle_H2O_mass/mantle_mass) 337 | #print (XCO2_melt,'vs',mantle_CO2_mass/mantle_mass) 338 | #[PH2O, PH2, PCO2, PCO, PCH4, alphaG, xCO2, xH2O, mH2O,mH2,mCO2,mCO,mCH4,mO2] = solve_gases(Temp,Pressure/1e5,pO2,mantle_CO2_mass/mantle_mass,mantle_H2O_mass/mantle_mass) 339 | [PH2O, PH2, PCO2, PCO, PCH4, alphaG, xCO2, xH2O, mH2O,mH2,mCO2,mCO,mCH4,mO2] = solve_gases(Temp,Pressure/1e5,pO2,XCO2_melt,XH2O_melt) 340 | #print('outs',[PH2O, PH2, PCO2, PCO, PCH4, alphaG, xCO2, xH2O, mH2O,mH2,mCO2,mCO,mCH4,mO2]) 341 | if alphaG<0: 342 | print ('-ve alphaG, outgassing assumed to be zero!') 343 | return [0.0,0.0,0.0,0.0,0.0,0.0] 344 | xm = 1.55e-2 #mol magma / g magma 345 | q_H2O = mH2O * alphaG * xm / (1-alphaG) #mol gas/g magma 346 | q_CO2 = mCO2 * alphaG * xm / (1-alphaG) 347 | q_H2 = mH2 * alphaG * xm / (1-alphaG) 348 | q_CO = mCO * alphaG * xm / (1-alphaG) 349 | q_CH4 = mCH4 * alphaG * xm / (1-alphaG) 350 | 351 | 352 | if alphaG>1.0: # quick fudge, hopefully will go away when make sure rock pressure used for melt calculation (fixed version): 353 | #import pdb 354 | #pdb.set_trace() 355 | F_H2O = 0.0#XH2O_melt*M_MELT * mH2O/(mH2O+mH2+2*mCH4) 356 | F_CO2 = 0.0#XCO2_melt * M_MELT * mCO2/(mCO2+mCO) 357 | F_CO = 0.0#XCO2_melt * M_MELT * mCO/(mCO2+mCO) 358 | F_H2 = 0.0#XH2O_melt*M_MELT * mH2/(mH2O+mH2+2*mCH4) 359 | F_CH4 = 0.0#XH2O_melt*M_MELT * 2*mCH4/(mH2O+mH2+2*mCH4) 360 | O2_consumption = 0.5*F_H2 + 0.5*F_CO + 2 * F_CH4 361 | print ('weird >1 alphaG') 362 | return [F_H2O,F_CO2,F_H2,F_CO,F_CH4,O2_consumption] 363 | 364 | F_H2O = M_MELT*q_H2O 365 | F_CO2 = M_MELT*q_CO2 366 | F_H2 = M_MELT*q_H2 # H2 + 0.5O2 = H2O 367 | F_CO = M_MELT*q_CO #CO + 0.5O2 = CO2 368 | F_CH4 = M_MELT*q_CH4 # CH4 + 2O2 = CO2 + 2H2O 369 | O2_consumption = 0.5*F_H2 + 0.5*F_CO + 2 * F_CH4 370 | return [F_H2O,F_CO2,F_H2,F_CO,F_CH4,O2_consumption] # outputs mol/s fluxes of gases, and mol/s O2 consumption 371 | 372 | -------------------------------------------------------------------------------- /outgassing_module_fast.py: -------------------------------------------------------------------------------- 1 | ########################## 2 | ## Load modules 3 | import numpy as np 4 | from scipy import optimize 5 | import sys 6 | import random 7 | from numba import jit 8 | from numba_nelder_mead import nelder_mead 9 | from VolcGases import functions 10 | ################################ 11 | 12 | ## The VolcGases outgassing model is described in Wogan et al. (2020; PSJ), and must be installed prior to running this code: https://github.com/Nicholaswogan/VolcGases 13 | 14 | def buffer_fO2(T,Press,redox_buffer): # T in K, P in bar 15 | if redox_buffer == 'FMQ': 16 | [A,B,C] = [25738.0, 9.0, 0.092] 17 | elif redox_buffer == 'IW': 18 | [A,B,C] = [27215 ,6.57 ,0.0552] 19 | elif redox_buffer == 'MH': 20 | [A,B,C] = [25700.6,14.558,0.019] # from Frost 21 | else: 22 | print ('error, no such redox buffer') 23 | return -999 24 | return 10**(-A/T + B + C*(Press-1)/T) 25 | 26 | @jit(nopython=True) 27 | def get_fO2(XFe2O3_over_XFeO,P,T,Total_Fe): #Function for calculating oxygen fugacity given Fe3+/Fe2+ speciation 28 | ## Total_Fe is a mole fraction of iron minerals XFeO + XFeO1.5 = Total_Fe, and XFe2O3 = 0.5*XFeO1.5, so XFeO + 2XFe2O3 = Total_Fe 29 | XAl2O3 = 0.022423 30 | XCaO = 0.0335 31 | XNa2O = 0.0024 32 | XK2O = 0.0001077 33 | terms1 = 11492.0/T - 6.675 - 2.243*XAl2O3 34 | terms2 = 3.201*XCaO + 5.854 * XNa2O 35 | terms3 = 6.215*XK2O - 3.36 * (1 - 1673.0/T - np.log(T/1673.0)) 36 | terms4 = -7.01e-7 * P/T - 1.54e-10 * P * (T - 1673)/T + 3.85e-17 * P**2 / T 37 | fO2 = np.exp( (np.log(XFe2O3_over_XFeO) + 1.828 * Total_Fe -(terms1+terms2+terms3+terms4) )/0.196) 38 | return fO2 39 | 40 | @jit(nopython=True) 41 | def outgas_flux_cal_fast(Temp,Pressure,mantle_ratio,mantle_mass,mantle_CO2_mass,mantle_H2O_mass,M_MELT,Total_Fe,F): #M_MELT is g/s melt production, Pressure in Pa, Temp in KS 42 | pO2 = get_fO2(mantle_ratio,Pressure,Temp,Total_Fe) 43 | if (mantle_CO2_mass<0.0)or(mantle_H2O_mass<0)or(Pressure<0): 44 | print ('Nothing in the mantle!') 45 | return [0.0,0.0,0.0,0.0,0.0,0.0] 46 | 47 | x = 0.01550152865954013 48 | M_H2O = 18.01528 49 | M_CO2 = 44.01 50 | 51 | XH2O_melt_max = x*M_H2O*0.499 # half of mol fraction allowed to be H2O 52 | XCO2_melt_max = x*M_CO2*0.499 # half of mol fraction allowed to be CO2 53 | 54 | XH2O_melt = (1- (1-F)**(1/0.01)) * (mantle_H2O_mass/mantle_mass)/F 55 | if 0.99*XH2O_melt_max1.0: 95 | F_H2O = 0.0 96 | F_CO2 = 0.0 97 | F_CO = 0.0 98 | F_H2 = 0.0 99 | F_CH4 = 0.0 100 | O2_consumption = 0.5*F_H2 + 0.5*F_CO + 2 * F_CH4 101 | print ('weird >1 alphaG') 102 | return [F_H2O,F_CO2,F_H2,F_CO,F_CH4,O2_consumption] 103 | 104 | F_H2O = M_MELT*q_H2O 105 | F_CO2 = M_MELT*q_CO2 106 | F_H2 = M_MELT*q_H2 # H2 + 0.5O2 = H2O 107 | F_CO = M_MELT*q_CO #CO + 0.5O2 = CO2 108 | F_CH4 = M_MELT*q_CH4 # CH4 + 2O2 = CO2 + 2H2O 109 | O2_consumption = 0.5*F_H2 + 0.5*F_CO + 2 * F_CH4 110 | return [F_H2O,F_CO2,F_H2,F_CO,F_CH4,O2_consumption] # outputs mol/s fluxes of gases, and mol/s O2 consumption 111 | 112 | ## Units check: 113 | ## xm, constant = mol magma/ g magma (inverse molar mass) 114 | ## alpha, mol gas total / mol gas AND magma 115 | ## 1- alpha = mol gas AND magma/mol gas AND magma - mol gas total / mol gas AND magma = mol magma / mol gas AND magma 116 | ## qi, mol gas i per kg magma, qi = (alpha*xm / (1 - alpha) * (Pi/P) 117 | ## what is 1 - alpha = mol magma / mol gas AND magma 118 | ## qi units: (mol gas total / mol gas and magma total) * (mol magma)/ (g magma) / (mol magma / mol gas AND magma) 119 | ## = (mol gas total / g magma) * Pi/P = mol gas i / g total 120 | ## outgassing flux, Fi = qi*QM QM is kg magma/s so mol gas i / kg magma * kg magma / s = mol gas i / s 121 | -------------------------------------------------------------------------------- /radiative_functions.py: -------------------------------------------------------------------------------- 1 | ################################## 2 | import numpy as np 3 | import pylab 4 | from scipy.integrate import * 5 | from scipy.interpolate import interp1d 6 | from scipy import optimize 7 | import pdb 8 | import scipy.interpolate 9 | from numba import jit 10 | import time 11 | ################################# 12 | 13 | 14 | 15 | ### H2O and CO2 ranges for OLR grid 16 | P_H2O_grid_new = np.logspace(1,9,34) 17 | P_CO2_grid_new = np.logspace(1,8,24) 18 | 19 | 20 | ########### OLR grid used for Tstrat sensitivity test #### 21 | OLR_hybrid_FIX=np.load("OLR_200_FIX_flat.npy") 22 | OLR_hybrid_FIX = np.log10(OLR_hybrid_FIX) 23 | water_frac_multi_new=np.load("Atmo_frac_200_FIX_flat.npy") 24 | fH2O_new = np.load("fH2O_200_FIX_flat.npy") 25 | # dont use k=0 for anything, and dont use k=1 for OLR (ok for fH2O though) 26 | T_surf_grid = np.linspace(274,4000,200) 27 | Te_grid = np.linspace(150,350,8) 28 | ############################################################## 29 | 30 | ########### OLR grid used for nominal model ############## 31 | OLR_hybrid_FIX=np.load("OLR_200_FIX_cold.npy") 32 | OLR_hybrid_FIX = np.log10(OLR_hybrid_FIX) 33 | water_frac_multi_new=np.load("Atmo_frac_200_FIX_cold.npy") 34 | fH2O_new = np.load("fH2O_200_FIX_cold.npy") 35 | T_surf_grid = np.linspace(250,4000,200) 36 | Te_grid = np.linspace(180,350,10) 37 | ########################################################## 38 | 39 | fH2O_new = np.log10(fH2O_new) 40 | 41 | @jit(nopython=True) 42 | def my_interp(Tsurf,Te,PH2O,PCO2): 43 | 44 | if Tsurf<=np.min(T_surf_grid): 45 | Actual_Ts = np.min(T_surf_grid) 46 | Ts_index = 0 47 | elif Tsurf>=np.max(T_surf_grid): 48 | Actual_Ts = np.max(T_surf_grid) 49 | Ts_index = len(T_surf_grid) - 2 #98 50 | else: 51 | for i in range(1,len(T_surf_grid)): 52 | if (T_surf_grid[i]>Tsurf)and(T_surf_grid[i-1]<=Tsurf): 53 | Ts_index = i-1 54 | Actual_Ts = Tsurf 55 | 56 | if Te<=np.min(Te_grid): 57 | Actual_Te = np.min(Te_grid) 58 | Te_index = 0 59 | elif Te>=np.max(Te_grid): 60 | Actual_Te = np.max(Te_grid) 61 | Te_index = len(Te_grid) - 2 #4 62 | else: 63 | for i in range(1,len(Te_grid)): ## Te already filtered, hopefully 64 | if (Te_grid[i]>Te)and(Te_grid[i-1]<=Te): 65 | Te_index = i-1 66 | Actual_Te = Te 67 | 68 | if PH2O <= np.min(P_H2O_grid_new): 69 | Actual_H2O = np.min(P_H2O_grid_new) 70 | H2O_index = 0 71 | elif PH2O >= np.max(P_H2O_grid_new): 72 | Actual_H2O = np.max(P_H2O_grid_new) 73 | H2O_index = len(P_H2O_grid_new) - 2 #32 74 | else: 75 | for i in range(1,len(P_H2O_grid_new)): 76 | if (P_H2O_grid_new[i]>PH2O)and(P_H2O_grid_new[i-1]<=PH2O): 77 | H2O_index = i-1 78 | Actual_H2O = PH2O 79 | 80 | if PCO2 <= np.min(P_CO2_grid_new): 81 | Actual_CO2 = np.min(P_CO2_grid_new) 82 | CO2_index = 0 83 | elif PCO2 >= np.max(P_CO2_grid_new): 84 | Actual_CO2 = np.max(P_CO2_grid_new) 85 | CO2_index = len(P_CO2_grid_new) - 2 #22 86 | else: 87 | for i in range(1,len(P_CO2_grid_new)): 88 | if (P_CO2_grid_new[i]>PCO2)and(P_CO2_grid_new[i-1]<=PCO2): 89 | CO2_index = i-1 90 | Actual_CO2 = PCO2 91 | intTs = T_surf_grid[1+Ts_index]-T_surf_grid[Ts_index] 92 | intTe = Te_grid[1+Te_index] - Te_grid[Te_index] 93 | intH2O = P_H2O_grid_new[1+H2O_index] - P_H2O_grid_new[H2O_index] 94 | intCO2 = P_CO2_grid_new[1+CO2_index] - P_CO2_grid_new[CO2_index] 95 | 96 | 97 | delTs = Actual_Ts - T_surf_grid[Ts_index] 98 | delTe = Actual_Te - Te_grid[Te_index] 99 | delH2O = Actual_H2O - P_H2O_grid_new[H2O_index] 100 | delCO2 = Actual_CO2 - P_CO2_grid_new[CO2_index] 101 | 102 | xd = delTs / intTs 103 | yd = delTe / intTe 104 | zd = delH2O / intH2O 105 | qd = delCO2 / intCO2 106 | 107 | C00 = OLR_hybrid_FIX[Ts_index,Te_index,H2O_index,CO2_index] * (1 - xd) + xd * OLR_hybrid_FIX[Ts_index+1,Te_index,H2O_index,CO2_index] 108 | C01 = OLR_hybrid_FIX[Ts_index,Te_index,H2O_index+1,CO2_index] * (1 - xd) + xd * OLR_hybrid_FIX[Ts_index+1,Te_index,H2O_index+1,CO2_index] 109 | C10 = OLR_hybrid_FIX[Ts_index,Te_index+1,H2O_index,CO2_index] * (1 - xd) + xd * OLR_hybrid_FIX[Ts_index+1,Te_index+1,H2O_index,CO2_index] 110 | C11 = OLR_hybrid_FIX[Ts_index,Te_index+1,H2O_index+1,CO2_index] * (1 - xd) + xd * OLR_hybrid_FIX[Ts_index+1,Te_index+1,H2O_index+1,CO2_index] 111 | 112 | C0 = C00 * (1 - yd) + yd * C10 113 | C1 = C01 * (1 - yd) + yd * C11 114 | 115 | C = C0*(1-zd) + C1*zd 116 | 117 | DC00 = OLR_hybrid_FIX[Ts_index,Te_index,H2O_index,CO2_index+1] * (1 - xd) + xd * OLR_hybrid_FIX[Ts_index+1,Te_index,H2O_index,CO2_index+1] 118 | DC01 = OLR_hybrid_FIX[Ts_index,Te_index,H2O_index+1,CO2_index+1] * (1 - xd) + xd * OLR_hybrid_FIX[Ts_index+1,Te_index,H2O_index+1,CO2_index+1] 119 | DC10 = OLR_hybrid_FIX[Ts_index,Te_index+1,H2O_index,CO2_index+1] * (1 - xd) + xd * OLR_hybrid_FIX[Ts_index+1,Te_index+1,H2O_index,CO2_index+1] 120 | DC11 = OLR_hybrid_FIX[Ts_index,Te_index+1,H2O_index+1,CO2_index+1] * (1 - xd) + xd * OLR_hybrid_FIX[Ts_index+1,Te_index+1,H2O_index+1,CO2_index+1] 121 | 122 | DC0 = DC00 * (1 - yd) + yd * DC10 123 | DC1 = DC01 * (1 - yd) + yd * DC11 124 | 125 | DC = DC0*(1-zd) + DC1*zd 126 | 127 | answer = C*(1-qd) + qd * DC 128 | if Tsurf < np.min(T_surf_grid): 129 | return (Tsurf/np.min(T_surf_grid))**4 * 10**answer # Extrapolation for cold surface temperatures (rarely used) 130 | elif Tsurf > np.max(T_surf_grid): 131 | return (Tsurf/np.max(T_surf_grid))**4 * 10**answer # Extrapolation for hot surface temperatures (rarely used) 132 | else: 133 | return 10**answer 134 | 135 | @jit(nopython=True) 136 | def my_water_frac(Tsurf,Te,PH2O,PCO2): 137 | 138 | if Tsurf >=647: 139 | return 1.0 140 | if Tsurf<=np.min(T_surf_grid): 141 | Actual_Ts = np.min(T_surf_grid) 142 | Ts_index = 0 143 | elif Tsurf>=np.max(T_surf_grid): 144 | Actual_Ts = np.max(T_surf_grid) 145 | Ts_index = len(T_surf_grid) - 2 #98 146 | else: 147 | for i in range(1,len(T_surf_grid)): 148 | if (T_surf_grid[i]>Tsurf)and(T_surf_grid[i-1]<=Tsurf): 149 | Ts_index = i-1 150 | Actual_Ts = Tsurf 151 | 152 | if Te<=np.min(Te_grid): 153 | Actual_Te = np.min(Te_grid) 154 | Te_index = 0 155 | elif Te>=np.max(Te_grid): 156 | Actual_Te = np.max(Te_grid) 157 | Te_index = len(Te_grid) - 2#4 158 | else: 159 | for i in range(1,len(Te_grid)): ## Te already filtered, hopefully 160 | if (Te_grid[i]>Te)and(Te_grid[i-1]<=Te): 161 | Te_index = i-1 162 | Actual_Te = Te 163 | 164 | if PH2O <= np.min(P_H2O_grid_new): 165 | Actual_H2O = np.min(P_H2O_grid_new) 166 | H2O_index = 0 167 | elif PH2O >= np.max(P_H2O_grid_new): 168 | Actual_H2O = np.max(P_H2O_grid_new) 169 | H2O_index = len(P_H2O_grid_new) - 2 #32 170 | else: 171 | for i in range(1,len(P_H2O_grid_new)): 172 | if (P_H2O_grid_new[i]>PH2O)and(P_H2O_grid_new[i-1]<=PH2O): 173 | H2O_index = i-1 174 | Actual_H2O = PH2O 175 | 176 | if PCO2 <= np.min(P_CO2_grid_new): 177 | Actual_CO2 = np.min(P_CO2_grid_new) 178 | CO2_index = 0 179 | elif PCO2 >= np.max(P_CO2_grid_new): 180 | Actual_CO2 = np.max(P_CO2_grid_new) 181 | CO2_index = len(P_CO2_grid_new) - 2 #22 182 | else: 183 | for i in range(1,len(P_CO2_grid_new)): 184 | if (P_CO2_grid_new[i]>PCO2)and(P_CO2_grid_new[i-1]<=PCO2): 185 | CO2_index = i-1 186 | Actual_CO2 = PCO2 187 | intTs = T_surf_grid[1+Ts_index]-T_surf_grid[Ts_index] 188 | intTe = Te_grid[1+Te_index] - Te_grid[Te_index] 189 | intH2O = P_H2O_grid_new[1+H2O_index] - P_H2O_grid_new[H2O_index] 190 | intCO2 = P_CO2_grid_new[1+CO2_index] - P_CO2_grid_new[CO2_index] 191 | 192 | 193 | delTs = Actual_Ts - T_surf_grid[Ts_index] 194 | delTe = Actual_Te - Te_grid[Te_index] 195 | delH2O = Actual_H2O - P_H2O_grid_new[H2O_index] 196 | delCO2 = Actual_CO2 - P_CO2_grid_new[CO2_index] 197 | 198 | xd = delTs / intTs 199 | yd = delTe / intTe 200 | zd = delH2O / intH2O 201 | qd = delCO2 / intCO2 202 | 203 | C00 = water_frac_multi_new[Ts_index,Te_index,H2O_index,CO2_index] * (1 - xd) + xd * water_frac_multi_new[Ts_index+1,Te_index,H2O_index,CO2_index] 204 | C01 = water_frac_multi_new[Ts_index,Te_index,H2O_index+1,CO2_index] * (1 - xd) + xd * water_frac_multi_new[Ts_index+1,Te_index,H2O_index+1,CO2_index] 205 | C10 = water_frac_multi_new[Ts_index,Te_index+1,H2O_index,CO2_index] * (1 - xd) + xd * water_frac_multi_new[Ts_index+1,Te_index+1,H2O_index,CO2_index] 206 | C11 = water_frac_multi_new[Ts_index,Te_index+1,H2O_index+1,CO2_index] * (1 - xd) + xd * water_frac_multi_new[Ts_index+1,Te_index+1,H2O_index+1,CO2_index] 207 | 208 | C0 = C00 * (1 - yd) + yd * C10 209 | C1 = C01 * (1 - yd) + yd * C11 210 | 211 | C = C0*(1-zd) + C1*zd 212 | 213 | DC00 = water_frac_multi_new[Ts_index,Te_index,H2O_index,CO2_index+1] * (1 - xd) + xd * water_frac_multi_new[Ts_index+1,Te_index,H2O_index,CO2_index+1] 214 | DC01 = water_frac_multi_new[Ts_index,Te_index,H2O_index+1,CO2_index+1] * (1 - xd) + xd * water_frac_multi_new[Ts_index+1,Te_index,H2O_index+1,CO2_index+1] 215 | DC10 = water_frac_multi_new[Ts_index,Te_index+1,H2O_index,CO2_index+1] * (1 - xd) + xd * water_frac_multi_new[Ts_index+1,Te_index+1,H2O_index,CO2_index+1] 216 | DC11 = water_frac_multi_new[Ts_index,Te_index+1,H2O_index+1,CO2_index+1] * (1 - xd) + xd * water_frac_multi_new[Ts_index+1,Te_index+1,H2O_index+1,CO2_index+1] 217 | 218 | DC0 = DC00 * (1 - yd) + yd * DC10 219 | DC1 = DC01 * (1 - yd) + yd * DC11 220 | 221 | DC = DC0*(1-zd) + DC1*zd 222 | 223 | answer = C*(1-qd) + qd * DC 224 | 225 | if answer <0: 226 | return 0.0 227 | elif answer>1: 228 | return 1.0 229 | 230 | return answer 231 | 232 | @jit(nopython=True) 233 | def my_fH2O(Tsurf,Te,PH2O,PCO2): 234 | 235 | if Tsurf<=np.min(T_surf_grid): 236 | Actual_Ts = np.min(T_surf_grid) 237 | Ts_index = 0 238 | elif Tsurf>=np.max(T_surf_grid): 239 | Actual_Ts = np.max(T_surf_grid) 240 | Ts_index = len(T_surf_grid) - 2#98 241 | else: 242 | for i in range(1,len(T_surf_grid)): 243 | if (T_surf_grid[i]>Tsurf)and(T_surf_grid[i-1]<=Tsurf): 244 | Ts_index = i-1 245 | Actual_Ts = Tsurf 246 | 247 | if Te<=np.min(Te_grid): 248 | Actual_Te = np.min(Te_grid) 249 | Te_index = 0 250 | elif Te>=np.max(Te_grid): 251 | Actual_Te = np.max(Te_grid) 252 | Te_index = len(Te_grid) - 2#4 253 | else: 254 | for i in range(1,len(Te_grid)): ## Te already filtered, hopefully 255 | if (Te_grid[i]>Te)and(Te_grid[i-1]<=Te): 256 | Te_index = i-1 257 | Actual_Te = Te 258 | 259 | if PH2O <= np.min(P_H2O_grid_new): 260 | Actual_H2O = np.min(P_H2O_grid_new) 261 | H2O_index = 0 262 | elif PH2O >= np.max(P_H2O_grid_new): 263 | Actual_H2O = np.max(P_H2O_grid_new) 264 | H2O_index = len(P_H2O_grid_new) - 2 #32 265 | else: 266 | for i in range(1,len(P_H2O_grid_new)): 267 | if (P_H2O_grid_new[i]>PH2O)and(P_H2O_grid_new[i-1]<=PH2O): 268 | H2O_index = i-1 269 | Actual_H2O = PH2O 270 | 271 | if PCO2 <= np.min(P_CO2_grid_new): 272 | Actual_CO2 = np.min(P_CO2_grid_new) 273 | CO2_index = 0 274 | elif PCO2 >= np.max(P_CO2_grid_new): 275 | Actual_CO2 = np.max(P_CO2_grid_new) 276 | CO2_index = len(P_CO2_grid_new) - 2 #22 277 | else: 278 | for i in range(1,len(P_CO2_grid_new)): 279 | if (P_CO2_grid_new[i]>PCO2)and(P_CO2_grid_new[i-1]<=PCO2): 280 | CO2_index = i-1 281 | Actual_CO2 = PCO2 282 | intTs = T_surf_grid[1+Ts_index]-T_surf_grid[Ts_index] 283 | intTe = Te_grid[1+Te_index] - Te_grid[Te_index] 284 | intH2O = P_H2O_grid_new[1+H2O_index] - P_H2O_grid_new[H2O_index] 285 | intCO2 = P_CO2_grid_new[1+CO2_index] - P_CO2_grid_new[CO2_index] 286 | 287 | 288 | delTs = Actual_Ts - T_surf_grid[Ts_index] 289 | delTe = Actual_Te - Te_grid[Te_index] 290 | delH2O = Actual_H2O - P_H2O_grid_new[H2O_index] 291 | delCO2 = Actual_CO2 - P_CO2_grid_new[CO2_index] 292 | 293 | xd = delTs / intTs 294 | yd = delTe / intTe 295 | zd = delH2O / intH2O 296 | qd = delCO2 / intCO2 297 | 298 | C00 = fH2O_new[Ts_index,Te_index,H2O_index,CO2_index] * (1 - xd) + xd * fH2O_new[Ts_index+1,Te_index,H2O_index,CO2_index] 299 | C01 = fH2O_new[Ts_index,Te_index,H2O_index+1,CO2_index] * (1 - xd) + xd * fH2O_new[Ts_index+1,Te_index,H2O_index+1,CO2_index] 300 | C10 = fH2O_new[Ts_index,Te_index+1,H2O_index,CO2_index] * (1 - xd) + xd * fH2O_new[Ts_index+1,Te_index+1,H2O_index,CO2_index] 301 | C11 = fH2O_new[Ts_index,Te_index+1,H2O_index+1,CO2_index] * (1 - xd) + xd * fH2O_new[Ts_index+1,Te_index+1,H2O_index+1,CO2_index] 302 | 303 | C0 = C00 * (1 - yd) + yd * C10 304 | C1 = C01 * (1 - yd) + yd * C11 305 | 306 | C = C0*(1-zd) + C1*zd 307 | 308 | DC00 = fH2O_new[Ts_index,Te_index,H2O_index,CO2_index+1] * (1 - xd) + xd * fH2O_new[Ts_index+1,Te_index,H2O_index,CO2_index+1] 309 | DC01 = fH2O_new[Ts_index,Te_index,H2O_index+1,CO2_index+1] * (1 - xd) + xd * fH2O_new[Ts_index+1,Te_index,H2O_index+1,CO2_index+1] 310 | DC10 = fH2O_new[Ts_index,Te_index+1,H2O_index,CO2_index+1] * (1 - xd) + xd * fH2O_new[Ts_index+1,Te_index+1,H2O_index,CO2_index+1] 311 | DC11 = fH2O_new[Ts_index,Te_index+1,H2O_index+1,CO2_index+1] * (1 - xd) + xd * fH2O_new[Ts_index+1,Te_index+1,H2O_index+1,CO2_index+1] 312 | 313 | DC0 = DC00 * (1 - yd) + yd * DC10 314 | DC1 = DC01 * (1 - yd) + yd * DC11 315 | 316 | DC = DC0*(1-zd) + DC1*zd 317 | 318 | answer = C*(1-qd) + qd * DC 319 | 320 | if Tsurf < np.min(T_surf_grid): ## Extrapolation for low surface temperatures (rarely used) 321 | return (Tsurf/np.min(T_surf_grid))**3 * 10**answer 322 | 323 | return 10**answer 324 | 325 | ## OLR correction after partitioning CO2 between the atmosphere and liquid water ocean 326 | @jit(nopython=True) 327 | def correction(Tsurf,Te,PH2O,PCO2,rp,g,CO3,PN2,MMW,PO2): 328 | if Tsurf<274: # Carbonate equilibrium constants not valid below freezing 329 | T=274 330 | else: 331 | T=Tsurf 332 | pK1=17.788 - .073104 *T - .0051087*35 + 1.1463*10**-4*T**2 333 | pK2=20.919 - .064209 *T - .011887*35 + 8.7313*10**-5*T**2 334 | H_CO2=1.0/(0.018*10.0*np.exp(-6.8346 + 1.2817e4/T - 3.7668e6/T**2 + 2.997e8/T**3) ) 335 | # from https://srd.nist.gov/JPCRD/jpcrd427.pdf with unit conversion 336 | 337 | atmo_fraction = my_water_frac(Tsurf,Te,PH2O,PCO2) 338 | if (atmo_fraction == 1.0)or(PCO2<0): # if no liquid water ocean, all CO2 in atmosphere and same result as before 339 | return [my_interp(Tsurf,Te,PH2O,PCO2),PCO2,0.0,0.0,0.0,0.0] 340 | else: 341 | atmoH2O = atmo_fraction*PH2O 342 | Mass_oceans_crude = PH2O*(1.0 - atmo_fraction )* 4 *np.pi *rp**2 / g ## This is an approximation 343 | PTOT = atmoH2O + PCO2 + PN2 + PO2 344 | mtot = (PTOT * 4*np.pi*rp**2) / g 345 | MCO2 = 0.044 346 | Mave = MMW 347 | total_mass_CO2 = mtot * MCO2 * PCO2 /(PTOT * Mave) 348 | cCon = total_mass_CO2/(MCO2*Mass_oceans_crude) # mol CO2/kg ocean 349 | if cCon < CO3: #quick fix to ensure no more CO3 than in total atmo-ocean system! 350 | CO3 = 0.9999*cCon 351 | #cCon = s * pCO2 + DIC, where pCO2 is in bar 352 | s = 1e5*(4.0 *np.pi * rp**2 / (MCO2*g) )* (MCO2 / Mave) / Mass_oceans_crude ## mol CO2/ kg atm / Pa, so *1e5 Pa/bar 353 | aa = CO3 * (1 + s/H_CO2)/(10**-pK2*10**-pK1) 354 | bb = CO3 / (10**-pK2) 355 | cc = CO3 - cCon 356 | rr1 = - (bb + np.sqrt (bb**2 - 4 * aa * cc) ) / (2*aa) 357 | rr2 = - (bb - np.sqrt (bb**2 - 4 * aa * cc) ) / (2*aa) 358 | if rr1 <= rr2: 359 | EM_H_o = rr2 360 | else: 361 | EM_H_o = rr1 362 | ALK = CO3 * (2+EM_H_o/(10**-pK2)) 363 | EM_pH_o=-np.log10(EM_H_o) ## Evolving model ocean pH 364 | EM_hco3_o=ALK-2*CO3 ## Evolving model ocean bicarbonate molality 365 | EM_co2aq_o=( EM_hco3_o*EM_H_o/(10**-pK1) ) ## Evolving model ocean aqueous CO2 molality 366 | EM_ppCO2_o = EM_co2aq_o /H_CO2 ## Evolving model atmospheric pCO2 367 | DIC_check = EM_hco3_o + CO3 + EM_co2aq_o 368 | true_OLR = my_interp(Tsurf,Te,PH2O,EM_ppCO2_o*1e5) 369 | return [true_OLR,EM_ppCO2_o*1e5,EM_pH_o,ALK,Mass_oceans_crude,DIC_check] 370 | 371 | -------------------------------------------------------------------------------- /stellar_funs.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.interpolate import interp1d 3 | 4 | def main_sun_fun(time,stellar_mass,tsat_XUV,beta_XUV,fsat): 5 | 6 | if stellar_mass == 1.0: 7 | stellar_data = np.loadtxt('Baraffe3.txt',skiprows=31) # for reproducing sun exactly 8 | else: 9 | print ("This version of code is only set up for solar mass stars") 10 | return [time*0,time*0,time*0,time*0] 11 | 12 | stellar_array=[] 13 | for i in range(0,len(stellar_data[:,0])): 14 | if stellar_data[i,0] == stellar_mass: 15 | stellar_array.append(stellar_data[i,:]) 16 | 17 | stellar_array=np.array(stellar_array) 18 | 19 | min_time = np.min(stellar_array[:,1]) 20 | max_time = np.max(stellar_array[:,1]) 21 | 22 | if (min_time>np.min(time) ) or (max_time 373: 16 | T = 373.0 17 | logK0=-171.9065-0.077993*T+2839.319/T+71.595*np.log10(T) 18 | logK=logK0+(bo+b1*T+b2/T)*S**0.5+co*S+do*S**1.5 19 | return 10**logK 20 | 21 | def equil_cont(T): 22 | pK1=17.788 - .073104 *T - .0051087*35 + 1.1463*10**-4*T**2 23 | pK2=20.919 - .064209 *T - .011887*35 + 8.7313*10**-5*T**2 24 | H_CO2=np.exp(9345.17/T - 167.8108 + 23.3585 * np.log(T)+(.023517 - 2.3656*10**-4*T+4.7036*10**-7*T**2)*35) 25 | return [pK1,pK2,H_CO2] 26 | -------------------------------------------------------------------------------- /thermodynamics.py: -------------------------------------------------------------------------------- 1 | # script for gibbs energy in gas phase 2 | import re 3 | import numpy as np 4 | import sys 5 | import codecs 6 | 7 | def gibbs_helper(spec,T): 8 | R = 8.3145 9 | 10 | f = open('NEWNASA.TXT','r') 11 | #f = open('NEWNASA_revised.TXT','r') 12 | lines = f.readlines() 13 | 14 | 15 | j = 0 16 | jj = 0 17 | for i in range(0,len(lines)): 18 | if lines[i][0:len(spec)]==spec: 19 | j+=1 20 | lines_spec = lines[i:i+30] 21 | for ii in range(0,20): 22 | if lines_spec[2+3*ii]=='\n': 23 | break 24 | 25 | LB = float(lines_spec[2+3*ii].split()[0]) 26 | UB = float(lines_spec[2+3*ii].split()[1]) 27 | if LB1: 39 | print("Not specific enough of an entry!") 40 | sys.exit() 41 | if jj==0: 42 | print('Too hot or too cold!') 43 | sys.exit() 44 | 45 | a1,a2,a3,a4,a5,a6,a7,a8,a9,a10 = C 46 | 47 | DH = R*T*(-a1*T**-2+a2*np.log(T)/T+a3+a4*T/2+a5*T**2/3+a6*T**3/4+a7*T**4/5+a9/T) 48 | DS = R*(-a1*T**-2/2-a2/T+a3*np.log(T)+a4*T+a5*T**2/2+a6*T**3/3+a7*T**4/4+a10) 49 | 50 | DG = DH-T*DS 51 | return DG 52 | 53 | def gibbs_helper_speedy(spec,T): 54 | R = 8.3145 55 | 56 | f = open('NEWNASA_tiny.TXT','r') 57 | lines = f.readlines() 58 | 59 | 60 | j = 0 61 | jj = 0 62 | for i in range(0,len(lines)): 63 | if lines[i][0:len(spec)]==spec: 64 | j+=1 65 | lines_spec = lines[i:] 66 | for ii in range(0,20): 67 | if lines_spec[2+3*ii]=='\n': 68 | break 69 | 70 | LB = float(lines_spec[2+3*ii].split()[0]) 71 | UB = float(lines_spec[2+3*ii].split()[1]) 72 | if LB1: 84 | print("Not specific enough of an entry!") 85 | sys.exit() 86 | if jj==0: 87 | print('Too hot or too cold!') 88 | sys.exit() 89 | 90 | a1,a2,a3,a4,a5,a6,a7,a8,a9,a10 = C 91 | 92 | DH = R*T*(-a1*T**-2+a2*np.log(T)/T+a3+a4*T/2+a5*T**2/3+a6*T**3/4+a7*T**4/5+a9/T) 93 | DS = R*(-a1*T**-2/2-a2/T+a3*np.log(T)+a4*T+a5*T**2/2+a6*T**3/3+a7*T**4/4+a10) 94 | 95 | DG = DH-T*DS 96 | return DG 97 | 98 | 99 | def makeAs(aa): 100 | items =np.array([]) 101 | 102 | for a in aa: 103 | match = re.match(r"([a-z]+)([0-9]+)", a, re.I) 104 | if match: 105 | items = np.append(items,match.groups()) 106 | 107 | else: 108 | items = np.append(items,[a,1]) 109 | 110 | if len(aa)==1: 111 | items = np.resize(items,[2,len(aa)]) 112 | else: 113 | items = np.resize(items,[2,len(aa)]).T 114 | return items 115 | 116 | def convert2standard(aa,T): 117 | case = ['Br','I','N','Cl','H','O','F'] 118 | for jj in range(0,len(aa)): 119 | for cas in case: 120 | if aa[jj]==cas: 121 | aa[jj]=aa[jj]+'2' 122 | if aa[jj]=='C': 123 | aa[jj]=aa[jj]+'(gr)' 124 | if aa[jj]=='S': 125 | if T<368.3007: 126 | aa[jj]=aa[jj]+'(a)' 127 | elif T<388.3607: 128 | aa[jj]=aa[jj]+'(b)' 129 | else: 130 | aa[jj]=aa[jj]+'(L)' 131 | return aa 132 | 133 | def gibbsG(spec,T): 134 | 135 | G = gibbs_helper(spec,T) 136 | 137 | tmp = re.findall(r"([A-Z][a-z]?)(\d*)", spec.strip(' ')) 138 | sp = [] 139 | for i in range(0,len(tmp)): 140 | if tmp[i][1]=='': 141 | sp.append((tmp[i][0],1)) 142 | else: 143 | sp.append((tmp[i][0],int(tmp[i][1]))) 144 | 145 | a = [] 146 | aa = [] 147 | for aaa in sp: 148 | a.append(aaa[1]) 149 | aa.append(aaa[0]) 150 | 151 | aa = convert2standard(aa,T) 152 | speciala = makeAs(aa) 153 | 154 | elementg = [] 155 | for i in range(0,len(sp)): 156 | spec1 = aa[i]+' ' 157 | ig = gibbs_helper(spec1,T) 158 | ig = ig/float(speciala[1,i]) 159 | elementg.append(ig) 160 | 161 | G = G-np.sum(np.array(a)*np.array(elementg)) 162 | return G 163 | 164 | 165 | def gibbs(spec,T): 166 | G = gibbsG(spec,298.15)+gibbs_helper(spec,T)-gibbs_helper(spec,298.15) 167 | return G 168 | 169 | 170 | def gibbsG_speedy(spec,T): 171 | 172 | G = gibbs_helper_speedy(spec,T) 173 | 174 | tmp = re.findall(r"([A-Z][a-z]?)(\d*)", spec.strip(' ')) 175 | sp = [] 176 | for i in range(0,len(tmp)): 177 | if tmp[i][1]=='': 178 | sp.append((tmp[i][0],1)) 179 | else: 180 | sp.append((tmp[i][0],int(tmp[i][1]))) 181 | 182 | a = [] 183 | aa = [] 184 | for aaa in sp: 185 | a.append(aaa[1]) 186 | aa.append(aaa[0]) 187 | 188 | aa = convert2standard(aa,T) 189 | speciala = makeAs(aa) 190 | 191 | elementg = [] 192 | for i in range(0,len(sp)): 193 | spec1 = aa[i]+' ' 194 | ig = gibbs_helper(spec1,T) 195 | ig = ig/float(speciala[1,i]) 196 | elementg.append(ig) 197 | 198 | G = G-np.sum(np.array(a)*np.array(elementg)) 199 | return G 200 | 201 | 202 | def dielectric(T,P): 203 | t = T-273.15 204 | p = P-1 205 | a = [-22.5713, -.032066, -.00028568, .0011832, .000027895, -.00000001476, 2300.64, -.13476] 206 | D0 = 4.476150 207 | di = np.exp((-10**6*D0+2*a[0]*p+2*a[1]*p*t+2*a[2]*p*t**2+2*a[3]*p**2+2*a[4]*p**2*t+2*a[5]*p**2*t**2+2*a[6]*t+2*a[7]*t**2)/(-(10**6))) 208 | return di 209 | 210 | def gibbsAQ(spec,T,P): 211 | 212 | 213 | R = 8.3145 214 | 215 | f = open('sprons96_edited2.dat','r') 216 | lines1 = f.readlines() 217 | 218 | lines = [] 219 | pp = 0 220 | for line in lines1: 221 | 222 | if line.replace(' ','').strip('\n')=='aqueousspecies': 223 | pp = 1 224 | if pp==1: 225 | lines.append(line) 226 | 227 | 228 | j = 0 229 | for i in range(0,len(lines)): 230 | if lines[i][20:].replace(' ','').strip('\n')==spec.replace(' ','') or \ 231 | lines[i][20:].replace(' ','').strip('\n')==spec.replace(' ','')+'(0)': 232 | j+=1 233 | lines_spec = lines[i+3:i+6] 234 | temp = [] 235 | for ii in range(0,len(lines_spec)): 236 | temp = temp+lines_spec[ii].strip('\n').split() 237 | temp = [float(k) for k in temp] 238 | 239 | if j==0: 240 | print("Couldn't find it") 241 | sys.exit() 242 | if j>1: 243 | print("Not specific enough of an entry!") 244 | sys.exit() 245 | 246 | 247 | coef = temp 248 | Tr = 298.15 249 | Pr = 1 250 | Psi = 2600 251 | Theta = 228 252 | Y = -5.81*10**(-5) 253 | Gr = coef[0] 254 | Hr = coef[1] 255 | Sr = coef[2] 256 | a1 = coef[3]/10 257 | a2 = coef[4]*100 258 | a3 = coef[5] 259 | a4 = coef[6]*10000 260 | c1 = coef[7] 261 | c2 = coef[8]*10000 262 | w = coef[9]*100000 263 | q = coef[10] 264 | diE = dielectric(T,P) 265 | # These formulas are all parts of the formulas used in Walther's Essentials of GeoChemistry. 266 | G1 = Gr 267 | G2 = -1*Sr*(T-Tr) 268 | G3 = -1*c1*(T*np.log(T/Tr)-T+Tr) 269 | G4 = a1*(P-Pr) 270 | 271 | h1 = np.log( (Psi+P) / (Psi + Pr)) 272 | 273 | G5 = a2*h1 274 | 275 | h2 = (1/(T-Theta))-(1/(Tr-Theta)) 276 | h3 = (Theta-T)/Theta 277 | h4 = np.log(Tr*(T-Theta)/(T*(Tr-Theta))) 278 | 279 | G6 = -1*c2*(h2*h3-T/(Theta*Theta)*h4) 280 | G7 = (1/(T-Theta))*(a3*(P-Pr)+a4*h1) 281 | G8 = w*Y*(T-Tr) 282 | 283 | G = 4.184*(G1+G2+G3+G4+G5+G6+G7+G8) 284 | 285 | return G 286 | 287 | def henrys_coef(key,T,P): 288 | R = 8.314 289 | 290 | H = np.exp((gibbs(key+' ',T)-gibbsAQ(key,T,P))/(R*T)) 291 | return H 292 | 293 | --------------------------------------------------------------------------------