Analyzing the geothermal results

I have reduced all the simulation results to a single data file of the recovery factors and CO2 emission. In this notebook, I'm going to analyze the data.


In [3]:
using Dierckx, DataFrames, PyPlot, JLD, CoolProp, CSV
include("SetPyPlot.jl")
SetPyPlot.setrcparam()
@load "input_param.jld"


WARNING: replacing module SetPyPlot.
Out[3]:
16-element Array{Symbol,1}:
 :MW_CO2
 :MW_coal
 :MW_gas
 :e_igcc
 :e_ngcc
 :e_pc
 :em_coal
 :em_gas
 :eta_driver
 :eta_pump
 :ex_ccs
 :ex_coal
 :ex_gas
 :η_igcc
 :η_ngcc
 :η_pc

Read the Dutch subsurface porosity data

This data is obtained from the following webpage: Source: http://nlog.nl/reservoireigenschappen


In [4]:
phi_data = CSV.read("TNO_data.csv")
perm_data = dropmissing(phi_data, :Perm_arith)


Out[4]:

16 rows × 10 columns (omitted printing of 4 columns)

Top_tvd_mBasis_tvd_mBruto_dikte_mNetto_dikte_mKlei_Volume_percentPorositeitsmodel
Float64Float64Float64Float64Float64String
13299.993410.48110.4981.4528.7Density-Neutron
23455.043548.5793.5315.536.1Density-Neutron
31443.461561.82118.36118.3610.8Density-Neutron
43463.183573.14109.9692.2733.2Sonic
53619.633710.1290.496.942.2Sonic
63214.263309.4195.1488.8227.2Density-Neutron
73323.453415.9892.5388.7414.7Density-Neutron
83995.184108.39113.2190.1920.4Density-Neutron
94132.594207.1574.5632.2229.1Density-Neutron
104032.114139.03106.9192.8412.7Sonic
114171.164236.5165.3630.1522.7Sonic
123671.533783.1111.5797.0719.9Density-Neutron
133819.593909.8690.2723.8135.4Density-Neutron
141999.142074.0874.9474.9421.5Density-Neutron
153615.053715.38100.3393.0628.9Density-Neutron
163748.793821.3972.653.0923.4Density-Neutron

In [5]:
figure(figsize=(4,3))
plot(phi_data.Basis_tvd_m, phi_data.Porositeit, "o")
xlabel("Depth [m]")
ylabel("Porosity [vol%]")
savefig("figs/porosity_depth.png")
savefig("figs/porosity_depth.svg")



In [20]:
# plotyy(x1, y1, x2, y2; fig_size, x_label, y1_label, y2_label, y1_style, y2_style)
SetPyPlot.plotyy(phi_data.Basis_tvd_m, phi_data.Porositeit, 
    perm_data.Basis_tvd_m, perm_data.Perm_arith, y1_style = "ob", y2_style="vr", 
    x_label="Depth [m]", y1_label="Porosity [vol%]", y2_label="mili Darcy")
# plot(phi_data.Basis_tvd_m, phi_data.Perm_arith)
savefig("figs/porosity_perm_depth.png")
savefig("figs/porosity_perm_depth.svg")


Pressure profiles

Here is the pressure profile of the Peaceman and local refinement models:


In [32]:
p_local_ref = CSV.read("p_profile_local_refine.csv")
p_peaceman = CSV.read("p_profile_coarse_peaceman.csv")
figure(figsize=(4,3))
plot(p_local_ref.x_m, p_local_ref.p_pa./1e5, "-", label="Locally refined grid")
plot(p_peaceman.x_m, p_peaceman.p_pa./1e5, "--", label="Peaceman model")
legend()
xlabel("Distance between wells [m]")
ylabel("Pressure [bar]")
tight_layout()
savefig("figs/pressure_profile.png")
savefig("figs/pressure_profile.svg")



In [8]:
df_geo = CSV.read("geothermal_final_results.csv")


Out[8]:

150 rows × 11 columns (omitted printing of 3 columns)

q_m3_sk_m2L_mt_life_sR_thR_prR_zec_CO2
Float64Float64Float64Float64Float64Float64Float64Float64
10.01388892.0e-14500.01.05763e9-0.310919-3.78832-5.077610.286508
20.01388892.0e-14650.01.4292e9-0.417142-4.2143-5.620730.31254
30.01388892.0e-14800.01.77659e9-0.523523-4.63601-6.158410.338312
40.01388892.0e-14900.02.02833e9-0.60341-4.95111-6.560160.357568
50.01388892.0e-141000.02.2559e9-0.676724-5.23971-6.928130.375205
60.01388895.0e-14500.01.05763e90.388476-1.05631-1.594290.119552
70.01388895.0e-14650.01.4292e90.352124-1.20935-1.789430.128905
80.01388895.0e-14800.01.77659e90.313042-1.36818-1.991930.138611
90.01388895.0e-14900.02.02833e90.282732-1.48961-2.146760.146032
100.01388895.0e-141000.02.2559e90.254659-1.6015-2.289410.152869
110.01388891.0e-13500.01.05763e90.621608-0.145634-0.4331840.0638999
120.01388891.0e-13650.01.4292e90.608546-0.207706-0.5123250.0676931
130.01388891.0e-13800.01.77659e90.591896-0.278904-0.6031020.0720441
140.01388891.0e-13900.02.02833e90.578113-0.335783-0.6756240.0755201
150.01388891.0e-131000.02.2559e90.56512-0.388756-0.7431640.0787573
160.01388892.0e-13500.01.05763e90.7381740.3097010.1473690.0360738
170.01388892.0e-13650.01.4292e90.7367560.2931180.1262250.0370872
180.01388892.0e-13800.01.77659e90.7313240.2657350.09131210.0387606
190.01388892.0e-13900.02.02833e90.7258030.2411320.05994340.0402641
200.01388892.0e-131000.02.2559e90.7203510.2176130.02995690.0417014
210.01388895.0e-13500.01.05763e90.8081130.5829020.4957010.0193782
220.01388895.0e-13650.01.4292e90.8136830.5936120.5093560.0187237
230.01388895.0e-13800.01.77659e90.814980.5925180.5079610.0187906
240.01388895.0e-13900.02.02833e90.8144180.5872810.5012840.0191106
250.01388895.0e-131000.02.2559e90.8134890.5814350.493830.0194679
260.01388891.0e-12500.01.05763e90.8314270.6739690.6118110.013813
270.01388891.0e-12650.01.4292e90.8393250.6937770.6370660.0126025
280.01388891.0e-12800.01.77659e90.8428660.7014460.6468430.0121339
290.01388891.0e-12900.02.02833e90.8439560.7026650.6483970.0120594
300.01388891.0e-121000.02.2559e90.8445350.7027090.6484540.0120567
&vellip&vellip&vellip&vellip&vellip&vellip&vellip&vellip&vellip

Sensitivity analysis

Step 1

Find a list of permeability, well-spacing, and flow rates


In [9]:
flow_rates = df_geo[:q_m3_s] # [m^3/s]
k          = df_geo[:k_m2]   # [m^2]
L          = df_geo[:L_m]    # [m]
flow_range = union(flow_rates)
k_range    = union(k)
L_range    = union(L)


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[9]:1
└ @ Core In[9]:1
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[9]:2
└ @ Core In[9]:2
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[9]:3
└ @ Core In[9]:3
Out[9]:
5-element Array{Float64,1}:
  500.0
  650.0
  800.0
  900.0
 1000.0

In [10]:
k_range


Out[10]:
6-element Array{Float64,1}:
 2.0e-14
 5.0e-14
 1.0e-13
 2.0e-13
 5.0e-13
 1.0e-12

Step 2

For a fixed value of well spacing, plot the effect of perm for different flow rates, and see if the results look good:

Effect of permeability


In [11]:
line_style = ["-", "--", "-.", "-", ":"]
for L_well in L_range
    figure(figsize=(5,3))
    xlabel("Permeability [m\$^2\$]")
    ylabel("Practical recovery factor [-]")
    i=0
    for q in flow_range
        i+=1
        df_temp = df_geo[(df_geo[:q_m3_s].==q) .& (df_geo[:L_m].==L_well), :]
        plot(df_temp[:k_m2], df_temp[:R_pr], line_style[i], label = "q = $(q*3600) m\$^3\$/h")
    end
    legend()
    plot([0, 1e-12], [0,0], "--k")
    axis([0, 1e-12, -1,1])
#     title("L = $L_well [m]")
    savefig("figs/practical_recovery_perm.png")
end


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[11]:9
└ @ Core .\In[11]:9
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[11]:9
└ @ Core .\In[11]:9
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[11]:10
└ @ Core .\In[11]:10
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[11]:10
└ @ Core .\In[11]:10

In [12]:
for L_well in L_range
    figure(figsize=(4,3))
    xlabel("Permeability [m\$^2\$]")
    ylabel("Zero-emission recovery factor [-]")
    i=0
    for q in flow_range
        i+=1
        df_temp = df_geo[(df_geo[:q_m3_s].==q) .& (df_geo[:L_m].==L_well), :]
        plot(df_temp[:k_m2], df_temp[:R_ze], line_style[i], label = "q = $(q*3600) m\$^3\$/h")
#         plot(df_temp[:k_m2], df_temp[:R_pr], label = "q = $(q*3600) m\$^3\$/h")
    end
    legend()
    plot([0, 1e-12], [0,0], "--k")
    axis([0, 1e-12, -1,1])
    title("L = $L_well [m]")
end


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[12]:8
└ @ Core .\In[12]:8
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[12]:8
└ @ Core .\In[12]:8
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[12]:9
└ @ Core .\In[12]:9
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[12]:9
└ @ Core .\In[12]:9

Effect of flow rate

Change the flow rate in different well spacing in different reservoirs


In [13]:
for k_res in k_range
    figure(figsize=(5,3))
    xlabel("Flow rate \L[m^3/h]")
    ylabel("Practical recovery factor [-]")
    i=0
    for L_well in L_range
        i+=1
        df_temp = df_geo[(df_geo[:k_m2].==k_res) .& (df_geo[:L_m].==L_well), :]
        plot(df_temp[:q_m3_s]*3600, df_temp[:R_pr], line_style[i], alpha = 0.5, label = "L = $L_well [m]")
    end
    legend()
    #plot([0, 1e-12], [0,0], "--k")
    #axis([0, 1e-12, -1,1])
    title("k = $(k_res*1e12) [D]")
end


syntax: invalid escape sequence

Effect of flow rate on the project life time

Conclusion: project life time does not depend on the permeability of the reservoir. It is important for the project to have an acceptable lifetime to justify the capital investments.


In [14]:
for k_res in k_range
    figure(figsize=(4,3))
    xlabel("Flow rate [m\$^3\$/h]")
    ylabel("Project life time [year]")
    i=0
    for L_well in L_range
        i+=1
        df_temp = df_geo[(df_geo[:k_m2].==k_res) .& (df_geo[:L_m].==L_well), :]
        plot(df_temp[:q_m3_s]*3600, df_temp[:t_life_s]/(3600*24*365), line_style[i], alpha = 0.8, label = "L = $L_well [m]")
    end
    legend()
    #plot([0, 1e-12], [0,0], "--k")
    #axis([0, 1e-12, -1,1])
#     plot(df_geo[:q_m3_s]*3600, df_geo[:t_life_s]/(3600*24*365), "o")
    savefig("figs/project_life_time.png")
#     title("k = $(k_res*1e12) [D]")
end


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[14]:8
└ @ Core .\In[14]:8
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[14]:8
└ @ Core .\In[14]:8
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[14]:9
└ @ Core .\In[14]:9
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[14]:9
└ @ Core .\In[14]:9

CO2 emission:


In [15]:
figure(figsize=(4,3))
semilogy(df_geo[:q_m3_s]*3600, df_geo[:c_CO2], alpha = 0.5, "o")
semilogy([0, 260], [em_gas, em_gas]*1e3, "--k")
xlabel("Flow rate [m\$^3\$/h]")
ylabel("CO\$_2\$ emission [kg CO\$_2\$/MJ]")
axis([0, 260, 1e-2, 3])
savefig("figs/CO2_emission_flow.png")


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[15]:2
└ @ Core In[15]:2
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[15]:2
└ @ Core In[15]:2

In [16]:
figure(figsize=(4,3))
semilogy(df_geo[:k_m2], df_geo[:c_CO2], alpha = 0.5, "o")
semilogy([0, 1.1e-12], [em_gas, em_gas]*1e3, "--k")
xlabel("Permeability [m\$^2\$]")
ylabel("CO\$_2\$ emission [kg CO\$_2\$/MJ]")
axis([0, 1.1e-12, 1e-2, 3])
savefig("figs/CO2_emission_full_perm.png")


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[16]:2
└ @ Core In[16]:2
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[16]:2
└ @ Core In[16]:2

In [68]:
figure(figsize=(4,3))
semilogy(df_geo[:k_m2], df_geo[:c_CO2]*0.15, alpha = 0.5, "o")
semilogy([0, 1.1e-12], [em_gas, em_gas]*1e3, "--k")
xlabel("Permeability [m\$^2\$]")
ylabel("CO\$_2\$ emission [kg CO\$_2\$/MJ]")
axis([0, 1.1e-12, 1e-3, 4e-1])
savefig("figs/CO2_emission_perm.png")


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[68]:2
└ @ Core In[68]:2
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[68]:2
└ @ Core In[68]:2

Find the optimum range

Apply the following criteria:

  • life time longer than 30 years
  • positive zero-emission recovery factors
  • lower that two times methane emission

In [52]:
T_c = 2.0+273.15 # [K]
T_h = 22+273.15 # [K]
COP_th = T_h/(T_h-T_c)
COP_air = 2.0 # average value (per unit electricity)
COP_geo = 5.0 # shallow heat (comes from the sun)
COP_methane = 1.0 # ignoring heat loss

t_life_crit = 30       # years
c_CO2_crit  = em_gas*1e3*2.0 # kg/MJ

u_ht = 1.0 # W/m2/K
A_ht = 4*10.0*3.0 # only surrounding walls
Q_heat = u_ht*A_ht*(T_h-T_c)/1000 # kJ/s
n_houses = 400 # number of houses to be covered by the geothermal project
m_water = Q_heat*1000*n_houses/(PropsSI("HMASS", "T", 80+273.15, "P", 1e5, "H2O")-PropsSI("HMASS", "T", 40+273.15, "P", 1e5, "H2O"))
q_water = m_water/PropsSI("D", "T", 40+273.15, "P", 1e5, "H2O") # [m3/s]

df_practical = df_geo[(df_geo[:t_life_s].>(30.0*24*3600)) & (df_geo[:R_ze].>0.0) & (df_geo[:c_CO2].<c_CO2_crit), :]


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[52]:17
└ @ Core In[52]:17
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[52]:17
└ @ Core In[52]:17
MethodError: no method matching &(::BitArray{1}, ::BitArray{1})
Closest candidates are:
  &(::Any, ::Any, !Matched::Any, !Matched::Any...) at operators.jl:529
  &(!Matched::PyCall.PyObject, ::Any) at /home/ali/.julia/packages/PyCall/zqDXB/src/pyoperators.jl:13
  &(::Any, !Matched::PyCall.PyObject) at /home/ali/.julia/packages/PyCall/zqDXB/src/pyoperators.jl:14

Stacktrace:
 [1] top-level scope at In[52]:17

In [53]:
flow_rates_pr = df_practical[:q_m3_s] # [m^3/s]
k_pr          = df_practical[:k_m2]   # [m^2]
L_pr          = df_practical[:L_m]    # [m]
flow_range_pr = union(flow_rates_pr)
k_range_pr    = union(k_pr)
L_range_pr    = union(L_pr)


UndefVarError: df_practical not defined

Stacktrace:
 [1] top-level scope at In[53]:1

In [54]:
k_range_pr


UndefVarError: k_range_pr not defined

Stacktrace:
 [1] top-level scope at In[54]:1

In [55]:
figure(figsize=(5,3))
plot(df_practical[:q_m3_s]*3600, df_practical[:c_CO2], alpha = 0.5, "o")
plot([0, 260], [em_gas, em_gas]*1e3, "--k")
xlabel("Flow rate [m^3/h]")
ylabel("CO2 emission [kg CO2/MJ]")
tight_layout()
savefig("carbon_emission_geothermal.png")


UndefVarError: df_practical not defined

Stacktrace:
 [1] top-level scope at In[55]:2

Compare with the current state of technology

The theoretical COP can be calculated based on the temperature of the outside (temperature outside of the house, where heat is extracted) and the desirable temperature inside (where heat is delivered). It is calculated by $$COP_{th} = \frac{T_h}{T_h-T_c}$$

compare with a methane burner

Find cases with a COP less than 1.0


In [56]:
df_current = df_geo[df_geo[:COP].<1.0, :]


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[56]:1
└ @ Core In[56]:1
Out[56]:

29 rows × 11 columns (omitted printing of 3 columns)

q_m3_sk_m2L_mt_life_sR_thR_prR_zec_CO2
Float64Float64Float64Float64Float64Float64Float64Float64
10.02777782.0e-14500.05.28817e8-1.4896-8.39255-10.9480.567878
20.02777782.0e-14650.07.14598e8-1.71221-9.27316-12.07080.621693
30.02777782.0e-14800.08.88297e8-1.9307-10.1328-13.16680.674227
40.02777782.0e-14900.01.01416e9-2.09324-10.7707-13.98020.713212
50.02777782.0e-141000.01.12795e9-2.24194-11.3538-14.72360.748846
60.04166672.0e-14500.03.52545e8-2.67527-13.0241-16.85320.850915
70.04166672.0e-14650.04.76399e8-3.01422-14.3591-18.55540.932503
80.04166672.0e-14800.05.92198e8-3.34479-15.6566-20.20961.01179
90.04166672.0e-14900.06.7611e8-3.58998-16.6174-21.43471.07051
100.04166672.0e-141000.07.51967e8-3.81405-17.4949-22.55351.12413
110.05555562.0e-14500.02.64408e8-3.86746-17.6811-22.79091.13551
120.05555562.0e-14650.03.57299e8-4.32272-19.4705-25.07241.24486
130.05555562.0e-14800.04.44149e8-4.76532-21.2056-27.28461.35089
140.05555562.0e-14900.05.07082e8-5.09317-22.4892-28.92131.42934
150.05555562.0e-141000.05.63975e8-5.3926-23.6611-30.41541.50096
160.05555565.0e-14650.03.57299e8-1.24637-7.45347-9.750670.51049
170.05555565.0e-14800.04.44149e8-1.41973-8.13684-10.6220.552251
180.05555565.0e-14900.05.07082e8-1.54926-8.64583-11.27090.583356
190.05555565.0e-141000.05.63975e8-1.6677-9.11071-11.86370.611766
200.06944442.0e-14500.02.11527e8-5.06588-22.3624-28.75961.42159
210.06944442.0e-14650.02.85839e8-5.63742-24.606-31.62021.5587
220.06944442.0e-14800.03.55319e8-6.19202-26.7786-34.39021.69147
230.06944442.0e-14900.04.05666e8-6.60253-28.3852-36.43861.78965
240.06944442.0e-141000.04.5118e8-6.9773-29.8514-38.3081.87925
250.06944445.0e-14500.02.11527e8-1.56986-8.70607-11.34770.587038
260.06944445.0e-14650.02.85839e8-1.79203-9.58497-12.46830.640748
270.06944445.0e-14800.03.55319e8-2.01009-10.4429-13.56220.693177
280.06944445.0e-14900.04.05666e8-2.17269-11.0811-14.37590.732179
290.06944445.0e-141000.04.5118e8-2.32123-11.6636-15.11850.767773

:MW_CO2
:MW_coal
:MW_gas
:e_igcc
:e_ngcc
:e_pc
:em_coal
:em_gas
:eta_driver :eta_pump
:ex_ccs
:ex_coal
:ex_gas
:η_igcc
:η_ngcc
:η_pc


In [57]:
figure(figsize=(5,3))
plot(df_geo[:k_m2]*1e12, df_geo[:COP], "o", alpha = 0.5)
plot([0,1.1], [1.0, 1.0], "--k", label = "methane")
plot([0,1.1], [2.0, 2.0], "-r", label = "heat pump")
plot([0,1.1], [5.0, 5.0], ":g", label = "geo heat pump")
axis([0,1.05,0,40])
legend()
ylabel("COP")
xlabel("Permeability [D]")


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[57]:2
└ @ Core In[57]:2
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[57]:2
└ @ Core In[57]:2
Out[57]:
PyObject Text(0.5, 66.0, 'Permeability [D]')

In [58]:
figure(figsize=(4,3))
semilogy(df_geo[:k_m2]*1e12, df_geo[:COP], "o", alpha = 0.5)
semilogy([0,1.1], [0.8, 0.8], "--k", label = "methane")
semilogy([0,1.1], [1.0, 1.0], "-r", label = "heat pump")
semilogy([0,1.1], [2.0, 2.0], ":g", label = "geo heat pump")
axis([0,1.05,0,40])
legend()
ylabel("COP")
xlabel("Permeability [D]")
savefig("figs/COP_alternatives.png")


┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[58]:2
└ @ Core In[58]:2
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│   caller = top-level scope at In[58]:2
└ @ Core In[58]:2