Skip to content

Commit 619e6e4

Browse files
Merge pull request NREL#121 from softwareengineerprogrammer/main
CoolProp integration; Pressure-aware water property calculations (v3.4.2) NREL#113
2 parents 74181aa + 911cbb2 commit 619e6e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2733
-2365
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 3.4.0
2+
current_version = 3.4.2
33
commit = True
44
tag = True
55

.cookiecutterrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ default_context:
5454
sphinx_doctest: "no"
5555
sphinx_theme: "sphinx-py3doc-enhanced-theme"
5656
test_matrix_separate_coverage: "no"
57-
version: 3.4.0
57+
version: 3.4.2
5858
version_manager: "bump2version"
5959
website: "https://github.com/NREL"
6060
year_from: "2023"

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ Free software: `MIT license <LICENSE>`__
4747
:alt: Supported implementations
4848
:target: https://pypi.org/project/geophires-x
4949

50-
.. |commits-since| image:: https://img.shields.io/github/commits-since/NREL/GEOPHIRES-X/v3.4.0.svg
50+
.. |commits-since| image:: https://img.shields.io/github/commits-since/NREL/GEOPHIRES-X/v3.4.2.svg
5151
:alt: Commits since latest release
52-
:target: https://github.com/NREL/GEOPHIRES-X/compare/v3.4.0...main
52+
:target: https://github.com/NREL/GEOPHIRES-X/compare/v3.4.2...main
5353

5454
.. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat
5555
:target: https://nrel.github.io/GEOPHIRES-X

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
year = '2023'
1919
author = 'NREL'
2020
copyright = f'{year}, {author}'
21-
version = release = '3.4.0'
21+
version = release = '3.4.2'
2222

2323
pygments_style = 'trac'
2424
templates_path = ['./templates']

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def read(*names, **kwargs):
1313

1414
setup(
1515
name='geophires-x',
16-
version='3.4.0',
16+
version='3.4.2',
1717
license='MIT',
1818
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
1919
long_description='{}\n{}'.format(
@@ -76,6 +76,7 @@ def read(*names, **kwargs):
7676
'h5py',
7777
'scipy',
7878
'iapws',
79+
'coolprop',
7980
],
8081
extras_require={
8182
# eg:

src/geophires_x/AGSWellBores.py

Lines changed: 74 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
import os
1212
import math
1313
import numpy as np
14+
from pint.facets.plain import PlainQuantity
15+
1416
import geophires_x.Model as Model
1517
from .Parameter import floatParameter, intParameter, boolParameter, OutputParameter
16-
from .Reservoir import densitywater, heatcapacitywater
18+
from geophires_x.GeoPHIRESUtils import density_water_kg_per_m3
19+
from geophires_x.GeoPHIRESUtils import heat_capacity_water_J_per_kg_per_K
1720
from .Units import *
1821
from .OptionList import WorkingFluid, Configuration
1922

@@ -26,7 +29,7 @@
2629
from .WellBores import WellBores, RameyCalc, ProdPressureDropAndPumpingPowerUsingIndexes, WellPressureDrop, \
2730
ProdPressureDropsAndPumpingPowerUsingImpedenceModel
2831

29-
from geophires_x.GeoPHIRESUtils import ViscosityWater as viscositywater
32+
from geophires_x.GeoPHIRESUtils import viscosity_water_Pa_sec
3033

3134
esp2 = 10.0e-10
3235

@@ -162,7 +165,6 @@ def interp_kWt_avg(self, point):
162165
return self.GWhr * interpn(ivars, self.Wt, point) / (1000. * self.time[-1] * 86400. * 365.)
163166

164167

165-
# #############################point source/sink solution functions################################
166168

167169
def pointsource(self, yy, zz, yt, zt, ye, ze, alpha, sp, t):
168170
"""
@@ -200,7 +202,6 @@ def pointsource(self, yy, zz, yt, zt, ye, ze, alpha, sp, t):
200202
return z
201203

202204

203-
# #####Chebyshev approximation for numerical Laplace transformation integration from 1e-8 to 1e30###################
204205

205206
def chebeve_pointsource(self, yy, zz, yt, zt, ye, ze, alpha, sp) -> float:
206207
"""
@@ -237,29 +238,29 @@ def chebeve_pointsource(self, yy, zz, yt, zt, ye, ze, alpha, sp) -> float:
237238
return temp + (1 / sp * (math.exp(-sp * 1.0e5) - math.exp(-sp * 1.0e30))) / (ye * ze) / self.rhorock / self.cprock
238239

239240

240-
# ############################Duhamerl convolution method for closed-loop system######################################
241-
def laplace_solution(self, sp) -> float:
241+
242+
def laplace_solution(cls:WellBores, sp, pressure:PlainQuantity) -> float:
242243
"""
243244
Duhamel convolution method for closed-loop system
244245
:param sp: Laplace variable (1/s)
245246
:type sp: float
246247
:return: Toutletl
247248
:rtype: float
249+
:param pressure: Lithostatic pressure, per https://github.com/NREL/GEOPHIRES-X/issues/113#issuecomment-1941951134
248250
"""
249251

250252
Toutletl = 0.0
251-
ss = 1.0 / sp / chebeve_pointsource(self, self.y_well, self.z_well, self.y_well, self.z_well - 0.078,
252-
self.y_boundary, self.z_boundary, self.alpha_rock, sp)
253+
ss = 1.0 / sp / chebeve_pointsource(cls, cls.y_well, cls.z_well, cls.y_well, cls.z_well - 0.078,
254+
cls.y_boundary, cls.z_boundary, cls.alpha_rock, sp)
253255

254-
Toutletl = (self.Tini - self.Tinj.value) / sp * np.exp(
255-
-sp * ss / self.q_circulation / 24.0 / densitywater(
256-
self.Tini) / heatcapacitywater(
257-
self.Tini) * self.Nonvertical_length.value - sp / self.velocity * self.Nonvertical_length.value)
256+
Toutletl = (cls.Tini - cls.Tinj.value) / sp * np.exp(
257+
-sp * ss / cls.q_circulation / 24.0 / density_water_kg_per_m3(
258+
cls.Tini, pressure=pressure) / heat_capacity_water_J_per_kg_per_K(
259+
cls.Tini, pressure=pressure) * cls.Nonvertical_length.value - sp / cls.velocity * cls.Nonvertical_length.value)
258260
return Toutletl
259261

260262

261-
# ###############################Numerical Laplace transformation algorithm#########################
262-
def inverselaplace(self, NL, MM):
263+
def inverselaplace(cls:WellBores, NL, MM, pressure:PlainQuantity):
263264
"""
264265
Numerical Laplace transformation algorithm
265266
:param NL: NL
@@ -303,13 +304,13 @@ def inverselaplace(self, NL, MM):
303304
MM = NL
304305

305306
FI = 0.0
306-
Az = DLN2 / self.time_operation.value
307+
Az = DLN2 / cls.time_operation.value
307308
Toutlet = 0.0
308309
for k in range(1, NL + 1):
309310
Z = Az * k
310-
Toutletl = laplace_solution(self, Z)
311+
Toutletl = laplace_solution(cls, Z, pressure)
311312
Toutlet += Toutletl * V[k]
312-
Toutlet = self.Tini - Az * Toutlet
313+
Toutlet = cls.Tini - Az * Toutlet
313314
return Toutlet
314315

315316

@@ -449,7 +450,7 @@ def __init__(self, model: Model):
449450
:type model: :class:`~geophires_x.Model.Model`
450451
:return: Nothing, and is used to initialize the class
451452
"""
452-
model.logger.info("Init " + str(__class__) + ": " + sys._getframe().f_code.co_name)
453+
model.logger.info(f'Init {__class__!s}: {sys._getframe().f_code.co_name}')
453454

454455
# Initialize the superclass first to gain access to those variables
455456
super().__init__(model)
@@ -856,7 +857,7 @@ def on_invalid_parameter_value(err_msg):
856857

857858
# Multilateral code
858859

859-
def CalculateNonverticalPressureDrop(self, model, time_operation: float, time_max: float, al: float):
860+
def CalculateNonverticalPressureDrop(self, model:Model, time_operation: float, time_max: float, al: float):
860861
"""
861862
Calculate nonvertical pressure drops - it will vary as the temperature varies
862863
:param model: The container class of the application, giving access to everything else, including the logger
@@ -876,8 +877,15 @@ def CalculateNonverticalPressureDrop(self, model, time_operation: float, time_ma
876877
year = math.trunc(time_operation / al)
877878

878879
# nonvertical wellbore fluid conditions based on current temperature
879-
rhowater = densitywater(self.NonverticalProducedTemperature.value[year])
880-
muwater = viscositywater(self.NonverticalProducedTemperature.value[year])
880+
rhowater = density_water_kg_per_m3(
881+
self.NonverticalProducedTemperature.value[year],
882+
pressure=model.reserv.lithostatic_pressure()
883+
)
884+
885+
muwater = viscosity_water_Pa_sec(
886+
self.NonverticalProducedTemperature.value[year],
887+
pressure=model.reserv.lithostatic_pressure()
888+
)
881889
vhoriz = self.q_circulation / rhowater / (math.pi / 4. * self.nonverticalwellborediameter.value ** 2)
882890

883891
# assume turbulent flow.
@@ -955,13 +963,19 @@ def Calculate(self, model: Model) -> None:
955963

956964
t = self.time_operation.value
957965
while self.time_operation.value <= self.time_max:
958-
# MIR figure out how to calculate year ands extract Tini from reserv Tresoutput array
966+
# MIR figure out how to calculate year and extract Tini from reserv Tresoutput array
959967
year = math.trunc(self.time_operation.value / self.al)
960-
self.NonverticalProducedTemperature.value[year] = inverselaplace(self, 16, 0)
968+
self.NonverticalProducedTemperature.value[year] = inverselaplace(
969+
self, 16, 0, model.reserv.lithostatic_pressure())
961970
# update alpha_fluid value based on next temperature of reservoir
962-
self.alpha_fluid = self.WaterThermalConductivity.value / densitywater(
963-
self.NonverticalProducedTemperature.value[year]) / heatcapacitywater(
964-
self.NonverticalProducedTemperature.value[year]) * 24.0 * 3600.0
971+
972+
self.alpha_fluid = self.WaterThermalConductivity.value / density_water_kg_per_m3(
973+
self.NonverticalProducedTemperature.value[year],
974+
pressure=model.reserv.lithostatic_pressure()
975+
) / heat_capacity_water_J_per_kg_per_K(
976+
self.NonverticalProducedTemperature.value[year],
977+
pressure=model.reserv.lithostatic_pressure()
978+
) * 24.0 * 3600.0
965979
self.time_operation.value += self.al
966980

967981
self.time_operation.value = t # set it back for use in later loop
@@ -972,7 +986,10 @@ def Calculate(self, model: Model) -> None:
972986
# Calculate the temperature drop as the fluid makes it way to the surface (or use a constant value)
973987
# if not Ramey, hard code a user-supplied temperature drop.
974988
self.ProdTempDrop.value = self.tempdropprod.value
975-
model.reserv.cpwater.value = heatcapacitywater(self.NonverticalProducedTemperature.value[0])
989+
model.reserv.cpwater.value = heat_capacity_water_J_per_kg_per_K(
990+
self.NonverticalProducedTemperature.value[0],
991+
pressure=model.reserv.lithostatic_pressure()
992+
)
976993
if self.rameyoptionprod.value:
977994
self.ProdTempDrop.value = RameyCalc(model.reserv.krock.value,
978995
model.reserv.rhorock.value,
@@ -992,10 +1009,17 @@ def Calculate(self, model: Model) -> None:
9921009
# Now use the parent's calculation to calculate the upgoing and downgoing pressure drops and pumping power
9931010
self.PumpingPower.value = [0.0] * len(self.ProducedTemperature.value) # initialize the array
9941011
if self.productionwellpumping.value:
995-
self.rhowaterinj = densitywater(model.reserv.Tsurf.value) * np.linspace(1, 1,
996-
len(self.ProducedTemperature.value))
997-
self.rhowaterprod = densitywater(model.reserv.Trock.value) * np.linspace(1, 1,
998-
len(self.ProducedTemperature.value))
1012+
self.rhowaterinj = density_water_kg_per_m3(
1013+
model.reserv.Tsurf.value,
1014+
pressure=model.reserv.lithostatic_pressure()
1015+
) * np.linspace(1, 1,
1016+
len(self.ProducedTemperature.value))
1017+
1018+
self.rhowaterprod = density_water_kg_per_m3(
1019+
model.reserv.Trock.value,
1020+
pressure=model.reserv.lithostatic_pressure()
1021+
) * np.linspace(1, 1, len(self.ProducedTemperature.value))
1022+
9991023
self.DPProdWell.value, f3, vprod, self.rhowaterprod = WellPressureDrop(model,
10001024
model.reserv.Tresoutput.value - self.ProdTempDrop.value / 4.0,
10011025
self.prodwellflowrate.value,
@@ -1020,19 +1044,19 @@ def Calculate(self, model: Model) -> None:
10201044
else: # PI is used for both the verticals
10211045
UpgoingPumpingPower, self.PumpingPowerProd.value, self.DPProdWell.value, self.Pprodwellhead.value = \
10221046
ProdPressureDropAndPumpingPowerUsingIndexes(
1023-
model, self.usebuiltinhydrostaticpressurecorrelation, self.productionwellpumping.value,
1047+
model, self.productionwellpumping.value,
10241048
self.usebuiltinppwellheadcorrelation,
1025-
model.reserv.Trock.value, model.reserv.Tsurf.value, model.reserv.depth.value,
1026-
model.reserv.averagegradient.value, self.ppwellhead.value, self.PI.value,
1049+
model.reserv.Trock.value, model.reserv.depth.value,
1050+
self.ppwellhead.value, self.PI.value,
10271051
self.prodwellflowrate.value, f3, vprod,
10281052
self.prodwelldiam.value, self.nprod.value, model.surfaceplant.pump_efficiency.value,
10291053
self.rhowaterprod)
10301054

10311055
DowngoingPumpingPower, ppp2, dppw, ppwh = ProdPressureDropAndPumpingPowerUsingIndexes(
1032-
model, self.usebuiltinhydrostaticpressurecorrelation, self.productionwellpumping.value,
1056+
model, self.productionwellpumping.value,
10331057
self.usebuiltinppwellheadcorrelation,
1034-
model.reserv.Trock.value, model.reserv.Tsurf.value, model.reserv.depth.value,
1035-
model.reserv.averagegradient.value, self.ppwellhead.value, self.PI.value,
1058+
model.reserv.Trock.value, model.reserv.depth.value,
1059+
self.ppwellhead.value, self.PI.value,
10361060
self.prodwellflowrate.value, f3, vprod,
10371061
self.injwelldiam.value, self.nprod.value, model.surfaceplant.pump_efficiency.value,
10381062
self.rhowaterinj)
@@ -1078,7 +1102,10 @@ def Calculate(self, model: Model) -> None:
10781102
self.ProducedTemperature.value = self.InterpolatedTemperatureArray.copy()
10791103

10801104
tot_length, vert_length, horizontal_lengths = self.calculatedrillinglengths(model)
1081-
model.reserv.depth.value = model.reserv.InputDepth.value * 1000.0 # in this case, reserv.depth is just the vertical drill depth
1105+
1106+
# in this case, reserv.depth is just the vertical drill depth
1107+
# FIXME earlier calculations use depth before this value is set, meaning they're using the wrong value
1108+
model.reserv.depth.value = model.reserv.InputDepth.quantity().to(model.reserv.depth.CurrentUnits).magnitude
10821109

10831110
# getTandP results must be rejiggered to match wellbores expected output. Once done,
10841111
# the surfaceplant and economics models should just work
@@ -1089,12 +1116,16 @@ def Calculate(self, model: Model) -> None:
10891116

10901117
# calculate water values based on initial temperature
10911118

1092-
# FIXME TODO - get rid of fallback calculations https://github.com/NREL/GEOPHIRES-X/issues/110
1093-
rho_water = densitywater(self.Tout[0], enable_fallback_calculation=True)
1119+
rho_water = density_water_kg_per_m3(
1120+
self.Tout[0],
1121+
pressure = model.reserv.lithostatic_pressure(),
1122+
)
1123+
10941124

1095-
# FIXME TODO - get rid of fallback calculations https://github.com/NREL/GEOPHIRES-X/issues/110
1096-
model.reserv.cpwater.value = heatcapacitywater(
1097-
self.Tout[0], enable_fallback_calculation=True) # Need this for surface plant output calculation
1125+
model.reserv.cpwater.value = heat_capacity_water_J_per_kg_per_K(
1126+
self.Tout[0],
1127+
pressure=model.reserv.lithostatic_pressure(),
1128+
) # Need this for surface plant output calculation
10981129

10991130
# set pumping power to zero for all times, assuming that the thermosphere wil always
11001131
# make pumping of working fluid unnecessary

0 commit comments

Comments
 (0)