Skip to content

Fervo_Project_Cape-4 multilaterals #80

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions docs/Fervo_Project_Cape-4.md

Large diffs are not rendered by default.

26 changes: 23 additions & 3 deletions src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1633,18 +1633,29 @@ def __init__(self, model: Model):
f'Provide {self.ccexplfixed.Name} to override the default correlation and set your own cost.'
)

# noinspection SpellCheckingInspection
self.Cwell = self.OutputParameterDict[self.Cwell.Name] = OutputParameter(
Name="Wellfield cost",
display_name='Drilling and completion costs',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,

# See TODO re:parameterizing indirect costs at src/geophires_x/Economics.py:652
# (https://github.com/NREL/GEOPHIRES-X/issues/383)
# TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor
ToolTipText="Includes total drilling and completion cost of all injection and production wells and "
"laterals, plus 5% indirect costs."
)
self.drilling_and_completion_costs_per_well = self.OutputParameterDict[
self.drilling_and_completion_costs_per_well.Name] = OutputParameter(
Name='Drilling and completion costs per well',
UnitType=Units.CURRENCY,
PreferredUnits=CurrencyUnit.MDOLLARS,
CurrentUnits=CurrencyUnit.MDOLLARS,

# TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor
ToolTipText='Includes total drilling and completion cost per well, '
'including injection and production wells and laterals, plus 5% indirect costs.'
)
self.Coamwell = self.OutputParameterDict[self.Coamwell.Name] = OutputParameter(
Name="O&M Wellfield cost",
display_name='Wellfield maintenance costs',
Expand Down Expand Up @@ -2313,7 +2324,9 @@ def Calculate(self, model: Model) -> None:
else:
self.cost_lateral_section.value = 0.0
# cost of the well field
# 1.05 for 5% indirect costs - see TODO re:parameterizing at src/geophires_x/Economics.py:652

# 1.05 for 5% indirect costs
# TODO https://github.com/NREL/GEOPHIRES-X/issues/383?title=Parameterize+indirect+cost+factor
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_lateral_section.value)
Expand Down Expand Up @@ -2972,6 +2985,7 @@ def calculate_cashflow(self, model: Model) -> None:
for i in range(1, model.surfaceplant.plant_lifetime.value + model.surfaceplant.construction_years.value, 1):
self.TotalCummRevenue.value[i] = self.TotalCummRevenue.value[i-1] + self.TotalRevenue.value[i]

# noinspection SpellCheckingInspection
def _calculate_derived_outputs(self, model: Model) -> None:
"""
Subclasses should call _calculate_derived_outputs at the end of their Calculate methods to populate output
Expand All @@ -2988,5 +3002,11 @@ def _calculate_derived_outputs(self, model: Model) -> None:
self.real_discount_rate.value = self.discountrate.quantity().to(convertible_unit(
self.real_discount_rate.CurrentUnits)).magnitude

if hasattr(self, 'Cwell') and hasattr(model.wellbores, 'nprod') and hasattr(model.wellbores, 'ninj'):
self.drilling_and_completion_costs_per_well.value = (
self.Cwell.value /
(model.wellbores.nprod.value + model.wellbores.ninj.value)
)

def __str__(self):
return "Economics"
24 changes: 3 additions & 21 deletions src/geophires_x/EconomicsSam.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@
project_vir_parameter,
project_payback_period_parameter,
)
from geophires_x.GeoPHIRESUtils import is_float, is_int
from geophires_x.GeoPHIRESUtils import is_float, is_int, sig_figs
from geophires_x.OptionList import EconomicModel, EndUseOptions
from geophires_x.Parameter import Parameter, OutputParameter, floatParameter
from geophires_x.Units import convertible_unit, EnergyCostUnit, CurrencyUnit, Units, PercentUnit
from geophires_x.Units import convertible_unit, EnergyCostUnit, CurrencyUnit, Units


@dataclass
Expand Down Expand Up @@ -162,7 +162,7 @@ def calculate_sam_economics(model: Model) -> SamEconomicsCalculations:
cash_flow = _calculate_sam_economics_cash_flow(model, single_owner)

def sf(_v: float, num_sig_figs: int = 5) -> float:
return _sig_figs(_v, num_sig_figs)
return sig_figs(_v, num_sig_figs)

sam_economics: SamEconomicsCalculations = SamEconomicsCalculations(sam_cash_flow_profile=cash_flow)
sam_economics.lcoe_nominal.value = sf(single_owner.Outputs.lcoe_nom)
Expand Down Expand Up @@ -435,21 +435,3 @@ def _ppa_pricing_model(

def _get_max_total_generation_kW(model: Model) -> float:
return np.max(model.surfaceplant.ElectricityProduced.quantity().to(convertible_unit('kW')).magnitude)


def _sig_figs(val: float | list | tuple, num_sig_figs: int) -> float:
"""
TODO move to utilities, probably
"""

if val is None:
return None

if isinstance(val, list) or isinstance(val, tuple):
return [_sig_figs(v, num_sig_figs) for v in val]

try:
return float('%s' % float(f'%.{num_sig_figs}g' % val)) # pylint: disable=consider-using-f-string
except TypeError:
# TODO warn
return val
13 changes: 13 additions & 0 deletions src/geophires_x/GeoPHIRESUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,3 +642,16 @@ def is_float(o: Any) -> bool:
else:
return True


def sig_figs(val: float | list | tuple, num_sig_figs: int) -> float:
if val is None:
return None

if isinstance(val, list) or isinstance(val, tuple):
return [sig_figs(v, num_sig_figs) for v in val]

try:
return float('%s' % float(f'%.{num_sig_figs}g' % val)) # pylint: disable=consider-using-f-string
except TypeError:
# TODO warn
return val
3 changes: 2 additions & 1 deletion src/geophires_x/Outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,8 @@ def PrintOutputs(self, model: Model):
f.write(f' Drilling and completion costs per production well: {econ.cost_one_production_well.value:10.2f} ' + econ.cost_one_production_well.CurrentUnits.value + NL)
f.write(f' Drilling and completion costs per injection well: {econ.cost_one_injection_well.value:10.2f} ' + econ.cost_one_injection_well.CurrentUnits.value + NL)
else:
f.write(f' Drilling and completion costs per well: {model.economics.Cwell.value/(model.wellbores.nprod.value+model.wellbores.ninj.value):10.2f} ' + model.economics.Cwell.CurrentUnits.value + NL)
cpw_label = Outputs._field_label(econ.drilling_and_completion_costs_per_well.display_name, 47)
f.write(f' {cpw_label}{econ.drilling_and_completion_costs_per_well.value:10.2f} {econ.Cwell.CurrentUnits.value}\n')
f.write(f' {econ.Cstim.display_name}: {econ.Cstim.value:10.2f} {econ.Cstim.CurrentUnits.value}\n')
f.write(f' Surface power plant costs: {model.economics.Cplant.value:10.2f} ' + model.economics.Cplant.CurrentUnits.value + NL)
if model.surfaceplant.plant_type.value == PlantType.ABSORPTION_CHILLER:
Expand Down
14 changes: 12 additions & 2 deletions src/geophires_x/WellBores.py
Original file line number Diff line number Diff line change
Expand Up @@ -1035,14 +1035,24 @@ def __init__(self, model: Model):
ErrMessage="assume default for Non-vertical Wellbore Diameter (0.156 m)",
ToolTipText="Non-vertical Wellbore Diameter"
)

max_allowed_total_wells = max(self.nprod.AllowableRange) + max(self.ninj.AllowableRange)
max_allowed_laterals_per_well_when_max_wells = 3
"""Arbitrary upper limit, could be increased in future if needed"""

# noinspection SpellCheckingInspection
self.numnonverticalsections = self.ParameterDict[self.numnonverticalsections.Name] = intParameter(
"Number of Multilateral Sections",
DefaultValue=0,
AllowableRange=list(range(0, 101, 1)),
AllowableRange=list(range(0, max_allowed_total_wells * max_allowed_laterals_per_well_when_max_wells, 1)),
UnitType=Units.NONE,
ErrMessage="assume default for Number of Nonvertical Wellbore Sections (0)",
ToolTipText="Number of Nonvertical Wellbore Sections"
ToolTipText='Number of Nonvertical Wellbore Sections, aka laterals or horizontals. '
'Note that this is the total number of sections for the entire project and not the number of '
'sections per well. For example, a project with 2 injectors and 2 producers with 3 laterals '
'per well should set Number of Multilateral Sections = 2 * 2 * 3 = 12.'
)

self.NonverticalsCased = self.ParameterDict[self.NonverticalsCased.Name] = boolParameter(
"Multilaterals Cased",
DefaultValue=False,
Expand Down
4 changes: 2 additions & 2 deletions src/geophires_x_schema_generator/geophires-request.json
Original file line number Diff line number Diff line change
Expand Up @@ -947,13 +947,13 @@
"maximum": 100.0
},
"Number of Multilateral Sections": {
"description": "Number of Nonvertical Wellbore Sections",
"description": "Number of Nonvertical Wellbore Sections, aka laterals or horizontals. Note that this is the total number of sections for the entire project and not the number of sections per well. For example, a project with 2 injectors and 2 producers with 3 laterals per well should set Number of Multilateral Sections = 2 * 2 * 3 = 12.",
"type": "integer",
"units": null,
"category": "Well Bores",
"default": 0,
"minimum": 0,
"maximum": 100
"maximum": 1199
},
"Multilaterals Cased": {
"description": "If set to True, casing & cementing are assumed to comprise 50% of drilling costs (doubling cost compared to uncased).",
Expand Down
6 changes: 5 additions & 1 deletion src/geophires_x_schema_generator/geophires-result.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,11 @@
"description": "Wellfield cost. Includes total drilling and completion cost of all injection and production wells and laterals, plus 5% indirect costs.",
"units": "MUSD"
},
"Drilling and completion costs per well": {},
"Drilling and completion costs per well": {
"type": "number",
"description": "Includes total drilling and completion cost per well, including injection and production wells and laterals, plus 5% indirect costs.",
"units": "MUSD"
},
"Drilling and completion costs per production well": {},
"Drilling and completion costs per injection well": {},
"Drilling and completion costs per vertical production well": {},
Expand Down
Loading
Loading