Skip to content
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

Added additional estimates from GR website #247

Closed
wants to merge 14 commits into from
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
21 changes: 15 additions & 6 deletions src/geophires_x/CylindricalReservoir.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import numpy as np
from pint.facets.plain import PlainQuantity

from geophires_x.GeoPHIRESUtils import density_water_kg_per_m3, static_pressure_MPa, quantity
from geophires_x.GeoPHIRESUtils import density_water_kg_per_m3, lithostatic_pressure_MPa, quantity

from geophires_x.GeoPHIRESUtils import heat_capacity_water_J_per_kg_per_K
import geophires_x.Model as Model
Expand Down Expand Up @@ -43,6 +43,7 @@ def __init__(self, model: Model):

self.InputDepth = self.ParameterDict[self.InputDepth.Name] = floatParameter(
"Cylindrical Reservoir Input Depth",
value=3.0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove - only DefaultValue should be specified, not value (which is redundant with DefaultValue in this case anyway)

DefaultValue=3.0,
Min=0.1,
Max=15,
Expand All @@ -56,6 +57,7 @@ def __init__(self, model: Model):

self.OutputDepth = self.ParameterDict[self.OutputDepth.Name] = floatParameter(
"Cylindrical Reservoir Output Depth",
value=self.InputDepth.value,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove - only DefaultValue should be specified, not value (which is redundant with DefaultValue in this case anyway)

DefaultValue=self.InputDepth.value,
Min=0.1,
Max=15,
Expand All @@ -68,6 +70,7 @@ def __init__(self, model: Model):
)
self.Length = self.ParameterDict[self.Length.Name] = floatParameter(
"Cylindrical Reservoir Length",
value=4.0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove - only DefaultValue should be specified, not value (which is redundant with DefaultValue in this case anyway)

DefaultValue=4.0,
Min=0.1,
Max=10.0,
Expand All @@ -80,6 +83,7 @@ def __init__(self, model: Model):
)
self.RadiusOfEffect = self.ParameterDict[self.RadiusOfEffect.Name] = floatParameter(
"Cylindrical Reservoir Radius of Effect",
value=30.0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove - only DefaultValue should be specified, not value (which is redundant with DefaultValue in this case anyway)

DefaultValue=30.0,
Min=0,
Max=1000.0,
Expand All @@ -92,13 +96,14 @@ def __init__(self, model: Model):
)
self.RadiusOfEffectFactor = self.ParameterDict[self.RadiusOfEffectFactor.Name] = floatParameter(
"Cylindrical Reservoir Radius of Effect Factor",
value=1.0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove - only DefaultValue should be specified, not value (which is redundant with DefaultValue in this case anyway)

DefaultValue=1.0,
Min=0.0,
Max=10.0,
UnitType=Units.PERCENT,
PreferredUnits=PercentUnit.TENTH,
CurrentUnits=PercentUnit.TENTH,
ErrMessage="assume default cylindrical reservoir radius of effect reduction factor (0.1)",
ErrMessage="assume default cyclindrical reservoir radius of effect reduction factor (0.1)",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo "cyclindrical"

ToolTipText="The radius of effect reduction factor - to account for the fact that we cannot extract 100%"
+ " of the heat in the cylinder.",
)
Expand All @@ -109,6 +114,7 @@ def __init__(self, model: Model):
# internal values required for calculations
self.depth = self.ParameterDict[self.depth.Name] = floatParameter(
"Drilled length",
value=0.0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove - only DefaultValue should be specified, not value (which is redundant with DefaultValue in this case anyway)

DefaultValue=0.0,
Min=0.0,
Max=150,
Expand All @@ -121,6 +127,7 @@ def __init__(self, model: Model):
)
self.waterloss = self.ParameterDict[self.waterloss.Name] = floatParameter(
"Water Loss Fraction",
value=0.0,
DefaultValue=0.0,
Min=0.0,
Max=0.99,
Expand Down Expand Up @@ -222,8 +229,8 @@ def Calculate(self, model: Model) -> None:
# initialize with the Initial reservoir temperature
self.Tresoutput.value = np.array(len(self.timevector.value) * [self.Trock.value])
# depth in this case is actually the total length of the drilled assembly
# as the average of the InputDepth and the OutputDepth
self.depth.value = (self.InputDepth.value + self.OutputDepth.value) / 2.0
self.depth.value = ((self.InputDepth.quantity() + self.OutputDepth.quantity() + self.Length.quantity()
).to(self.depth.CurrentUnits).magnitude)

# Total volume of all laterals but hollow cylinder - doesn't include drilled-out area, units = m3
self.resvolcalc.value = (
Expand All @@ -244,17 +251,19 @@ def Calculate(self, model: Model) -> None:
) / 1e15 # 10^15 J
self.cpwater.value = heat_capacity_water_J_per_kg_per_K(
model.wellbores.Tinj.value * 0.5 + (self.Trock.value * 0.9 + model.wellbores.Tinj.value * 0.1) * 0.5,
pressure=self.hydrostatic_pressure()
pressure=model.reserv.lithostatic_pressure()
)
self.rhowater.value = density_water_kg_per_m3(
model.wellbores.Tinj.value * 0.5 + (self.Trock.value * 0.9 + model.wellbores.Tinj.value * 0.1) * 0.5,
pressure=self.hydrostatic_pressure()
pressure=model.reserv.lithostatic_pressure()
Comment on lines -247 to +258
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We just changed these from lithostatic to hydrostatic last month (#217 / c77f418), is this change back intentional? If so, what's the rationale?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had merge conflicts during the class which I had to resolve quickly. We should go back to hydrostatic.

)

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

def lithostatic_pressure(self) -> PlainQuantity:
"""@override"""

Standard reservoir implementation uses depth but CylindricalReservoir sets depth to total drilled length
"""
return quantity(static_pressure_MPa(self.rhorock.quantity().to('kg/m**3').magnitude,
self.InputDepth.quantity().to('m').magnitude), 'MPa')
76 changes: 73 additions & 3 deletions src/geophires_x/Economics.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ def CalculateLCOELCOHLCOC(self, model: Model) -> tuple:
NPVgrt = self.GTR.value / (1 - self.GTR.value) * (NPVcap + NPVoandm + NPVfc + NPVit - NPVitc)
LCOH = (NPVcap + NPVoandm + NPVfc + NPVit + NPVgrt - NPVitc) / np.sum(
model.surfaceplant.HeatkWhProduced.value * inflationvector * discountvector) * 1E8
LCOH = self.LCOH.value * 2.931 # $/MMBTU
LCOH = LCOH * 2.931 # $/MMBTU
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't seem possible without impacting example outputs, but no example output changes are in the PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a one line bug I found some weeks ago and committed but forgot to do a PR, so it got folded into this PR. This bug showed itself in only a couple of cases, but the fix is correct. There may be some output changes, but maybe not.


elif model.surfaceplant.enduse_option.value == EndUseOptions.HEAT and model.surfaceplant.plant_type.value == PlantType.DISTRICT_HEATING:
PumpingCosts = model.surfaceplant.PumpingkWh.value * model.surfaceplant.electricity_cost_to_buy.value / 1E6
Expand Down Expand Up @@ -1497,11 +1497,46 @@ def __init__(self, model: Model):
self.jobs_created_per_MW_electricity.Name] = floatParameter(
"Estimated Jobs Created per MW of Electricity Produced",
DefaultValue=2.13,
UnitType=Units.NONE,
UnitType=Units.JOBS_PER_ENERGY,
PreferredUnits=JobsPerEnergyUnit.JOBSPERMW,
CurrentUnits=JobsPerEnergyUnit.JOBSPERMW,
Required=False,
ToolTipText="Estimated jobs created per MW of electricity produced, per https://geothermal.org/resources/geothermal-basics"
)

self.property_tax_per_MW_electricity = self.ParameterDict[
self.property_tax_per_MW_electricity.Name] = floatParameter(
"Estimated Property Tax per MW of Electricity Produced",
DefaultValue=0.210,
UnitType=Units.ROYALTY_PER_ENERGY,
PreferredUnits=RoyaltyPerEnergyUnit.ROYALTYPERMW,
CurrentUnits=RoyaltyPerEnergyUnit.ROYALTYPERMW,
Required=False,
ToolTipText="Estimated property tax per MW of electricity produced, per https://geothermal.org/resources/geothermal-basics"
)

self.gov_royalty_per_MW_electricity = self.ParameterDict[
self.gov_royalty_per_MW_electricity.Name] = floatParameter(
"Estimated Governmental Royalty per MW of Electricity Produced",
DefaultValue=0.315,
UnitType=Units.ROYALTY_PER_ENERGY,
PreferredUnits=RoyaltyPerEnergyUnit.ROYALTYPERMW,
CurrentUnits=RoyaltyPerEnergyUnit.ROYALTYPERMW,
Required=False,
ToolTipText="Estimated Estimated Governmental Royalty per MW of electricity produced, per https://geothermal.org/resources/geothermal-basics"
)

self.total_royalty_per_MW_electricity = self.ParameterDict[
self.total_royalty_per_MW_electricity.Name] = floatParameter(
"Estimated Jobs Created per MW of Electricity Produced",
DefaultValue=0.420,
UnitType=Units.ROYALTY_PER_ENERGY,
PreferredUnits=RoyaltyPerEnergyUnit.ROYALTYPERMW,
CurrentUnits=RoyaltyPerEnergyUnit.ROYALTYPERMW,
Required=False,
ToolTipText="Estimated total royalty per MW of electricity produced, per https://geothermal.org/resources/geothermal-basics"
)

# local variable initialization
self.CAPEX_cost_electricity_plant = 0.0
self.CAPEX_cost_heat_plant = 0.0
Expand Down Expand Up @@ -1813,7 +1848,27 @@ def __init__(self, model: Model):
)
self.jobs_created = self.OutputParameterDict[self.jobs_created.Name] = OutputParameter(
Name="Estimated Jobs Created",
UnitType=Units.NONE,
UnitType=Units.JOBS,
CurrentUnits=JobsUnit.JOBS,
PreferredUnits=JobsUnit.JOBS
Comment on lines +1851 to +1853
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the value in defining a specific unit for this - counts are unitless and represented as Units.NONE i.e.

UnitType=Units.NONE,

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah, I was struggling with the code during the class, so I added this just to get it run. I will set it back to what is right - but this code does work... just doesn't make alot of sense.

)
self.property_tax_created = self.OutputParameterDict[self.property_tax_created.Name] = OutputParameter(
Name="Estimated amount of property tax that will be paid",
UnitType=Units.CURRENCY,
CurrentUnits=CurrencyUnit.MDOLLARS,
PreferredUnits=CurrencyUnit.MDOLLARS
)
self.total_royalties_created = self.OutputParameterDict[self.total_royalties_created.Name] = OutputParameter(
Name="Estimated total royalties that will have to be paid",
UnitType=Units.CURRENCY,
CurrentUnits=CurrencyUnit.MDOLLARS,
PreferredUnits=CurrencyUnit.MDOLLARS
)
self.gov_royalties_created = self.OutputParameterDict[self.gov_royalties_created.Name] = OutputParameter(
Name="Estimated governmental royalties to be paid",
UnitType=Units.CURRENCY,
CurrentUnits=CurrencyUnit.MDOLLARS,
PreferredUnits=CurrencyUnit.MDOLLARS
)

model.logger.info(f'Complete {__class__!s}: {sys._getframe().f_code.co_name}')
Expand Down Expand Up @@ -2892,6 +2947,21 @@ def Calculate(self, model: Model) -> None:
np.average(model.surfaceplant.ElectricityProduced.quantity().to(
'MW').magnitude * self.jobs_created_per_MW_electricity.value))

# https://github.com/NREL/GEOPHIRES-X/issues/232
self.property_tax_created.value = (
np.average(model.surfaceplant.ElectricityProduced.quantity().to(
'MW').magnitude * self.property_tax_per_MW_electricity.value))

# https://github.com/NREL/GEOPHIRES-X/issues/232
self.total_royalties_created.value = (
np.average(model.surfaceplant.ElectricityProduced.quantity().to(
'MW').magnitude * self.total_royalty_per_MW_electricity.value))

# https://github.com/NREL/GEOPHIRES-X/issues/232
self.gov_royalties_created.value = (
np.average(model.surfaceplant.ElectricityProduced.quantity().to(
'MW').magnitude * self.gov_royalty_per_MW_electricity.value))

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

def __str__(self):
Expand Down
6 changes: 5 additions & 1 deletion src/geophires_x/Outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1641,7 +1641,11 @@ def PrintOutputs(self, model: Model):
f.write(f' CHP: Percent cost allocation for electrical plant: {model.economics.CAPEX_heat_electricity_plant_ratio.value*100.0:10.2f} %\n')

if model.surfaceplant.enduse_option.value in [EndUseOptions.ELECTRICITY]:
f.write(f' Estimated Jobs Created: {model.economics.jobs_created.value}\n')
f.write(f' Estimates from https://geothermal.org/resources/geothermal-basics:' + NL)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert to Estimated Jobs Created

  1. This would be a backwards-incompatible field name change
  2. Although I like the idea of citing on a per-field basis, we shouldn't include citations in field names as a general principle since this would make output very difficult to read. Rather, we should include citations in documentation and/or a documentation metadata field - filed tracking issue Include citation metadata in parameter descriptions #248
  3. This specific citation is already present in the parameter description https://nrel.github.io/GEOPHIRES-X/parameters.html#id11 -> ctrl+F "Estimated Jobs Created per MW of Electricity Produced"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking a second look - I see this is actually an additional line, not a replacement of estimated jobs created. Will follow up with additional thoughts on this, but making note that part of my comment is partially inaccurate

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that having the URL in the code is useful, but it is invisible to users who just use the UI. I think we need to show it in the UI in a backward compatible way. I will await your further comments before I change anything.

f.write(f' Estimated Jobs Created: {model.economics.jobs_created.value}' + NL)
f.write(f' Estimated Property tax that will be paid: {model.economics.property_tax_created.value:10.2f }' + model.economics.property_tax_created.PreferredUnits.value + NL)
f.write(f' Estimated total royalties that will be paid: {model.economics.total_royalties_created.value:10.2f }' + model.economics.total_royalties_created.PreferredUnits.value + NL)
f.write(f' Estimated government royalties to be paid: {model.economics.gov_royalties_created.value:10.2f }' + model.economics.gov_royalties_created.PreferredUnits.value+ NL)
Comment on lines +1646 to +1648
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there better/more precise phrasing that can eliminate the long and clunky "to be paid"/"that will be paid" suffixes here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will work on the phrasing

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But do you like the additional calculations based on the information used to generate the estimate of "jobs created?"



f.write(NL)
Expand Down
19 changes: 19 additions & 0 deletions src/geophires_x/Units.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class Units(IntEnum):
POWERPERUNITVOLUME = auto()
DECAY_RATE=auto()
INFLATION_RATE=auto()
JOBS_PER_ENERGY=auto()
ROYALTY_PER_ENERGY=auto()
JOBS = auto()


class TemperatureUnit(str, Enum):
Expand Down Expand Up @@ -350,3 +353,19 @@ class Decay_RateUnit(str,Enum):
class Inflation_RateUnit(str,Enum):
"""Decay rate Units"""
KPASCALPERYEAR = "kPa/yr"


class JobsPerEnergyUnit(str,Enum):
"""Jobs per energy Units"""
JOBSPERKW = "jobs/kW"
JOBSPERMW = "jobs/MW"
JOBSPERGW = "jobs/GW"

class RoyaltyPerEnergyUnit(str,Enum):
"""Royalty per energy Units"""
ROYALTYPERMW = "MUSD/MW"
ROYALTYPERKW = "MUSD/kW"
Comment on lines +364 to +367
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant and/or inconsistent with EnergyCostUnit

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, but I will check.


class JobsUnit(str,Enum):
"""Jobs Units"""
JOBS = "jobs"
Loading