Loading pixi.lock +2 −2 Original line number Diff line number Diff line Loading @@ -4766,8 +4766,8 @@ packages: timestamp: 1733195786147 - pypi: C:/Users/knappersfy/work/thermogis/pydoubletcalc name: pydoubletcalc version: 0.0.2 sha256: 89fbb20be0f5da6d0e8e543b9160e4ac3aef8369557ca71ae4a96086f521145e version: 0.0.3 sha256: a391e31a4d31cad94ef6af7520fb9418f2c7f1741fa28384734097c9fda1b1fd requires_dist: - pandas - matplotlib Loading pyproject.toml +1 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ platforms = ["win-64", "linux-64"] [tool.pixi.pypi-dependencies] pythermogis = { path = ".", editable = true } pydoubletcalc = { path = 'C:\Users\knappersfy\work\thermogis\pydoubletcalc', editable = true } [tool.pixi.tasks] test = "PYTHONPATH=src/pythermogis pytest -s tests/" Loading src/pythermogis/workflow/utc/cooling_temp.py +13 −1 Original line number Diff line number Diff line from __future__ import annotations from typing import TYPE_CHECKING from pythermogis.workflow.utc.doublet_utils import calc_lifetime from pythermogis.workflow.utc.flow import calculate_volumetric_flow if TYPE_CHECKING: from pythermogis.workflow.utc.doublet import DoubletInput from pythermogis.workflow.utc.utc_properties import UTCConfiguration def calculate_cooling_temperature( props, input, drawdown_pressure: float, well_distance: float, injection_temp: float props: UTCConfiguration, input: DoubletInput, drawdown_pressure: float, well_distance: float, injection_temp: float, ) -> float: results = calculate_volumetric_flow( props=props, Loading src/pythermogis/workflow/utc/doubletcalc.py +13 −6 Original line number Diff line number Diff line from __future__ import annotations import math from dataclasses import dataclass from typing import TYPE_CHECKING from numba import njit from pydoubletcalc import Aquifer, Doublet, Well, WellPipeSegment from pythermogis.workflow.utc.rock import get_geothermal_gradient from pythermogis.workflow.utc.utc_properties import UTCConfiguration from pythermogis.workflow.utc.water import get_salinity if TYPE_CHECKING: from pythermogis.workflow.utc.doublet import DoubletInput from pythermogis.workflow.utc.utc_properties import UTCConfiguration INCH_SI = 0.0254 Loading @@ -23,7 +30,7 @@ class Doublet1DResults: def doubletcalc( props: UTCConfiguration, input, input: DoubletInput, drawdown_pressure: float, well_distance: float, injection_temp: float, Loading Loading @@ -137,18 +144,18 @@ def doubletcalc( ) def get_total_skin_injection(props, input): def get_total_skin_injection(props: UTCConfiguration, input: DoubletInput) -> float: if (not props.use_stimulation) or ( input.transmissivity_with_ntg > props.stim_kh_max ): stim_add_skin_inj = 0.0 else: stim_add_skin_inj = props.stim_add_skin_inj() stim_add_skin_inj = props.stim_add_skin_inj return props.skin_injector + stim_add_skin_inj def get_total_skin_production(props, input): def get_total_skin_production(props: UTCConfiguration, input: DoubletInput): if (not props.use_stimulation) or ( input.transmissivity_with_ntg > props.stim_kh_max ): Loading @@ -159,7 +166,7 @@ def get_total_skin_production(props, input): return props.skin_producer + stim_add_skin_prod def get_pump_production_depth(props, depth: float) -> float: def get_pump_production_depth(props: UTCConfiguration, depth: float) -> float: return min(props.pump_depth, depth / 2) Loading src/pythermogis/workflow/utc/economics.py +80 −49 Original line number Diff line number Diff line from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING import numpy as np from numpy.typing import NDArray from pythermogis.workflow.utc.doublet_utils import get_along_hole_length if TYPE_CHECKING: from pythermogis.workflow.utc.doublet import DoubletInput from pythermogis.workflow.utc.utc_properties import UTCConfiguration SECONDS_IN_YEAR = 365 * 24 * 3600 MJ_TO_GJ = 1e-3 KWH_TO_MJ = 0.36 * 1e9 Loading Loading @@ -33,8 +41,8 @@ class EconomicsResults: def calculate_economics( props, input, props: UTCConfiguration, input: DoubletInput, well_distance: float, heat_power_produced: list[float], stimulation_capex: float, Loading Loading @@ -81,7 +89,7 @@ def calculate_economics( def calculate_capex( props, props: UTCConfiguration, heat_power_produced: list[float], stimulation_capex: float, hp_cop: float, Loading Loading @@ -147,7 +155,7 @@ def calculate_capex( ) def shift_time_series(series: list[float], shift: int): def shift_time_series(series: list[float], shift: int) -> None: n = len(series) if shift <= 0 or shift >= n: for i in range(n): Loading @@ -158,7 +166,9 @@ def shift_time_series(series: list[float], shift: int): series[i] = 0 def calculate_capex_for_wells(props, ah_length_array, stimulation_capex): def calculate_capex_for_wells( props: UTCConfiguration, ah_length_array: list[float], stimulation_capex: float ) -> float: inj_depth = ah_length_array[0] prod_depth = ah_length_array[0] Loading @@ -170,13 +180,15 @@ def calculate_capex_for_wells(props, ah_length_array, stimulation_capex): return capex * props.well_cost_scaling def calculate_well_cost(props, depth): def calculate_well_cost(props: UTCConfiguration, depth: float) -> float: return ( props.well_cost_z2 * depth**2 + props.well_cost_z * depth ) * 1e-6 + props.well_cost_const def allocate_capex_for_wells(props, total_capex_ts, capex_well): def allocate_capex_for_wells( props: UTCConfiguration, total_capex_ts: NDArray[np.float64], capex_well: float ): drilling_years = props.drilling_time yearly_cost = capex_well / max(drilling_years, 1) Loading @@ -184,23 +196,32 @@ def allocate_capex_for_wells(props, total_capex_ts, capex_well): total_capex_ts[y] += yearly_cost def get_max_hp_added_power(initial, hp_power_years): def get_max_hp_added_power(initial: float, hp_power_years: list[float] | None) -> float: if hp_power_years is not None: return max(hp_power_years) return initial def calculate_hp_added_power_after_heat_pump(props, hp_added_power, hp_cop): def calculate_hp_added_power_after_heat_pump( props: UTCConfiguration, hp_added_power: float, hp_cop: float ) -> float: alt_price = props.hp_alternative_heating_price return hp_added_power if alt_price < 0 else hp_added_power * (1 + 1 / (hp_cop - 1)) def calculate_installation_mw(heat_power_produced, hp_added_power): def calculate_installation_mw( heat_power_produced: list[float], hp_added_power: float ) -> float: last_val = heat_power_produced[-1] return max(last_val - hp_added_power, 0) def calculate_total_capex_1year(props, total_capex_ts, hp_after_hp, installation_mw): def calculate_total_capex_1year( props: UTCConfiguration, total_capex_ts: NDArray[np.float64], hp_after_hp: float, installation_mw: float, ) -> float: last_year = max(props.drilling_time - 1, 0) total_capex_ts[last_year] += ( Loading @@ -214,21 +235,21 @@ def calculate_total_capex_1year(props, total_capex_ts, hp_after_hp, installation def process_annual_data( props, heat_power_produced, total_capex_ts, heat_power_per_year, variable_opex, fixed_opex, total_opex_ts, total_capex_1year, installation_mw, hp_after_hp, hp_cop, hp_cop_years, season_factor_years, pump_power_required, ): props: UTCConfiguration, heat_power_produced: list[float], total_capex_ts: NDArray[np.float64], heat_power_per_year: NDArray[np.float64], variable_opex: NDArray[np.float64], fixed_opex: NDArray[np.float64], total_opex_ts: NDArray[np.float64], total_capex_1year: float, installation_mw: float, hp_after_hp: float, hp_cop: float, hp_cop_years: float, season_factor_years: float, pump_power_required: float, ) -> float: sum_capex = 0.0 for year in range(props.economic_lifetime): Loading Loading @@ -281,16 +302,16 @@ def process_annual_data( def calculate_variable_opex( props, season_factor, elec_price, pump_power, heat_power_produced, heat_exchanger_parasitic, heat_power_gj_per_year, hp_cop, hp_after_hp, ): props: UTCConfiguration, season_factor: float, elec_price: float, pump_power: float, heat_power_produced: float, heat_exchanger_parasitic: float, heat_power_gj_per_year: float, hp_cop: float, hp_after_hp: float, ) -> float: pump_cost = ( -(pump_power * 1e3) * SECONDS_IN_YEAR Loading Loading @@ -333,7 +354,12 @@ def calculate_variable_opex( return pump_cost + parasitic_cost + heat_pump_cost + opex_per_energy def calculate_fixed_opex(props, hp_after_hp, installation_mw, total_capex): def calculate_fixed_opex( props: UTCConfiguration, hp_after_hp: float, installation_mw: float, total_capex: float, ) -> float: return ( -props.opex_base / 1e6 - (props.hp_opex * hp_after_hp + props.opex_per_power * installation_mw) * 1e-3 Loading @@ -341,15 +367,15 @@ def calculate_fixed_opex(props, hp_after_hp, installation_mw, total_capex): ) def get_heat_exchanger_season_factor(props): def get_heat_exchanger_season_factor(props: UTCConfiguration) -> float: return props.load_hours / HOURS_IN_YEAR def calculate_utc( props, heat_power_per_year, total_opex_ts, total_capex, props: UTCConfiguration, heat_power_per_year: float, total_opex_ts: float, total_capex: float, ) -> UTCCalculatorResults: net_cash_worth_eia = calculate_net_cash_worth_eia(props, total_capex) present_value = total_capex * props.debt_equity - net_cash_worth_eia Loading Loading @@ -393,13 +419,13 @@ def calculate_utc( ) def calculate_net_cash_worth_eia(props, total_capex): def calculate_net_cash_worth_eia(props: UTCConfiguration, total_capex: float) -> float: tax_rate = props.tax_rate project_interest_rate = calculate_project_interest_rate(props) return (tax_rate * (props.ecn_eia * total_capex)) / (1 + project_interest_rate) def calculate_payment_value(props, present_value): def calculate_payment_value(props: UTCConfiguration, present_value: float) -> float: if props.interest_loan == 0: return 0.0 Loading @@ -407,11 +433,13 @@ def calculate_payment_value(props, present_value): return (props.interest_loan / (factor - 1)) * (-(present_value * factor)) def calculate_depreciation_cost(props, total_capex): def calculate_depreciation_cost(props: UTCConfiguration, total_capex: float) -> float: return -(total_capex / props.economic_lifetime) def calculate_future_value(present_value, payment, interest, year): def calculate_future_value( present_value: float, payment: float, interest: float, year: float ) -> float: future_value = present_value * ((1 + interest) ** (year - 1)) if payment != 0: future_value += payment * (((1 + interest) ** (year - 1)) - 1) / interest Loading @@ -419,8 +447,11 @@ def calculate_future_value(present_value, payment, interest, year): def calculate_unit_technical_cost( props, total_capex, discounted_income, discounted_heat_produced ): props: UTCConfiguration, total_capex: float, discounted_income: float, discounted_heat_produced: float, ) -> float: if discounted_heat_produced <= 0: return 0.0 return ( Loading @@ -429,7 +460,7 @@ def calculate_unit_technical_cost( ) * 1e6 def calculate_project_interest_rate(props): def calculate_project_interest_rate(props: UTCConfiguration) -> float: return (1 - props.tax_rate) * props.interest_loan * props.debt_equity + ( 1 - props.debt_equity ) * props.equity_return Loading
pixi.lock +2 −2 Original line number Diff line number Diff line Loading @@ -4766,8 +4766,8 @@ packages: timestamp: 1733195786147 - pypi: C:/Users/knappersfy/work/thermogis/pydoubletcalc name: pydoubletcalc version: 0.0.2 sha256: 89fbb20be0f5da6d0e8e543b9160e4ac3aef8369557ca71ae4a96086f521145e version: 0.0.3 sha256: a391e31a4d31cad94ef6af7520fb9418f2c7f1741fa28384734097c9fda1b1fd requires_dist: - pandas - matplotlib Loading
pyproject.toml +1 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ platforms = ["win-64", "linux-64"] [tool.pixi.pypi-dependencies] pythermogis = { path = ".", editable = true } pydoubletcalc = { path = 'C:\Users\knappersfy\work\thermogis\pydoubletcalc', editable = true } [tool.pixi.tasks] test = "PYTHONPATH=src/pythermogis pytest -s tests/" Loading
src/pythermogis/workflow/utc/cooling_temp.py +13 −1 Original line number Diff line number Diff line from __future__ import annotations from typing import TYPE_CHECKING from pythermogis.workflow.utc.doublet_utils import calc_lifetime from pythermogis.workflow.utc.flow import calculate_volumetric_flow if TYPE_CHECKING: from pythermogis.workflow.utc.doublet import DoubletInput from pythermogis.workflow.utc.utc_properties import UTCConfiguration def calculate_cooling_temperature( props, input, drawdown_pressure: float, well_distance: float, injection_temp: float props: UTCConfiguration, input: DoubletInput, drawdown_pressure: float, well_distance: float, injection_temp: float, ) -> float: results = calculate_volumetric_flow( props=props, Loading
src/pythermogis/workflow/utc/doubletcalc.py +13 −6 Original line number Diff line number Diff line from __future__ import annotations import math from dataclasses import dataclass from typing import TYPE_CHECKING from numba import njit from pydoubletcalc import Aquifer, Doublet, Well, WellPipeSegment from pythermogis.workflow.utc.rock import get_geothermal_gradient from pythermogis.workflow.utc.utc_properties import UTCConfiguration from pythermogis.workflow.utc.water import get_salinity if TYPE_CHECKING: from pythermogis.workflow.utc.doublet import DoubletInput from pythermogis.workflow.utc.utc_properties import UTCConfiguration INCH_SI = 0.0254 Loading @@ -23,7 +30,7 @@ class Doublet1DResults: def doubletcalc( props: UTCConfiguration, input, input: DoubletInput, drawdown_pressure: float, well_distance: float, injection_temp: float, Loading Loading @@ -137,18 +144,18 @@ def doubletcalc( ) def get_total_skin_injection(props, input): def get_total_skin_injection(props: UTCConfiguration, input: DoubletInput) -> float: if (not props.use_stimulation) or ( input.transmissivity_with_ntg > props.stim_kh_max ): stim_add_skin_inj = 0.0 else: stim_add_skin_inj = props.stim_add_skin_inj() stim_add_skin_inj = props.stim_add_skin_inj return props.skin_injector + stim_add_skin_inj def get_total_skin_production(props, input): def get_total_skin_production(props: UTCConfiguration, input: DoubletInput): if (not props.use_stimulation) or ( input.transmissivity_with_ntg > props.stim_kh_max ): Loading @@ -159,7 +166,7 @@ def get_total_skin_production(props, input): return props.skin_producer + stim_add_skin_prod def get_pump_production_depth(props, depth: float) -> float: def get_pump_production_depth(props: UTCConfiguration, depth: float) -> float: return min(props.pump_depth, depth / 2) Loading
src/pythermogis/workflow/utc/economics.py +80 −49 Original line number Diff line number Diff line from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING import numpy as np from numpy.typing import NDArray from pythermogis.workflow.utc.doublet_utils import get_along_hole_length if TYPE_CHECKING: from pythermogis.workflow.utc.doublet import DoubletInput from pythermogis.workflow.utc.utc_properties import UTCConfiguration SECONDS_IN_YEAR = 365 * 24 * 3600 MJ_TO_GJ = 1e-3 KWH_TO_MJ = 0.36 * 1e9 Loading Loading @@ -33,8 +41,8 @@ class EconomicsResults: def calculate_economics( props, input, props: UTCConfiguration, input: DoubletInput, well_distance: float, heat_power_produced: list[float], stimulation_capex: float, Loading Loading @@ -81,7 +89,7 @@ def calculate_economics( def calculate_capex( props, props: UTCConfiguration, heat_power_produced: list[float], stimulation_capex: float, hp_cop: float, Loading Loading @@ -147,7 +155,7 @@ def calculate_capex( ) def shift_time_series(series: list[float], shift: int): def shift_time_series(series: list[float], shift: int) -> None: n = len(series) if shift <= 0 or shift >= n: for i in range(n): Loading @@ -158,7 +166,9 @@ def shift_time_series(series: list[float], shift: int): series[i] = 0 def calculate_capex_for_wells(props, ah_length_array, stimulation_capex): def calculate_capex_for_wells( props: UTCConfiguration, ah_length_array: list[float], stimulation_capex: float ) -> float: inj_depth = ah_length_array[0] prod_depth = ah_length_array[0] Loading @@ -170,13 +180,15 @@ def calculate_capex_for_wells(props, ah_length_array, stimulation_capex): return capex * props.well_cost_scaling def calculate_well_cost(props, depth): def calculate_well_cost(props: UTCConfiguration, depth: float) -> float: return ( props.well_cost_z2 * depth**2 + props.well_cost_z * depth ) * 1e-6 + props.well_cost_const def allocate_capex_for_wells(props, total_capex_ts, capex_well): def allocate_capex_for_wells( props: UTCConfiguration, total_capex_ts: NDArray[np.float64], capex_well: float ): drilling_years = props.drilling_time yearly_cost = capex_well / max(drilling_years, 1) Loading @@ -184,23 +196,32 @@ def allocate_capex_for_wells(props, total_capex_ts, capex_well): total_capex_ts[y] += yearly_cost def get_max_hp_added_power(initial, hp_power_years): def get_max_hp_added_power(initial: float, hp_power_years: list[float] | None) -> float: if hp_power_years is not None: return max(hp_power_years) return initial def calculate_hp_added_power_after_heat_pump(props, hp_added_power, hp_cop): def calculate_hp_added_power_after_heat_pump( props: UTCConfiguration, hp_added_power: float, hp_cop: float ) -> float: alt_price = props.hp_alternative_heating_price return hp_added_power if alt_price < 0 else hp_added_power * (1 + 1 / (hp_cop - 1)) def calculate_installation_mw(heat_power_produced, hp_added_power): def calculate_installation_mw( heat_power_produced: list[float], hp_added_power: float ) -> float: last_val = heat_power_produced[-1] return max(last_val - hp_added_power, 0) def calculate_total_capex_1year(props, total_capex_ts, hp_after_hp, installation_mw): def calculate_total_capex_1year( props: UTCConfiguration, total_capex_ts: NDArray[np.float64], hp_after_hp: float, installation_mw: float, ) -> float: last_year = max(props.drilling_time - 1, 0) total_capex_ts[last_year] += ( Loading @@ -214,21 +235,21 @@ def calculate_total_capex_1year(props, total_capex_ts, hp_after_hp, installation def process_annual_data( props, heat_power_produced, total_capex_ts, heat_power_per_year, variable_opex, fixed_opex, total_opex_ts, total_capex_1year, installation_mw, hp_after_hp, hp_cop, hp_cop_years, season_factor_years, pump_power_required, ): props: UTCConfiguration, heat_power_produced: list[float], total_capex_ts: NDArray[np.float64], heat_power_per_year: NDArray[np.float64], variable_opex: NDArray[np.float64], fixed_opex: NDArray[np.float64], total_opex_ts: NDArray[np.float64], total_capex_1year: float, installation_mw: float, hp_after_hp: float, hp_cop: float, hp_cop_years: float, season_factor_years: float, pump_power_required: float, ) -> float: sum_capex = 0.0 for year in range(props.economic_lifetime): Loading Loading @@ -281,16 +302,16 @@ def process_annual_data( def calculate_variable_opex( props, season_factor, elec_price, pump_power, heat_power_produced, heat_exchanger_parasitic, heat_power_gj_per_year, hp_cop, hp_after_hp, ): props: UTCConfiguration, season_factor: float, elec_price: float, pump_power: float, heat_power_produced: float, heat_exchanger_parasitic: float, heat_power_gj_per_year: float, hp_cop: float, hp_after_hp: float, ) -> float: pump_cost = ( -(pump_power * 1e3) * SECONDS_IN_YEAR Loading Loading @@ -333,7 +354,12 @@ def calculate_variable_opex( return pump_cost + parasitic_cost + heat_pump_cost + opex_per_energy def calculate_fixed_opex(props, hp_after_hp, installation_mw, total_capex): def calculate_fixed_opex( props: UTCConfiguration, hp_after_hp: float, installation_mw: float, total_capex: float, ) -> float: return ( -props.opex_base / 1e6 - (props.hp_opex * hp_after_hp + props.opex_per_power * installation_mw) * 1e-3 Loading @@ -341,15 +367,15 @@ def calculate_fixed_opex(props, hp_after_hp, installation_mw, total_capex): ) def get_heat_exchanger_season_factor(props): def get_heat_exchanger_season_factor(props: UTCConfiguration) -> float: return props.load_hours / HOURS_IN_YEAR def calculate_utc( props, heat_power_per_year, total_opex_ts, total_capex, props: UTCConfiguration, heat_power_per_year: float, total_opex_ts: float, total_capex: float, ) -> UTCCalculatorResults: net_cash_worth_eia = calculate_net_cash_worth_eia(props, total_capex) present_value = total_capex * props.debt_equity - net_cash_worth_eia Loading Loading @@ -393,13 +419,13 @@ def calculate_utc( ) def calculate_net_cash_worth_eia(props, total_capex): def calculate_net_cash_worth_eia(props: UTCConfiguration, total_capex: float) -> float: tax_rate = props.tax_rate project_interest_rate = calculate_project_interest_rate(props) return (tax_rate * (props.ecn_eia * total_capex)) / (1 + project_interest_rate) def calculate_payment_value(props, present_value): def calculate_payment_value(props: UTCConfiguration, present_value: float) -> float: if props.interest_loan == 0: return 0.0 Loading @@ -407,11 +433,13 @@ def calculate_payment_value(props, present_value): return (props.interest_loan / (factor - 1)) * (-(present_value * factor)) def calculate_depreciation_cost(props, total_capex): def calculate_depreciation_cost(props: UTCConfiguration, total_capex: float) -> float: return -(total_capex / props.economic_lifetime) def calculate_future_value(present_value, payment, interest, year): def calculate_future_value( present_value: float, payment: float, interest: float, year: float ) -> float: future_value = present_value * ((1 + interest) ** (year - 1)) if payment != 0: future_value += payment * (((1 + interest) ** (year - 1)) - 1) / interest Loading @@ -419,8 +447,11 @@ def calculate_future_value(present_value, payment, interest, year): def calculate_unit_technical_cost( props, total_capex, discounted_income, discounted_heat_produced ): props: UTCConfiguration, total_capex: float, discounted_income: float, discounted_heat_produced: float, ) -> float: if discounted_heat_produced <= 0: return 0.0 return ( Loading @@ -429,7 +460,7 @@ def calculate_unit_technical_cost( ) * 1e6 def calculate_project_interest_rate(props): def calculate_project_interest_rate(props: UTCConfiguration) -> float: return (1 - props.tax_rate) * props.interest_loan * props.debt_equity + ( 1 - props.debt_equity ) * props.equity_return