Loading pixi.lock +90 −1 Original line number Diff line number Diff line Loading @@ -140,6 +140,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.45.1-py313hdd307be_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.4-py313h8756d67_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda Loading @@ -162,6 +163,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.2.21-py39h77e2912_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h3f2d84a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.62.1-py313hd8e3f9f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py313h17eae1a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openpyxl-3.1.5-py313h9c9eb82_1.conda Loading Loading @@ -371,6 +373,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.8-h442d1da_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvmlite-0.45.1-py313h5c49287_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.4-py313h05901a4_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda Loading @@ -392,6 +395,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.43.1-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nh3-0.2.21-py39he870945_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numba-0.62.1-py313h924e429_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.2.5-py313hefb8edb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.3-h4d64b90_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openpyxl-3.1.5-py313he57e174_1.conda Loading Loading @@ -3622,6 +3626,40 @@ packages: purls: [] size: 55476 timestamp: 1727963768015 - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.45.1-py313hdd307be_0.conda sha256: 5b8e2063e93bea160fd50274d05ce4436f01c383a392f293b769dfb973c4df21 md5: 5afb15643ef1fcea20798bb6086bb3f9 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - python >=3.13,<3.14.0a0 - python_abi 3.13.* *_cp313 - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/llvmlite?source=hash-mapping size: 34153671 timestamp: 1759394632193 - conda: https://conda.anaconda.org/conda-forge/win-64/llvmlite-0.45.1-py313h5c49287_0.conda sha256: 0b63923082e724b2c2939621aef77d9ec65aa468a7b29917a850e47e2083adda md5: d946ee3e7228e48270589791871a891e depends: - libzlib >=1.3.1,<2.0a0 - python >=3.13,<3.14.0a0 - python_abi 3.13.* *_cp313 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/llvmlite?source=hash-mapping size: 22905696 timestamp: 1759394712687 - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 sha256: 9afe0b5cfa418e8bdb30d8917c5a6cec10372b037924916f1f85b9f4899a67a6 md5: 91e27ef3d05cc772ce627e51cff111c4 Loading Loading @@ -4081,6 +4119,57 @@ packages: - pkg:pypi/nodeenv?source=hash-mapping size: 34574 timestamp: 1734112236147 - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.62.1-py313hd8e3f9f_0.conda sha256: 0b0ccf81ecd0cfb934818c3fb88404821904fe84e67815ab9958d37b0dca61e4 md5: 82ffdc573a667626351f4110605da846 depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 - libgcc >=14 - libstdcxx >=14 - llvmlite >=0.45.0,<0.46.0a0 - numpy >=1.23,<3 - numpy >=1.24,<2.4 - python >=3.13,<3.14.0a0 - python_abi 3.13.* *_cp313 constrains: - cudatoolkit >=11.2 - cuda-version >=11.2 - tbb >=2021.6.0 - cuda-python >=11.6 - scipy >=1.0 - libopenblas !=0.3.6 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/numba?source=hash-mapping size: 5743830 timestamp: 1759165232580 - conda: https://conda.anaconda.org/conda-forge/win-64/numba-0.62.1-py313h924e429_0.conda sha256: 79835953985d64565f76f912517ab5700148e86659b8e79ecd2d0e6d7377ac46 md5: ae201f33cbcbb2aba93daf4b3263b4a5 depends: - llvmlite >=0.45.0,<0.46.0a0 - numpy >=1.23,<3 - numpy >=1.24,<2.4 - python >=3.13,<3.14.0a0 - python_abi 3.13.* *_cp313 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - libopenblas !=0.3.6 - cudatoolkit >=11.2 - tbb >=2021.6.0 - scipy >=1.0 - cuda-python >=11.6 - cuda-version >=11.2 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/numba?source=hash-mapping size: 5706597 timestamp: 1759165298367 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py313h17eae1a_0.conda sha256: c0a200d0e53a1acbfa1d1e2277e3337ea2aa8cb584448790317a98c62dcaebce md5: 6ceeff9ed72e54e4a2f9a1c88f47bdde Loading Loading @@ -4830,7 +4919,7 @@ packages: - pypi: ./ name: pythermogis version: 1.2.0 sha256: 50c9d857c9314b246b28d7e610647475414c718d839408818aa4f441813b51a7 sha256: bbba086a07a9972c4416d90c0cddf7f691529a60cac13dd8dd9fb2a236d68059 requires_dist: - jpype1>=1.5.2,<2 - xarray==2024.9.0.* Loading pyproject.toml +1 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ dask = ">=2025.5.1,<2026" narwhals = ">=1.43.1,<2" pre-commit = ">=4.3.0,<5" python-dotenv = ">=1.2.1,<2" numba = ">=0.62.1,<0.63" [tool.ruff] line-length = 88 Loading src/pythermogis/workflow/utc/doublet.py 0 → 100644 +84 −0 Original line number Diff line number Diff line from dataclasses import dataclass from pythermogis.workflow.utc.utc_properties import UTCConfiguration from pythermogis.workflow.utc.doublet_utils import calculate_injection_temp_with_heat_pump @dataclass class DoubletInput: unknown_input_value: float thickness: float transmissivity: float transmissivity_with_ntg: float ntg: float depth: float porosity: float temperature: float @dataclass class DoubletOutput: power: float hppower: float capex: float var_opex: float fixed_opex: float opex: float utc: float npv: float hprod: float cop: float cophp: float pres: float flow: float welld: float breakthrough: float cooling: float production_temp: float injection_temp: float def calculate_doublet_performance(props: UTCConfiguration, input: DoubletInput) -> DoubletOutput: well_distance = ( (props.optim_dist_well_dist_min + props.optim_dist_well_dist_max) / 2 if props.optim_well_dist else props.default_well_distance ) injection_temperature = ( max(input.temperature - props.max_cooling_temp_range, props.hp_minimum_injection_temperature) if props.use_heat_pump else max(input.temperature - props.max_cooling_temp_range, props.dh_return_temp) ) if props.use_heat_pump and props.calculate_cop and not props.hp_application_mode: injection_temperature = calculate_injection_temp_with_heat_pump( input.temperature, props.hp_direct_heat_input_temp, props.dh_return_temp, input.temperature, props.max_cooling_temp_range, props.hp_minimum_injection_temperature ) return DoubletOutput( power=0.0, hppower=0.0, capex=0.0, var_opex=0.0, fixed_opex=0.0, opex=0.0, utc=0.0, npv=0.0, hprod=0.0, cop=0.0, cophp=0.0, pres=0.0, flow=0.0, welld=0.0, breakthrough=0.0, cooling=0.0, production_temp=0.0, injection_temp=0.0 ) src/pythermogis/workflow/utc/doublet_utils.py 0 → 100644 +35 −0 Original line number Diff line number Diff line from numba import njit @njit def calculate_injection_temp_with_heat_pump( t_prod: float, t_goal: float, dh_return_temp: float, reservoir_temp: float, max_cooling_temp_range: float, hp_minimum_injection_temperature: float, ): t_kelvin = 273.15 eta = 0.5 delta_t = 3 # deg C tc = t_goal + t_kelvin tr = t_prod + t_kelvin t_waste = min(dh_return_temp, t_prod) + t_kelvin num = ( -eta * (tc + delta_t) * (t_waste + tr - tc) - tc * tc + tr * tc - 2 * delta_t * (tc - tr) ) denom = tr - tc - eta * (tc + delta_t) needed_injection_temp = num / denom - t_kelvin return max( needed_injection_temp, max(reservoir_temp - max_cooling_temp_range, hp_minimum_injection_temperature) ) No newline at end of file tests/utc/test_doublet.py 0 → 100644 +26 −0 Original line number Diff line number Diff line from pythermogis.workflow.utc.doublet import calculate_doublet_performance, DoubletInput, DoubletOutput from pythermogis.workflow.utc.utc_properties import UTCConfiguration def test_calculate_doublet_performance_runs_with_default_props(): # Arrange: instantiate default UTCConfiguration props = UTCConfiguration() # Create a minimal valid DoubletInput input_data = DoubletInput( unknown_input_value=0.0, thickness=100.0, transmissivity=5.0, transmissivity_with_ntg=3.0, ntg=0.1, depth=1500, porosity=0.2, temperature=80.0, ) # Act result = calculate_doublet_performance(props, input_data) # Assert assert isinstance(result, DoubletOutput) No newline at end of file Loading
pixi.lock +90 −1 Original line number Diff line number Diff line Loading @@ -140,6 +140,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.45.1-py313hdd307be_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-4.4.4-py313h8756d67_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda Loading @@ -162,6 +163,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.2.21-py39h77e2912_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h3f2d84a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.62.1-py313hd8e3f9f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py313h17eae1a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openpyxl-3.1.5-py313h9c9eb82_1.conda Loading Loading @@ -371,6 +373,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.8-h442d1da_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvmlite-0.45.1-py313h5c49287_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-4.4.4-py313h05901a4_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda Loading @@ -392,6 +395,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-1.43.1-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nh3-0.2.21-py39he870945_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numba-0.62.1-py313h924e429_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.2.5-py313hefb8edb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.3-h4d64b90_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openpyxl-3.1.5-py313he57e174_1.conda Loading Loading @@ -3622,6 +3626,40 @@ packages: purls: [] size: 55476 timestamp: 1727963768015 - conda: https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.45.1-py313hdd307be_0.conda sha256: 5b8e2063e93bea160fd50274d05ce4436f01c383a392f293b769dfb973c4df21 md5: 5afb15643ef1fcea20798bb6086bb3f9 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - python >=3.13,<3.14.0a0 - python_abi 3.13.* *_cp313 - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/llvmlite?source=hash-mapping size: 34153671 timestamp: 1759394632193 - conda: https://conda.anaconda.org/conda-forge/win-64/llvmlite-0.45.1-py313h5c49287_0.conda sha256: 0b63923082e724b2c2939621aef77d9ec65aa468a7b29917a850e47e2083adda md5: d946ee3e7228e48270589791871a891e depends: - libzlib >=1.3.1,<2.0a0 - python >=3.13,<3.14.0a0 - python_abi 3.13.* *_cp313 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/llvmlite?source=hash-mapping size: 22905696 timestamp: 1759394712687 - conda: https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2 sha256: 9afe0b5cfa418e8bdb30d8917c5a6cec10372b037924916f1f85b9f4899a67a6 md5: 91e27ef3d05cc772ce627e51cff111c4 Loading Loading @@ -4081,6 +4119,57 @@ packages: - pkg:pypi/nodeenv?source=hash-mapping size: 34574 timestamp: 1734112236147 - conda: https://conda.anaconda.org/conda-forge/linux-64/numba-0.62.1-py313hd8e3f9f_0.conda sha256: 0b0ccf81ecd0cfb934818c3fb88404821904fe84e67815ab9958d37b0dca61e4 md5: 82ffdc573a667626351f4110605da846 depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 - libgcc >=14 - libstdcxx >=14 - llvmlite >=0.45.0,<0.46.0a0 - numpy >=1.23,<3 - numpy >=1.24,<2.4 - python >=3.13,<3.14.0a0 - python_abi 3.13.* *_cp313 constrains: - cudatoolkit >=11.2 - cuda-version >=11.2 - tbb >=2021.6.0 - cuda-python >=11.6 - scipy >=1.0 - libopenblas !=0.3.6 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/numba?source=hash-mapping size: 5743830 timestamp: 1759165232580 - conda: https://conda.anaconda.org/conda-forge/win-64/numba-0.62.1-py313h924e429_0.conda sha256: 79835953985d64565f76f912517ab5700148e86659b8e79ecd2d0e6d7377ac46 md5: ae201f33cbcbb2aba93daf4b3263b4a5 depends: - llvmlite >=0.45.0,<0.46.0a0 - numpy >=1.23,<3 - numpy >=1.24,<2.4 - python >=3.13,<3.14.0a0 - python_abi 3.13.* *_cp313 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - libopenblas !=0.3.6 - cudatoolkit >=11.2 - tbb >=2021.6.0 - scipy >=1.0 - cuda-python >=11.6 - cuda-version >=11.2 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/numba?source=hash-mapping size: 5706597 timestamp: 1759165298367 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py313h17eae1a_0.conda sha256: c0a200d0e53a1acbfa1d1e2277e3337ea2aa8cb584448790317a98c62dcaebce md5: 6ceeff9ed72e54e4a2f9a1c88f47bdde Loading Loading @@ -4830,7 +4919,7 @@ packages: - pypi: ./ name: pythermogis version: 1.2.0 sha256: 50c9d857c9314b246b28d7e610647475414c718d839408818aa4f441813b51a7 sha256: bbba086a07a9972c4416d90c0cddf7f691529a60cac13dd8dd9fb2a236d68059 requires_dist: - jpype1>=1.5.2,<2 - xarray==2024.9.0.* Loading
pyproject.toml +1 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ dask = ">=2025.5.1,<2026" narwhals = ">=1.43.1,<2" pre-commit = ">=4.3.0,<5" python-dotenv = ">=1.2.1,<2" numba = ">=0.62.1,<0.63" [tool.ruff] line-length = 88 Loading
src/pythermogis/workflow/utc/doublet.py 0 → 100644 +84 −0 Original line number Diff line number Diff line from dataclasses import dataclass from pythermogis.workflow.utc.utc_properties import UTCConfiguration from pythermogis.workflow.utc.doublet_utils import calculate_injection_temp_with_heat_pump @dataclass class DoubletInput: unknown_input_value: float thickness: float transmissivity: float transmissivity_with_ntg: float ntg: float depth: float porosity: float temperature: float @dataclass class DoubletOutput: power: float hppower: float capex: float var_opex: float fixed_opex: float opex: float utc: float npv: float hprod: float cop: float cophp: float pres: float flow: float welld: float breakthrough: float cooling: float production_temp: float injection_temp: float def calculate_doublet_performance(props: UTCConfiguration, input: DoubletInput) -> DoubletOutput: well_distance = ( (props.optim_dist_well_dist_min + props.optim_dist_well_dist_max) / 2 if props.optim_well_dist else props.default_well_distance ) injection_temperature = ( max(input.temperature - props.max_cooling_temp_range, props.hp_minimum_injection_temperature) if props.use_heat_pump else max(input.temperature - props.max_cooling_temp_range, props.dh_return_temp) ) if props.use_heat_pump and props.calculate_cop and not props.hp_application_mode: injection_temperature = calculate_injection_temp_with_heat_pump( input.temperature, props.hp_direct_heat_input_temp, props.dh_return_temp, input.temperature, props.max_cooling_temp_range, props.hp_minimum_injection_temperature ) return DoubletOutput( power=0.0, hppower=0.0, capex=0.0, var_opex=0.0, fixed_opex=0.0, opex=0.0, utc=0.0, npv=0.0, hprod=0.0, cop=0.0, cophp=0.0, pres=0.0, flow=0.0, welld=0.0, breakthrough=0.0, cooling=0.0, production_temp=0.0, injection_temp=0.0 )
src/pythermogis/workflow/utc/doublet_utils.py 0 → 100644 +35 −0 Original line number Diff line number Diff line from numba import njit @njit def calculate_injection_temp_with_heat_pump( t_prod: float, t_goal: float, dh_return_temp: float, reservoir_temp: float, max_cooling_temp_range: float, hp_minimum_injection_temperature: float, ): t_kelvin = 273.15 eta = 0.5 delta_t = 3 # deg C tc = t_goal + t_kelvin tr = t_prod + t_kelvin t_waste = min(dh_return_temp, t_prod) + t_kelvin num = ( -eta * (tc + delta_t) * (t_waste + tr - tc) - tc * tc + tr * tc - 2 * delta_t * (tc - tr) ) denom = tr - tc - eta * (tc + delta_t) needed_injection_temp = num / denom - t_kelvin return max( needed_injection_temp, max(reservoir_temp - max_cooling_temp_range, hp_minimum_injection_temperature) ) No newline at end of file
tests/utc/test_doublet.py 0 → 100644 +26 −0 Original line number Diff line number Diff line from pythermogis.workflow.utc.doublet import calculate_doublet_performance, DoubletInput, DoubletOutput from pythermogis.workflow.utc.utc_properties import UTCConfiguration def test_calculate_doublet_performance_runs_with_default_props(): # Arrange: instantiate default UTCConfiguration props = UTCConfiguration() # Create a minimal valid DoubletInput input_data = DoubletInput( unknown_input_value=0.0, thickness=100.0, transmissivity=5.0, transmissivity_with_ntg=3.0, ntg=0.1, depth=1500, porosity=0.2, temperature=80.0, ) # Act result = calculate_doublet_performance(props, input_data) # Assert assert isinstance(result, DoubletOutput) No newline at end of file