Skip to content

Commit

Permalink
SBT first implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
malcolm-dsider committed Aug 13, 2024
1 parent 22e7ee8 commit d9f6009
Show file tree
Hide file tree
Showing 44 changed files with 4,177 additions and 3,841 deletions.
51 changes: 45 additions & 6 deletions src/geophires_x/AGSWellBores.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import itertools as itern

from .WellBores import WellBores, RameyCalc, ProdPressureDropAndPumpingPowerUsingIndexes, WellPressureDrop, \
ProdPressureDropsAndPumpingPowerUsingImpedenceModel
ProdPressureDropsAndPumpingPowerUsingImpedenceModel, calculate_total_drilling_lengths_m

from geophires_x.GeoPHIRESUtils import viscosity_water_Pa_sec

Expand Down Expand Up @@ -822,11 +822,53 @@ def Calculate(self, model: Model) -> None:
"""
model.logger.info(f'Init {__class__!s}: {sys._getframe().f_code.co_name}')

self.Tini = model.reserv.Trock.value # initialize the temperature to be the initial temperature of the reservoir
# initialize the temperature to be the initial temperature of the reservoir
self.Tini = model.reserv.Trock.value



# Calculate the total length of all drilling
if not self.injection_reservoir_depth.Provided:
self.injection_reservoir_depth.value = model.reserv.depth.quantity().to('km').magnitude
else:
self.injection_reservoir_depth.value = self.injection_reservoir_depth.quantity().to('km').magnitude

if not hasattr(self, 'junction_depth'):
# This must be a cylindrical reservoir for CLGS or Wanju method
input_vert_depth_km = model.reserv.InputDepth.quantity().to('km').magnitude
output_vert_depth_km = model.reserv.OutputDepth.quantity().to('km').magnitude
else:
# This must be SBT
self.tot_vert_m.value = self.vertical_section_length.quantity().to('km').magnitude
input_vert_depth_km = self.vertical_section_length.quantity().to('km').magnitude
output_vert_depth_km = self.lateral_endpoint_depth.quantity().to('km').magnitude
junction_depth_km = self.junction_depth.quantity().to('km').magnitude
angle_rad = ((90.0 * np.pi) / 180) - self.lateral_inclination_angle.quantity().to('radians').magnitude


self.total_drilled_length.value, self.tot_vert_m.value, self.tot_lateral_m.value, self.tot_to_junction_m.value = \
calculate_total_drilling_lengths_m(self.Configuration.value,
self.numnonverticalsections.value,
self.Nonvertical_length.value / 1000.0,
input_vert_depth_km,
output_vert_depth_km,
self.nprod.value,
self.ninj.value)
self.total_drilled_length.value = self.total_drilled_length.value / 1000.0 # convert to km






# decide what sort of AGS model we will use. Default is CLGS, but if the temperature is too high or there are
# multiple nonvertical sections, we will use SBT or Wanju code
if self.Tini > 375.0 or self.numnonverticalsections.value > 1:
# must be a multilateral setup or too hot for CLGS, so must try to use wanju code.
if self.Tini > 375.0:
msg = 'In AGS, but forced to use Wanju code because initial reservoir temperature is too high for CLGS'
msg = 'In AGS, but cannot use CLGS database because initial reservoir temperature is too high for CLGS'
msg - msg + ' or the number of nonvertical sections is greater than 1. Switching to SBT,'
msg = msg + ' then falling back to Wanju Yuan code as last resort.'
model.logger.warning(msg)
print(f'Warning: {msg}')

Expand Down Expand Up @@ -1033,6 +1075,3 @@ def Calculate(self, model: Model) -> None:
self.PumpingPower.value = [max(x, 0.) for x in self.PumpingPower.value]

model.logger.info(f'complete {str(__class__)}: {sys._getframe().f_code.co_name}')

def __str__(self):
return 'AGSWellBores'
159 changes: 26 additions & 133 deletions src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,6 @@
from geophires_x.Units import *


def calculate_total_drilling_lengths_m(Configuration, numnonverticalsections: int, nonvertical_length_km: float,
InputDepth_km: float, OutputDepth_km: float, nprod:int, ninj:int) -> tuple:
"""
returns the total length, vertical length, and non-vertical lengths, depending on the configuration
:param Configuration: Configuration of the well
:type Configuration: :class:`~geophires
:param numnonverticalsections: number of non-vertical sections
:type numnonverticalsections: int
:param nonvertical_length_km: length of non-vertical sections in km
:type nonvertical_length_km: float
:param InputDepth_km: depth of the well in km
:type InputDepth_km: float
:param OutputDepth_km: depth of the output end of the well in km, if U shaped, and not horizontal
:type OutputDepth_km: float
:param nprod: number of production wells
:type nprod: int
:param ninj: number of injection wells
:return: total length, vertical length, and horizontal lengths in meters
:rtype: tuple
"""
if Configuration == Configuration.ULOOP:
# Total drilling depth of both wells and laterals in U-loop [m]
vertical_pipe_length_m = (nprod * InputDepth_km * 1000.0) + (ninj * OutputDepth_km * 1000.0)
nonvertical_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
elif Configuration == Configuration.COAXIAL:
# Total drilling depth of well and lateral in co-axial case [m]
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
nonvertical_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
elif Configuration == Configuration.VERTICAL:
# Total drilling depth of well in vertical case [m]
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
nonvertical_pipe_length_m = 0.0
elif Configuration == Configuration.L:
# Total drilling depth of well in L case [m]
vertical_pipe_length_m = (nprod + ninj) * InputDepth_km * 1000.0
nonvertical_pipe_length_m = numnonverticalsections * nonvertical_length_km * 1000.0
else:
raise ValueError(f'Invalid Configuration: {Configuration}')

tot_pipe_length_m = vertical_pipe_length_m + nonvertical_pipe_length_m
return tot_pipe_length_m, vertical_pipe_length_m, nonvertical_pipe_length_m


def calculate_cost_of_one_vertical_well(model: Model, depth_m: float, well_correlation: int,
vertical_drilling_cost_per_m: float,
fixed_well_cost_name: str, well_cost_adjustment_factor: float) -> float:
Expand Down Expand Up @@ -195,60 +152,16 @@ def calculate_cost_of_non_vertical_section(model: Model, length_m: float, well_c
if not NonverticalsCased:
# assume that casing & cementing costs 50% of drilling costs
casing_factor = 0.5

if model.economics.Nonvertical_drilling_cost_per_m.Provided or well_correlation is WellDrillingCostCorrelation.SIMPLE:
cost_of_non_vertical_section = casing_factor * ((num_nonvertical_sections * nonvertical_drilling_cost_per_m * length_per_section_m)) * 1E-6
elif well_correlation is WellDrillingCostCorrelation.VERTICAL_SMALL:
cost_per_section = (0.30212 * length_per_section_m ** 2 + 584.91124 * length_per_section_m + 751368.47270) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.VERTICAL_LARGE:
cost_per_section = (0.28180 * length_per_section_m ** 2 + 1275.52130 * length_per_section_m + 632315.12640) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.DEVIATED_SMALL:
cost_per_section = (0.28977 * length_per_section_m ** 2 + 882.15067 * length_per_section_m + 680562.50150) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.DEVIATED_LARGE:
cost_per_section = (0.25528 * length_per_section_m ** 2 + 1716.71568 * length_per_section_m + 500866.89110) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.VERTICAL_SMALL_INT1:
cost_per_section = (0.13710 * length_per_section_m ** 2 + 129.61033 * length_per_section_m + 1205587.57100) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.VERTICAL_LARGE_INT1:
cost_per_section = (0.18927 * length_per_section_m ** 2 + 293.45174 * length_per_section_m + 1326526.31300) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.DEVIATED_SMALL_INT1:
cost_per_section = (0.15340 * length_per_section_m ** 2 + 120.31700 * length_per_section_m + 1431801.54400) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.DEVIATED_LARGE_INT1:
cost_per_section = (0.19950 * length_per_section_m ** 2 + 296.13011 * length_per_section_m + 1697867.70900) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.VERTICAL_SMALL_INT2:
cost_per_section = (0.00804 * length_per_section_m ** 2 + 455.60507 * length_per_section_m + 921007.68680) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.VERTICAL_LARGE_INT2:
cost_per_section = (0.00315 * length_per_section_m ** 2 + 782.69676 * length_per_section_m + 983620.25270) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.DEVIATED_SMALL_INT2:
cost_per_section = (0.00854 * length_per_section_m ** 2 + 506.08357 * length_per_section_m + 1057330.39000) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.DEVIATED_LARGE_INT2:
cost_per_section = (0.00380 * length_per_section_m ** 2 + 838.90249 * length_per_section_m + 1181947.04400) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.VERTICAL_SMALL_IDEAL:
cost_per_section = (0.00252 * length_per_section_m ** 2 + 439.44503 * length_per_section_m + 590611.90110) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.VERTICAL_LARGE_IDEAL:
cost_per_section = (-0.00240 * length_per_section_m ** 2 + 752.93946 * length_per_section_m + 524337.65380) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.DEVIATED_SMALL_IDEAL:
cost_per_section = (0.00719 * length_per_section_m ** 2 + 455.85233 * length_per_section_m + 753377.73080) * 1E-6
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section
elif well_correlation is WellDrillingCostCorrelation.DEVIATED_LARGE_IDEAL:
cost_per_section = (0.00376 * length_per_section_m ** 2 + 762.52696 * length_per_section_m + 765103.07690) * 1E-6
else:
cost_per_section = well_correlation.calculate_cost_MUSD(length_per_section_m)
cost_of_non_vertical_section = casing_factor * num_nonvertical_sections * cost_per_section

# account for adjustment factor

cost_of_non_vertical_section = well_cost_adjustment_factor * cost_of_non_vertical_section

return cost_of_non_vertical_section


Expand Down Expand Up @@ -1886,8 +1799,14 @@ def __init__(self, model: Model):
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
)
self.cost_nonvertical_section = self.OutputParameterDict[self.cost_nonvertical_section.Name] = OutputParameter(
Name="Cost of the non-vertical section of a well",
self.cost_lateral_section = self.OutputParameterDict[self.cost_lateral_section.Name] = OutputParameter(
Name="Cost of the (multi-) lateral section of a well",
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
)
self.cost_to_junction_section = self.OutputParameterDict[self.cost_to_junction_section.Name] = OutputParameter(
Name="Cost of the section of a well from bottom of vertical to junction with laterals",
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS
Expand Down Expand Up @@ -2332,62 +2251,36 @@ def Calculate(self, model: Model) -> None:
self.Cwell.value = ((self.cost_one_production_well.value * model.wellbores.nprod.value) +
(self.cost_one_injection_well.value * model.wellbores.ninj.value))
else:
if hasattr(model.wellbores, 'numnonverticalsections') and model.wellbores.numnonverticalsections.Provided:
self.cost_nonvertical_section.value = 0.0
if not model.wellbores.IsAGS.value:
input_vert_depth_km = model.reserv.depth.quantity().to('km').magnitude
output_vert_depth_km = 0.0
else:
input_vert_depth_km = model.reserv.InputDepth.quantity().to('km').magnitude
output_vert_depth_km = model.reserv.OutputDepth.quantity().to('km').magnitude
model.wellbores.injection_reservoir_depth.value = input_vert_depth_km

tot_m, tot_vert_m, tot_horiz_m = calculate_total_drilling_lengths_m(model.wellbores.Configuration.value,
model.wellbores.numnonverticalsections.value,
model.wellbores.Nonvertical_length.value / 1000.0,
input_vert_depth_km,
output_vert_depth_km,
model.wellbores.nprod.value,
model.wellbores.ninj.value)

else:
tot_m = tot_vert_m = model.reserv.depth.quantity().to('km').magnitude
tot_horiz_m = 0.0
if not model.wellbores.injection_reservoir_depth.Provided:
model.wellbores.injection_reservoir_depth.value = model.reserv.depth.quantity().to('km').magnitude
else:
model.wellbores.injection_reservoir_depth.value = model.wellbores.injection_reservoir_depth.quantity().to('km').magnitude

self.cost_one_production_well.value = calculate_cost_of_one_vertical_well(model, model.reserv.depth.quantity().to('m').magnitude,
# 1.05 for 5% indirect costs
self.cost_one_production_well.value = 1.05 * calculate_cost_of_one_vertical_well(model, model.reserv.depth.quantity().to('m').magnitude,
self.wellcorrelation.value,
self.Vertical_drilling_cost_per_m.value,
self.per_production_well_cost.Name,
self.production_well_cost_adjustment_factor.value)
if model.wellbores.ninj.value == 0:
self.cost_one_injection_well.value = -1.0
self.cost_one_injection_well.value = 0.0
else:
self.cost_one_injection_well.value = calculate_cost_of_one_vertical_well(model,
model.wellbores.injection_reservoir_depth.value * 1000.0,
self.wellcorrelation.value,
self.Vertical_drilling_cost_per_m.value,
self.per_injection_well_cost.Name,
self.injection_well_cost_adjustment_factor.value)
# Assume the production and injection wells cost the same thing
# unless we are redirectiong the flow to a different reservoir in the overpressure scenario
# 1.05 for 5% indirect costs
self.cost_one_injection_well.value = self.cost_one_production_well.value

if hasattr(model.wellbores, 'numnonverticalsections') and model.wellbores.numnonverticalsections.Provided:
self.cost_nonvertical_section.value = calculate_cost_of_non_vertical_section(model, tot_horiz_m,
# 1.05 for 5% indirect costs
self.cost_lateral_section.value = 1.05 * calculate_cost_of_non_vertical_section(model, model.wellbores.tot_lateral_m.value,
self.wellcorrelation.value,
self.Nonvertical_drilling_cost_per_m.value,
model.wellbores.numnonverticalsections.value,
self.per_injection_well_cost.Name,
model.wellbores.NonverticalsCased.value,
self.production_well_cost_adjustment_factor.value)
else:
self.cost_nonvertical_section.value = 0.0
self.cost_lateral_section.value = 0.0

# cost of the well field
# 1.05 for 5% indirect costs
self.Cwell.value = 1.05 * ((self.cost_one_production_well.value * model.wellbores.nprod.value) +
(self.cost_one_injection_well.value * model.wellbores.ninj.value) +
self.cost_nonvertical_section.value)
self.Cwell.value = (self.cost_one_production_well.value * model.wellbores.nprod.value) + \
(self.cost_one_injection_well.value * model.wellbores.ninj.value) + \
self.cost_lateral_section.value

# reservoir stimulation costs (M$/injection well). These are calculated whether totalcapcost.Valid = 1
if self.ccstimfixed.Valid:
Expand Down
Loading

0 comments on commit d9f6009

Please sign in to comment.