Skip to content

Commit d087ed2

Browse files
Merge pull request #52 from softwareengineerprogrammer/extraction-integration-fix
Surface Plant time series integrations fix (Electricity Provided + other profiles final year value correction)
2 parents 3b741cb + 6ecc396 commit d087ed2

Some content is hidden

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

46 files changed

+602
-451
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.7.21
2+
current_version = 3.7.23
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.7.21
57+
version: 3.7.23
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
@@ -56,9 +56,9 @@ Free software: `MIT license <LICENSE>`__
5656
:alt: Supported implementations
5757
:target: https://pypi.org/project/geophires-x
5858

59-
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.7.21.svg
59+
.. |commits-since| image:: https://img.shields.io/github/commits-since/softwareengineerprogrammer/GEOPHIRES-X/v3.7.23.svg
6060
:alt: Commits since latest release
61-
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.7.21...main
61+
:target: https://github.com/softwareengineerprogrammer/GEOPHIRES-X/compare/v3.7.23...main
6262

6363
.. |docs| image:: https://readthedocs.org/projects/GEOPHIRES-X/badge/?style=flat
6464
: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 = '2025'
1919
author = 'NREL'
2020
copyright = f'{year}, {author}'
21-
version = release = '3.7.21'
21+
version = release = '3.7.23'
2222

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

setup.py

Lines changed: 1 addition & 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.7.21',
16+
version='3.7.23',
1717
license='MIT',
1818
description='GEOPHIRES is a free and open-source geothermal techno-economic simulator.',
1919
long_description='{}\n{}'.format(

src/geophires_x/SurfacePlant.py

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,43 @@
1-
import sys
2-
import os
31
import numpy as np
42

53
from .GeoPHIRESUtils import quantity
64
from .OptionList import EndUseOptions, PlantType
7-
from .Parameter import floatParameter, intParameter, strParameter, OutputParameter, ReadParameter, \
5+
from .Parameter import floatParameter, intParameter, OutputParameter, ReadParameter, \
86
coerce_int_params_to_enum_values
97
from .Units import *
108
import geophires_x.Model as Model
11-
import pandas as pd
9+
1210

1311
class SurfacePlant:
12+
@staticmethod
13+
def integrate_time_series_slice(
14+
series: np.ndarray,
15+
_i: int,
16+
time_steps_per_year: int,
17+
utilization_factor: float
18+
) -> np.float64:
19+
slice_start_index = _i * time_steps_per_year
20+
slice_end_index = ((_i + 1) * time_steps_per_year) + 1
21+
_slice = list(series[slice_start_index:slice_end_index])
22+
23+
# Note that len(_slice) - 1 may be less than time_steps_per_year for the last slice.
24+
25+
if len(_slice) == 1:
26+
extrapolated_future_datapoint = _slice[0]
27+
if slice_start_index - 1 > 0:
28+
delta = series[slice_start_index] - series[slice_start_index - 1]
29+
extrapolated_future_datapoint = _slice[0] + delta
30+
_slice.append(extrapolated_future_datapoint)
31+
32+
dx_steps = len(_slice) - 1
33+
34+
integral = np.trapz(
35+
_slice,
36+
dx=1. / dx_steps * 365. * 24.
37+
)
38+
39+
return integral * 1000. * utilization_factor
40+
1441
def remaining_reservoir_heat_content(self, InitialReservoirHeatContent: np.ndarray, HeatkWhExtracted: np.ndarray) -> np.ndarray:
1542
"""
1643
Calculate reservoir heat content
@@ -152,15 +179,15 @@ def electricity_heat_production(self, enduse_option: EndUseOptions, availability
152179

153180
return ElectricityProduced, HeatExtracted, HeatProduced, HeatExtractedTowardsElectricity
154181

155-
def annual_electricity_pumping_power(self, plant_lifetime: int, enduse_option: EndUseOptions, HeatExtracted: np.ndarray,
156-
timestepsperyear: np.ndarray, utilization_factor: float, PumpingPower: np.ndarray,
182+
def annual_electricity_pumping_power(self, plant_lifetime: int,enduse_option: EndUseOptions, HeatExtracted: np.ndarray,
183+
time_steps_per_year: int, utilization_factor: float, PumpingPower: np.ndarray,
157184
ElectricityProduced: np.ndarray, NetElectricityProduced: np.ndarray, HeatProduced: np.ndarray) -> tuple:
158185
"""
159186
Calculate annual electricity/heat production
160187
:param plant_lifetime: plant lifetime
161188
:param enduse_option: end-use option
162189
:param HeatExtracted: heat extracted
163-
:param timestepsperyear: timesteps per year
190+
:param time_steps_per_year: time steps per year
164191
:param utilization_factor: utilization factor
165192
:param PumpingPower: pumping power
166193
:param ElectricityProduced: electricity produced
@@ -176,11 +203,13 @@ def annual_electricity_pumping_power(self, plant_lifetime: int, enduse_option: E
176203
NetkWhProduced = np.zeros(plant_lifetime)
177204
HeatkWhProduced = np.zeros(plant_lifetime)
178205

206+
def _integrate_slice(series: np.ndarray, _i: int) -> np.float64:
207+
return SurfacePlant.integrate_time_series_slice(series, _i, time_steps_per_year, utilization_factor)
208+
179209
for i in range(0, plant_lifetime):
180-
HeatkWhExtracted[i] = np.trapz(HeatExtracted[(0 + i * timestepsperyear):((i + 1) * timestepsperyear) + 1],
181-
dx = 1. / timestepsperyear * 365. * 24.) * 1000. * utilization_factor
182-
PumpingkWh[i] = np.trapz(PumpingPower[(0 + i * timestepsperyear):((i + 1) * timestepsperyear) + 1],
183-
dx = 1. / timestepsperyear * 365. * 24.) * 1000. * utilization_factor
210+
HeatkWhExtracted[i] = _integrate_slice(HeatExtracted, i)
211+
PumpingkWh[i] = _integrate_slice(PumpingPower, i)
212+
184213

185214
if enduse_option in [EndUseOptions.ELECTRICITY, EndUseOptions.COGENERATION_TOPPING_EXTRA_HEAT,
186215
EndUseOptions.COGENERATION_TOPPING_EXTRA_ELECTRICITY,
@@ -192,16 +221,14 @@ def annual_electricity_pumping_power(self, plant_lifetime: int, enduse_option: E
192221
TotalkWhProduced = np.zeros(plant_lifetime)
193222
NetkWhProduced = np.zeros(plant_lifetime)
194223
for i in range(0, plant_lifetime):
195-
TotalkWhProduced[i] = np.trapz(ElectricityProduced[(0 + i * timestepsperyear):((i + 1) * timestepsperyear) + 1],
196-
dx=1. / timestepsperyear * 365. * 24.) * 1000. * utilization_factor
197-
NetkWhProduced[i] = np.trapz(NetElectricityProduced[(0 + i * timestepsperyear):((i + 1) * timestepsperyear) + 1],
198-
dx=1. / timestepsperyear * 365. * 24.) * 1000. * utilization_factor
224+
TotalkWhProduced[i] = _integrate_slice(ElectricityProduced, i)
225+
NetkWhProduced[i] = _integrate_slice(NetElectricityProduced, i)
226+
199227
if enduse_option is not EndUseOptions.ELECTRICITY:
200228
# all those end-use options have a direct-use component
201229
HeatkWhProduced = np.zeros(plant_lifetime)
202230
for i in range(0, plant_lifetime):
203-
HeatkWhProduced[i] = np.trapz(HeatProduced[(0 + i * timestepsperyear):((i + 1) * timestepsperyear) + 1],
204-
dx=1. / timestepsperyear * 365. * 24.) * 1000. * utilization_factor
231+
HeatkWhProduced[i] = _integrate_slice(HeatProduced, i)
205232

206233
return HeatkWhExtracted, PumpingkWh, TotalkWhProduced, NetkWhProduced, HeatkWhProduced
207234

src/geophires_x/SurfacePlantAGS.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -738,26 +738,23 @@ def Calculate(self, model: Model) -> None:
738738
self.HeatExtracted.value = self.HeatExtracted.value / 1000.0
739739
# useful direct-use heat provided to application [MWth]
740740
self.HeatProduced.value = self.HeatExtracted.value * self.enduseefficiencyfactor.value
741+
742+
def _integrate_slice(series: np.ndarray, _i: int) -> np.float64:
743+
return SurfacePlant.integrate_time_series_slice(
744+
series, _i, model.economics.timestepsperyear.value, self.utilization_factor.value
745+
)
746+
741747
for i in range(0, self.plant_lifetime.value):
742-
self.HeatkWhExtracted.value[i] = np.trapz(self.HeatExtracted.value[
743-
(i * model.economics.timestepsperyear.value):((
744-
i + 1) * model.economics.timestepsperyear.value) + 1],
745-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
746-
self.PumpingkWh.value[i] = np.trapz(model.wellbores.PumpingPower.value[
747-
(i * model.economics.timestepsperyear.value):((
748-
i + 1) * model.economics.timestepsperyear.value) + 1],
749-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
748+
self.HeatkWhExtracted.value[i] = _integrate_slice(self.HeatExtracted.value, i)
749+
self.PumpingkWh.value[i] = _integrate_slice(model.wellbores.PumpingPower.value, i)
750750

751751
self.RemainingReservoirHeatContent.value = model.reserv.InitialReservoirHeatContent.value - np.cumsum(
752752
self.HeatkWhExtracted.value) * 3600 * 1E3 / 1E15
753753

754754
if self.End_use is not EndUseOptions.ELECTRICITY:
755755
self.HeatkWhProduced.value = np.zeros(self.plant_lifetime.value)
756756
for i in range(0, self.plant_lifetime.value):
757-
self.HeatkWhProduced.value[i] = np.trapz(self.HeatProduced.value[
758-
(0 + i * model.economics.timestepsperyear.value):((
759-
i + 1) * model.economics.timestepsperyear.value) + 1],
760-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
757+
self.HeatkWhProduced.value[i] = _integrate_slice(self.HeatProduced.value, i)
761758
else:
762759
# copy some arrays so we have a GEOPHIRES equivalent
763760
self.TotalkWhProduced.value = self.Annual_electricity_production.copy()

src/geophires_x/SurfacePlantAbsorptionChiller.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -114,29 +114,22 @@ def Calculate(self, model: Model) -> None:
114114
self.HeatkWhExtracted.value = np.zeros(self.plant_lifetime.value)
115115
self.PumpingkWh.value = np.zeros(self.plant_lifetime.value)
116116

117+
def _integrate_slice(series, _i):
118+
return SurfacePlant.integrate_time_series_slice(
119+
series, _i, model.economics.timestepsperyear.value, self.utilization_factor.value
120+
)
121+
117122
for i in range(0, self.plant_lifetime.value):
118-
self.HeatkWhExtracted.value[i] = np.trapz(self.HeatExtracted.value[
119-
(0 + i * model.economics.timestepsperyear.value):((
120-
i + 1) * model.economics.timestepsperyear.value) + 1],
121-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
122-
self.PumpingkWh.value[i] = np.trapz(model.wellbores.PumpingPower.value[
123-
(0 + i * model.economics.timestepsperyear.value):((
124-
i + 1) * model.economics.timestepsperyear.value) + 1],
125-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
123+
self.HeatkWhExtracted.value[i] = _integrate_slice(self.HeatExtracted.value, i)
124+
self.PumpingkWh.value[i] = _integrate_slice(model.wellbores.PumpingPower.value, i)
126125

127126
self.HeatkWhProduced.value = np.zeros(self.plant_lifetime.value)
128127
for i in range(0, self.plant_lifetime.value):
129-
self.HeatkWhProduced.value[i] = np.trapz(self.HeatProduced.value[
130-
(0 + i * model.economics.timestepsperyear.value):((
131-
i + 1) * model.economics.timestepsperyear.value) + 1],
132-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
128+
self.HeatkWhProduced.value[i] = _integrate_slice(self.HeatProduced.value, i)
133129

134130
self.cooling_kWh_Produced.value = np.zeros(self.plant_lifetime.value)
135131
for i in range(0, self.plant_lifetime.value):
136-
self.cooling_kWh_Produced.value[i] = np.trapz(self.cooling_produced.value[
137-
(0 + i * model.economics.timestepsperyear.value):((
138-
i + 1) * model.economics.timestepsperyear.value) + 1],
139-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
132+
self.cooling_kWh_Produced.value[i] = _integrate_slice(self.cooling_produced.value, i)
140133

141134
# calculate reservoir heat content
142135
self.RemainingReservoirHeatContent.value = SurfacePlant.remaining_reservoir_heat_content(

src/geophires_x/SurfacePlantDistrictHeating.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -215,35 +215,37 @@ def Calculate(self, model: Model) -> None:
215215
self.HeatkWhExtracted.value = np.zeros(self.plant_lifetime.value)
216216
self.PumpingkWh.value = np.zeros(self.plant_lifetime.value)
217217

218+
def _integrate_slice(series, _i, util_factor):
219+
return SurfacePlant.integrate_time_series_slice(
220+
series, _i, model.economics.timestepsperyear.value, util_factor
221+
)
222+
223+
218224
for i in range(0, self.plant_lifetime.value):
219-
if self.plant_type.value == PlantType.DISTRICT_HEATING: # for district heating, we have a util_factor_array
220-
self.HeatkWhExtracted.value[i] = np.trapz(self.HeatExtracted.value[
221-
(0 + i * model.economics.timestepsperyear.value):((
222-
i + 1) * model.economics.timestepsperyear.value) + 1],
223-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * \
224-
self.util_factor_array.value[i]
225-
self.PumpingkWh.value[i] = np.trapz(model.wellbores.PumpingPower.value[
226-
(0 + i * model.economics.timestepsperyear.value):((
227-
i + 1) * model.economics.timestepsperyear.value) + 1],
228-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * \
229-
self.util_factor_array.value[i]
225+
if self.plant_type.value == PlantType.DISTRICT_HEATING:
226+
self.HeatkWhExtracted.value[i] = _integrate_slice(
227+
self.HeatExtracted.value,
228+
i,
229+
self.util_factor_array.value[i]
230+
)
231+
232+
self.PumpingkWh.value[i] = _integrate_slice(
233+
model.wellbores.PumpingPower.value,
234+
i,
235+
# for district heating, we have a util_factor_array
236+
self.util_factor_array.value[i]
237+
)
230238
else:
231-
self.HeatkWhExtracted.value[i] = np.trapz(self.HeatExtracted.value[
232-
(0 + i * model.economics.timestepsperyear.value):((
233-
i + 1) * model.economics.timestepsperyear.value) + 1],
234-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
235-
self.PumpingkWh.value[i] = np.trapz(model.wellbores.PumpingPower.value[
236-
(0 + i * model.economics.timestepsperyear.value):((
237-
i + 1) * model.economics.timestepsperyear.value) + 1],
238-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * self.utilization_factor.value
239+
self.HeatkWhExtracted.value[i] = _integrate_slice(self.HeatExtracted.value, i, self.utilization_factor.value)
240+
self.PumpingkWh.value[i] = _integrate_slice(model.wellbores.PumpingPower.value, i, self.utilization_factor.value)
239241

240242
self.HeatkWhProduced.value = np.zeros(self.plant_lifetime.value)
241243
for i in range(0, self.plant_lifetime.value):
242-
self.HeatkWhProduced.value[i] = np.trapz(self.HeatProduced.value[
243-
(0 + i * model.economics.timestepsperyear.value):((
244-
i + 1) * model.economics.timestepsperyear.value) + 1],
245-
dx=1. / model.economics.timestepsperyear.value * 365. * 24.) * 1000. * \
246-
self.util_factor_array.value[i]
244+
self.HeatkWhProduced.value[i] = _integrate_slice(
245+
self.HeatProduced.value,
246+
i,
247+
self.util_factor_array.value[i]
248+
)
247249

248250
# calculate reservoir heat content
249251
self.RemainingReservoirHeatContent.value = SurfacePlant.remaining_reservoir_heat_content(

0 commit comments

Comments
 (0)