"""Injection well monitoring with Hall integral analysis and skin factor evaluation."""
import traceback
from datetime import datetime, timezone
import numpy as np
import pandas as pd
import pytz
from matplotlib import pyplot as plt
from gemini_application.application_abstract import ApplicationAbstract
from gemini_model.fluid.pvt_water_stp import PVTConstantSTP
from gemini_model.reservoir.reservoir_pressuredrop import bottomhole_skin_dp
from gemini_model.well.pressure_drop import DPDT
tzobject = pytz.timezone("Europe/Amsterdam")
[docs]
class InjectionWellMonitoring(ApplicationAbstract):
"""Class for application injection well II (Injectivity Index) & BHP calculations.
This class also prepares the plot for skic factor effect analysis.
"""
def __init__(self):
"""Initialize injection well monitoring."""
super().__init__()
self.bottomhole_skin_dp = bottomhole_skin_dp()
self.DPF = DPDT()
self.DPF.PVT = PVTConstantSTP()
[docs]
def init_parameters(self, parameters):
"""Initialize model parameters."""
well_unit = self.unit
res_param = dict()
res_param["reservoir_pressure"] = parameters["reservoir_pressure"] * 1e5 # bar to Pa
res_param["reservoir_radius"] = parameters["reservoir_radius"] # m
res_param["reservoir_top"] = parameters["reservoir_top"] # m
res_param["reservoir_permeability"] = (
parameters["reservoir_permeability"] * 9.869233e-16
) # mD to m2
res_param["reservoir_thickness"] = parameters["reservoir_thickness"]
self.bottomhole_skin_dp.update_parameters(res_param)
pvt_param = dict()
pvt_param["RHOL"] = parameters["liquid_density"]
pvt_param["VISL"] = parameters["liquid_viscosity"]
self.DPF.PVT.update_parameters(pvt_param)
well_param = dict()
well_traj = well_unit.parameters["property"]["injectionwell_trajectory_table"][-1]
length = []
diameter = []
angle = []
roughness = []
for ii in range(1, len(well_traj)):
MD = well_traj[ii]["MD"] - well_traj[ii - 1]["MD"]
TVD = well_traj[ii]["TVD"] - well_traj[ii - 1]["TVD"]
roughness.append(well_traj[ii]["roughness"])
length.append(MD)
diameter.append(well_traj[ii]["ID"])
angle.append((np.round(90 - np.arccos(TVD / MD) * 180 / np.pi, 2)) * np.pi / 180)
well_param["diameter"] = np.array(diameter) # well diameter in [m]
well_param["length"] = np.array(length) # well length in [m]
well_param["angle"] = np.array(angle) # well angle in [rad]
well_param["roughness"] = roughness # roughness of cells [m]
well_param["friction_correlation"] = well_unit.parameters["property"][
"injectionwell_friction_correlation"
][-1]
well_param["friction_correlation_2p"] = well_unit.parameters["property"][
"injectionwell_friction_correlation_2p"
][-1]
well_param["correction_factors"] = well_unit.parameters["property"][
"injectionwell_friction_correction_factors"
][-1]
well_param["injectionwell_soil_temperature"] = well_unit.parameters["property"][
"injectionwell_soil_temperature"
][
-1
] # soil temperature in [C]
self.DPF.update_parameters(well_param)
[docs]
def calculate(self):
"""Calculate hall integral."""
self.get_data()
self.calculate_hall_integral()
self.calculate_skin_lines()
[docs]
def get_data(self):
"""Get injection well monitoring data."""
start_time = datetime.strptime(self.inputs["start_time"], "%Y-%m-%d %H:%M:%S")
start_time = tzobject.localize(start_time)
start_time = start_time.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
end_time = datetime.strptime(self.inputs["end_time"], "%Y-%m-%d %H:%M:%S")
end_time = tzobject.localize(end_time)
end_time = end_time.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
timestep = 3600 # hardcoded 1 hour since flowrate is in m3/h
result, time = self.plant.database.read_internal_database(
self.unit.plant.name,
self.unit.name,
"injectionwell_flow.measured",
start_time,
end_time,
timestep,
)
self.inputs["flow"] = np.array(result) # m3/hr
self.inputs["time"] = np.array(time)
result, time = self.plant.database.read_internal_database(
self.unit.plant.name,
self.unit.name,
"injectionwell_wellhead_pressure.measured",
start_time,
end_time,
timestep,
)
self.inputs["wellhead_pressure"] = np.array(result) # bar
result, time = self.plant.database.read_internal_database(
self.unit.plant.name,
self.unit.name,
"injectionwell_bottomhole_pressure.calculated",
start_time,
end_time,
timestep,
)
self.inputs["BHP"] = np.array(result) # bar
[docs]
def calculate_hall_integral(self):
"""Calculate hall integral and its derivative."""
try:
cumulative_production = self.inputs["flow"].cumsum() # m3
hall_integral = (
self.inputs["BHP"] - self.inputs["reservoir_pressure"]
).cumsum() # bar.hr
cumulative_production = np.where(
cumulative_production <= 0, np.nan, cumulative_production
)
hall_derivative_numerical = np.gradient(
hall_integral, np.log(cumulative_production)
) # bar.hr/m3
except Exception:
print("ERROR in " + self.__class__.__name__ + " : " + traceback.format_exc())
cumulative_production = None
hall_integral = None
hall_derivative_numerical = None
self.outputs["cumulative_flow"] = cumulative_production # m3
self.outputs["hall_integral"] = hall_integral # bar.hr
self.outputs["hall_derivative_numerical"] = hall_derivative_numerical # bar.hr/m3
[docs]
def calculate_skin_lines(self):
"""Calculate skin factor lines."""
try:
# flow in m3/hr to m3/s
flow_array = np.linspace(
self.inputs["min_flow_plot"] / 3600,
self.inputs["max_flow_plot"] / 3600,
self.inputs["no_interval_flow_plot"],
)
skin_array = np.linspace(
self.inputs["min_skin_plot"],
self.inputs["max_skin_plot"],
self.inputs["no_interval_skin_plot"],
)
u_well = dict()
u_res = dict()
x = []
P_inj = []
u_well["pressure"] = 0.1 * 1e5 # Pa, currently hardcoded due to using fix PVT
u_well["temperature"] = 65 + 273.15 # C to K, currently hardcoded due to using fix PVT
u_well["direction"] = "down"
u_well["temperature_ambient"] = (
self.unit.parameters["property"]["injectionwell_soil_temperature"][-1] + 273.15
) # C to K
_, density, _, _, viscosity, _, _, _, _, _ = self.DPF.PVT.get_pvt(
u_well["pressure"], u_well["temperature"]
)
for skin in skin_array:
P_inj_skin = []
for flow in flow_array:
u_well["flowrate"] = flow # m3/s
self.DPF.calculate_output(u_well, x)
y = self.DPF.get_output()
deltaP_fric = y["pressuredrop_fric_output"] / 1e5 # Pa to bar
u_res["flow"] = flow # m3/s
u_res["viscosity"] = viscosity # Pa.s
u_res["density"] = density # kg/m3
u_res["well_radius"] = self.inputs["wellbore_radius"] # m
u_res["skin_factor"] = skin
self.bottomhole_skin_dp.calculate_output(u_res, x)
y = self.bottomhole_skin_dp.get_output()
BHP = y["reservoir_dp"] / 1e5 # Pa to bar
deltaP_HH = y["Hydrostatic_dp"] / 1e5 # Pa to bar
result = BHP - deltaP_HH + deltaP_fric # bar
if result < 0:
result = 0
P_inj_skin.append(result)
P_inj.append(P_inj_skin)
self.outputs["injection_pressure"] = P_inj # bar
self.outputs["max_cal_P_inj"] = np.max(P_inj)
self.inputs["flow_array"] = flow_array * 3600
self.inputs["skin_array"] = skin_array
except Exception:
print("ERROR in " + self.__class__.__name__ + " : " + traceback.format_exc())
[docs]
def plot(self):
"""Calculate all pressure from reservoir to topside."""
x = self.outputs["cumulative_flow"]
y0 = self.outputs["hall_integral"]
y1 = self.outputs["hall_derivative_numerical"]
plt.figure()
plt.plot(x, y0, label="hall_integral")
plt.plot(x, y1, label="hall_derivative_numerical")
plt.legend()
plt.show()
plt.figure()
plt.plot(x, y0, label="hall_integral")
plt.plot(x, y1, label="hall_derivative_numerical")
plt.xscale("log")
plt.yscale("log")
plt.legend()
plt.show()
[docs]
def plot_skin(self):
"""Calculate all skin factor effect."""
def format_func(value, tick_number):
date = pd.to_datetime(value)
return date.strftime("%d %B %Y")
realTime_time = self.inputs["time"]
realTime_time = pd.to_datetime(realTime_time)
realTime_flow = self.inputs["flow"]
realTime_wellhead_pressure = self.inputs["wellhead_pressure"]
max_flow_rate = self.inputs["max_flow_rate"]
max_pressure = self.inputs["max_pressure"]
flow_array = np.linspace(
self.inputs["min_flow_plot"],
self.inputs["max_flow_plot"],
self.inputs["no_interval_flow_plot"],
)
skin_array = np.linspace(
self.inputs["min_skin_plot"],
self.inputs["max_skin_plot"],
self.inputs["no_interval_skin_plot"],
)
plt.figure()
scatter = plt.scatter(
realTime_flow, realTime_wellhead_pressure, c=realTime_time.astype("int64"), cmap="jet"
)
colorbar = plt.colorbar(scatter)
colorbar.ax.yaxis.set_major_formatter(plt.FuncFormatter(format_func))
for idx, skin in enumerate(skin_array):
plt.plot(
flow_array,
self.outputs["injection_pressure"][idx],
label=f"Skin = {skin}",
color="dimgray",
linestyle="--",
)
plt.text(
flow_array[-1],
self.outputs["injection_pressure"][idx][-1],
f"skin = {skin:.0f}",
color="black",
fontsize=8,
verticalalignment="bottom",
horizontalalignment="left",
)
plt.axvline(x=max_flow_rate, color="r", linestyle="--")
plt.axhline(y=max_pressure, color="r", linestyle="-.")
plt.text(
max_flow_rate - 10,
self.outputs["max_cal_P_inj"] - 22,
f"Max Q = {max_flow_rate:.0f} m3/h",
color="red",
fontsize=8,
verticalalignment="bottom",
horizontalalignment="left",
rotation="vertical",
)
plt.text(
self.inputs["min_flow_plot"] + 2,
max_pressure + 2,
f"Max P = {max_pressure:.0f} bar",
color="red",
fontsize=8,
verticalalignment="bottom",
horizontalalignment="left",
)
plt.xlim((self.inputs["min_flow_plot"], self.inputs["max_flow_plot"] + 50))
plt.ylim((0, self.outputs["max_cal_P_inj"] + 5))
plt.title("Q-P plot incl. skin lines")
plt.xlabel("Flow Rate [m3/h]")
plt.ylabel("Injection pressure [bar]")
plt.grid()
plt.tight_layout()
plt.show()