From de753a675ef4b1eec160e1efd76f17814c566fa5 Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 13:39:49 +0100 Subject: [PATCH 01/13] changed jar --- resources/thermogis_jar/thermogis-1.7.0-shaded.jar | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/thermogis_jar/thermogis-1.7.0-shaded.jar b/resources/thermogis_jar/thermogis-1.7.0-shaded.jar index 131c95d..a4c2d4f 100644 --- a/resources/thermogis_jar/thermogis-1.7.0-shaded.jar +++ b/resources/thermogis_jar/thermogis-1.7.0-shaded.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4eba994664c590c1ad026c1544c00bde3b6cdcb7e498b3968ff7b203d3170ac0 -size 131514517 +oid sha256:40c921919714169a4a699e16e6544891864b4ca74014317a5df9a832913daeb8 +size 173390196 -- GitLab From eb1ee701745cc07b40c1027e220bf8b2592733a7 Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 13:39:57 +0100 Subject: [PATCH 02/13] fixed first test --- tests/test_ThermoGISDoublet_Benchmark.py | 43 ++++++++++++++---------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/tests/test_ThermoGISDoublet_Benchmark.py b/tests/test_ThermoGISDoublet_Benchmark.py index 37b586c..07a0fa2 100644 --- a/tests/test_ThermoGISDoublet_Benchmark.py +++ b/tests/test_ThermoGISDoublet_Benchmark.py @@ -19,39 +19,48 @@ class ThermoGISDoubletBenchmark(TestCase): Logger = JClass("logging.Logger") Mockito = JClass("org.mockito.Mockito") RNG = JClass("tno.geoenergy.stochastic.RandomNumberGenerator") - ThermoGISDoublet = JClass("thermogis.calc.doublet.ThermoGisDoublet") + ThermoGISDoublet = JClass("thermogis.calc.utc.doublet.ThermoGisDoublet") + DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") UTCPropertiesBuilder = JClass("thermogis.properties.builders.UTCPropertiesBuilder") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") # Instantiate the UTC properties class utc_properties = (UTCPropertiesBuilder() .setViscosityMode(ViscosityMode.KESTIN) + .setDhReturnTemp(40) .build()) + permeability = 175 + thickness = 100 + transmissivity = thickness * permeability + temperature = 76 + + input = DoubletInput( + -999.0, # unknowninput + thickness, + transmissivity, + 0.0, # transmissivityWithNtg + 1.0, # ntg + 2000.0, # depth + 0.0,# porosity + temperature, + None, # ates input + ) # Create an instance of a ThermoGISDoublet doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) - # Setting input values - thickness = 100 - permeability = 175 - transmissivity = thickness * permeability - doublet.setReservoirTemp(76) - doublet.setDepth(2000) - doublet.setInjectionTemp(40) - doublet.setNtg(1.0) - doublet.setNoStimulation() # Act - doublet.calculateDoubletPerformance(-999, thickness, transmissivity, False) + results = doublet.calculateDoubletPerformance(input) # Assert self.assertTrue(np.isclose(17500, transmissivity, 0.001)) - self.assertTrue(np.isclose(227.2757568359375, doublet.getFlowrate(), 1)) - self.assertTrue(np.isclose(60, doublet.getDrawdownPressure() / 1e5, 0.001)) - self.assertTrue(np.isclose(6.616096470753937, doublet.getUtcPeurctkWh(), 0.001)) - self.assertTrue(np.isclose(1159.17968, doublet.getWellDistance(), 0.001)) - self.assertTrue(np.isclose(8.636903762817383, doublet.getHeatPowerPerDoublet(), 0.001)) - self.assertTrue(np.isclose(13.627557754516602, doublet.getCop(), 0.001)) + self.assertTrue(np.isclose(227.2757568359375, results.flow(), 1)) + self.assertTrue(np.isclose(60, results.pres(), 0.001)) + self.assertTrue(np.isclose(6.616096470753937, results.utc(), 0.001)) + self.assertTrue(np.isclose(1159.17968, results.welld(), 0.001)) + self.assertTrue(np.isclose(8.636903762817383, results.power(), 0.001)) + self.assertTrue(np.isclose(13.627557754516602, results.cop(), 0.001)) def test_calculateDoubletPerformance_directHeat(self): """ -- GitLab From 9341fad7d4d9a5c87dac3f7d00345e5e95bebe0a Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 13:48:21 +0100 Subject: [PATCH 03/13] fixed another test --- tests/test_ThermoGISDoublet_Benchmark.py | 53 ++++++++++++++---------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/tests/test_ThermoGISDoublet_Benchmark.py b/tests/test_ThermoGISDoublet_Benchmark.py index 07a0fa2..ca01117 100644 --- a/tests/test_ThermoGISDoublet_Benchmark.py +++ b/tests/test_ThermoGISDoublet_Benchmark.py @@ -72,40 +72,48 @@ class ThermoGISDoubletBenchmark(TestCase): Logger = JClass("logging.Logger") Mockito = JClass("org.mockito.Mockito") RNG = JClass("tno.geoenergy.stochastic.RandomNumberGenerator") - ThermoGISDoublet = JClass("thermogis.calc.doublet.ThermoGisDoublet") + ThermoGISDoublet = JClass("thermogis.calc.utc.doublet.ThermoGisDoublet") + DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") # Instantiate the UTC properties class utc_properties = (self.setup_template_utc_properties_builder() .setOpexBase(0) + .setDhReturnTemp(40) .setViscosityMode(ViscosityMode.KESTIN) + .setUseStimulation(False) .build()) - # Create an instance of a ThermoGISDoublet - doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) - - # Setting input values thickness = 100 permeability = 175 transmissivity = thickness * permeability - doublet.setReservoirTemp(76) - doublet.setDepth(2000) - doublet.setInjectionTemp(40) - doublet.setNtg(1.0) - doublet.setNoStimulation() + temperature = 76 + + input = DoubletInput( + -999.0, # unknowninput + thickness, + transmissivity, + 0.0, # transmissivityWithNtg + 1.0, # ntg + 2000.0, # depth + 0.0,# porosity + temperature, + None, # ates input + ) + doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) # Act - doublet.calculateDoubletPerformance(-999, thickness, transmissivity, False) + results = doublet.calculateDoubletPerformance(input) # Assert self.assertTrue(np.isclose(17500, transmissivity, 0.001)) - self.assertTrue(np.isclose(227.2757568359375, doublet.getFlowrate(), 1)) - self.assertTrue(np.isclose(60, doublet.getDrawdownPressure() / 1e5, 0.001)) - self.assertTrue(np.isclose(1159.1796, doublet.getWellDistance(), 0.001)) - self.assertTrue(np.isclose(13.623167037963867, doublet.getCop(), 0.001)) - self.assertTrue(np.isclose(5.229816400909403, doublet.getUtcPeurctkWh(), 0.001)) - self.assertTrue(np.isclose(16.439682499211536, doublet.getSumcapex(), 0.001)) - self.assertTrue(np.isclose(8.624696731567383, doublet.getHeatPowerPerDoublet(), 0.001)) + self.assertTrue(np.isclose(227.2757568359375, results.flow(), 1)) + self.assertTrue(np.isclose(60, results.pres(), 0.001)) + self.assertTrue(np.isclose(1159.1796, results.welld(), 0.001)) + self.assertTrue(np.isclose(13.623167037963867, results.cop(), 0.001)) + self.assertTrue(np.isclose(5.229816400909403, results.utc(), 0.001)) + self.assertTrue(np.isclose(16.439682499211536, results.capex(), 0.001)) + self.assertTrue(np.isclose(8.624696731567383, results.power(), 0.001)) def test_calculateDoubletPerformance_chiller(self): """ @@ -117,7 +125,8 @@ class ThermoGISDoubletBenchmark(TestCase): Logger = JClass("logging.Logger") Mockito = JClass("org.mockito.Mockito") RNG = JClass("tno.geoenergy.stochastic.RandomNumberGenerator") - ThermoGISDoublet = JClass("thermogis.calc.doublet.ThermoGisDoublet") + ThermoGISDoublet = JClass("thermogis.calc.utc.doublet.ThermoGisDoublet") + DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") # Instantiate the UTC properties class @@ -164,7 +173,8 @@ class ThermoGISDoubletBenchmark(TestCase): Logger = JClass("logging.Logger") Mockito = JClass("org.mockito.Mockito") RNG = JClass("tno.geoenergy.stochastic.RandomNumberGenerator") - ThermoGISDoublet = JClass("thermogis.calc.doublet.ThermoGisDoublet") + ThermoGISDoublet = JClass("thermogis.calc.utc.doublet.ThermoGisDoublet") + DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") # Instantiate the UTC properties class @@ -214,7 +224,8 @@ class ThermoGISDoubletBenchmark(TestCase): Logger = JClass("logging.Logger") Mockito = JClass("org.mockito.Mockito") RNG = JClass("tno.geoenergy.stochastic.RandomNumberGenerator") - ThermoGISDoublet = JClass("thermogis.calc.doublet.ThermoGisDoublet") + ThermoGISDoublet = JClass("thermogis.calc.utc.doublet.ThermoGisDoublet") + DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") # Instantiate the UTC properties class -- GitLab From e5d9d2691459248cc67c125e926ebd0241ab58c2 Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 13:49:48 +0100 Subject: [PATCH 04/13] fixed another test --- tests/test_ThermoGISDoublet_Benchmark.py | 38 ++++++++++++++---------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tests/test_ThermoGISDoublet_Benchmark.py b/tests/test_ThermoGISDoublet_Benchmark.py index ca01117..22c4a05 100644 --- a/tests/test_ThermoGISDoublet_Benchmark.py +++ b/tests/test_ThermoGISDoublet_Benchmark.py @@ -138,30 +138,36 @@ class ThermoGISDoubletBenchmark(TestCase): .build()) # Create an instance of a ThermoGISDoublet - doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) - - # Setting input values thickness = 100 permeability = 175 transmissivity = thickness * permeability - doublet.setReservoirTemp(76) - doublet.setDepth(2000) - doublet.setInjectionTemp(60) - doublet.setNtg(1.0) - doublet.setNoStimulation() + temperature = 76 + + input = DoubletInput( + -999.0, # unknowninput + thickness, + transmissivity, + 0.0, # transmissivityWithNtg + 1.0, # ntg + 2000.0, # depth + 0.0,# porosity + temperature, + None, # ates input + ) + doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) # Act - doublet.calculateDoubletPerformance(-999, thickness, transmissivity, False) + results = doublet.calculateDoubletPerformance(-999, thickness, transmissivity, False) # Assert self.assertTrue(np.isclose(17500, transmissivity, 0.001)) - self.assertTrue(np.isclose(256.59625244140625, doublet.getFlowrate(), 1)) - self.assertTrue(np.isclose(60, doublet.getDrawdownPressure() / 1e5, 0.001)) - self.assertTrue(np.isclose(20.470115103822685, doublet.getUtcPeurctkWh(), 0.001)) - self.assertTrue(np.isclose(1227.1484375, doublet.getWellDistance(), 0.001)) - self.assertTrue(np.isclose(1.8887300754346803, doublet.getCop(), 0.001)) - self.assertTrue(np.isclose(1.6594701766967774, doublet.getHeatPowerPerDoublet(), 0.001)) - self.assertTrue(np.isclose(12.748051248109613, doublet.getSumcapex(), 0.001)) + self.assertTrue(np.isclose(256.59625244140625, results.flow(), 1)) + self.assertTrue(np.isclose(60, results.pres() / 1e5, 0.001)) + self.assertTrue(np.isclose(20.470115103822685, results.utc(), 0.001)) + self.assertTrue(np.isclose(1227.1484375, results.welld(), 0.001)) + self.assertTrue(np.isclose(1.8887300754346803, results.cop(), 0.001)) + self.assertTrue(np.isclose(1.6594701766967774, results.power(), 0.001)) + self.assertTrue(np.isclose(12.748051248109613, results.capex(), 0.001)) def test_calculateDoubletPerformance_directheatHP(self): """ -- GitLab From 16b8f57398e344306195f57e7917ba317664353c Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 13:51:19 +0100 Subject: [PATCH 05/13] fixed another test --- tests/test_ThermoGISDoublet_Benchmark.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_ThermoGISDoublet_Benchmark.py b/tests/test_ThermoGISDoublet_Benchmark.py index 22c4a05..6b0d229 100644 --- a/tests/test_ThermoGISDoublet_Benchmark.py +++ b/tests/test_ThermoGISDoublet_Benchmark.py @@ -130,10 +130,12 @@ class ThermoGISDoubletBenchmark(TestCase): ViscosityMode = JClass("tno.geoenergy.ViscosityMode") # Instantiate the UTC properties class - utc_properties = (self.setup_template_utc_properties_builder().setCapexConst(0.5) + utc_properties = (self.setup_template_utc_properties_builder() + .setCapexConst(0.5) .setCapexVariable(1100) .setHeatExchangerEfficiency(0.4) .setHeatExchangerParasitic(0.1) + .setDhReturnTemp(60) .setViscosityMode(ViscosityMode.KESTIN) .build()) @@ -157,12 +159,12 @@ class ThermoGISDoubletBenchmark(TestCase): doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) # Act - results = doublet.calculateDoubletPerformance(-999, thickness, transmissivity, False) + results = doublet.calculateDoubletPerformance(input) # Assert self.assertTrue(np.isclose(17500, transmissivity, 0.001)) self.assertTrue(np.isclose(256.59625244140625, results.flow(), 1)) - self.assertTrue(np.isclose(60, results.pres() / 1e5, 0.001)) + self.assertTrue(np.isclose(60, results.pres(), 0.001)) self.assertTrue(np.isclose(20.470115103822685, results.utc(), 0.001)) self.assertTrue(np.isclose(1227.1484375, results.welld(), 0.001)) self.assertTrue(np.isclose(1.8887300754346803, results.cop(), 0.001)) -- GitLab From e09e2cc4ac4c323f535bc2c9a57475bc4db3654e Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 13:59:25 +0100 Subject: [PATCH 06/13] fixed another test --- tests/test_ThermoGISDoublet_Benchmark.py | 43 +++++++++++++----------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/tests/test_ThermoGISDoublet_Benchmark.py b/tests/test_ThermoGISDoublet_Benchmark.py index 6b0d229..b6dfd7c 100644 --- a/tests/test_ThermoGISDoublet_Benchmark.py +++ b/tests/test_ThermoGISDoublet_Benchmark.py @@ -189,37 +189,42 @@ class ThermoGISDoubletBenchmark(TestCase): utc_properties = (self.setup_template_utc_properties_builder() .setOpexPerPower(100) .setOpexBase(0) + .setHpDirectHeatInputTemp(70) .setUseHeatPump(True) - .setHpApplicationMode(True) - .setHpDirectHeatInputTemp(80) + .setDhReturnTemp(35) .setViscosityMode(ViscosityMode.KESTIN) .build()) # Create an instance of a ThermoGISDoublet - doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) - - # Setting input values thickness = 100 permeability = 175 transmissivity = thickness * permeability - doublet.setReservoirTemp(50) - doublet.setDepth(2000) - doublet.setDhReturnTemp(35) - doublet.setInjectionTemp(15) - doublet.setNtg(1.0) - doublet.setNoStimulation() + temperature = 50 + + input = DoubletInput( + -999.0, # unknowninput + thickness, + transmissivity, + 0.0, # transmissivityWithNtg + 1.0, # ntg + 2000.0, # depth + 0.0,# porosity + temperature, + None, # ates input + ) + doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) # Act - doublet.calculateDoubletPerformance(-999, thickness, transmissivity, False) + results = doublet.calculateDoubletPerformance(input) # Assert self.assertTrue(np.isclose(17499.99940, transmissivity, 0.001)) - self.assertTrue(np.isclose(163.99771118164062, doublet.getFlowrate(), 1)) - self.assertTrue(np.isclose(60, doublet.getDrawdownPressure() / 1e5, 0.001)) - self.assertTrue(np.isclose(5.67, doublet.getHeatPowerPerDoublet(), 0.001)) - self.assertTrue(np.isclose(955.27, doublet.getWellDistance(), 0.001)) - self.assertTrue(np.isclose(3.51, doublet.getCop(), 0.001)) - self.assertTrue(np.isclose(10.9, doublet.getUtcPeurctkWh(), 0.001)) - self.assertTrue(np.isclose(17.56, doublet.getSumcapex(), 0.001)) + self.assertTrue(np.isclose(163.99771118164062, results.flow(), 1)) + self.assertTrue(np.isclose(60, results.pres(), 0.001)) + self.assertTrue(np.isclose(4.97566556930542, results.power(), 0.001)) + self.assertTrue(np.isclose(989.2578125, results.welld(), 0.001)) + self.assertTrue(np.isclose(4.383212200392486, results.cop(), 0.001)) + self.assertTrue(np.isclose(10.401010755009017, results.utc(), 0.001)) + self.assertTrue(np.isclose(16.50359210062243, results.capex(), 0.001)) def test_calculateDoubletPerformance_ORC(self): -- GitLab From b0d74286e35fd8ee235a1e2be28c7c6a2aade24a Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 14:01:58 +0100 Subject: [PATCH 07/13] fixed another test --- tests/test_ThermoGISDoublet_Benchmark.py | 53 ++++++++++++------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/tests/test_ThermoGISDoublet_Benchmark.py b/tests/test_ThermoGISDoublet_Benchmark.py index b6dfd7c..dca7e23 100644 --- a/tests/test_ThermoGISDoublet_Benchmark.py +++ b/tests/test_ThermoGISDoublet_Benchmark.py @@ -14,7 +14,6 @@ class ThermoGISDoubletBenchmark(TestCase): returns the same values. """ # Arrange - # Import Java Classes start_jvm() Logger = JClass("logging.Logger") Mockito = JClass("org.mockito.Mockito") @@ -24,7 +23,6 @@ class ThermoGISDoubletBenchmark(TestCase): UTCPropertiesBuilder = JClass("thermogis.properties.builders.UTCPropertiesBuilder") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") - # Instantiate the UTC properties class utc_properties = (UTCPropertiesBuilder() .setViscosityMode(ViscosityMode.KESTIN) .setDhReturnTemp(40) @@ -46,7 +44,6 @@ class ThermoGISDoubletBenchmark(TestCase): None, # ates input ) - # Create an instance of a ThermoGISDoublet doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) @@ -76,7 +73,6 @@ class ThermoGISDoubletBenchmark(TestCase): DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") - # Instantiate the UTC properties class utc_properties = (self.setup_template_utc_properties_builder() .setOpexBase(0) .setDhReturnTemp(40) @@ -129,7 +125,6 @@ class ThermoGISDoubletBenchmark(TestCase): DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") - # Instantiate the UTC properties class utc_properties = (self.setup_template_utc_properties_builder() .setCapexConst(0.5) .setCapexVariable(1100) @@ -139,7 +134,6 @@ class ThermoGISDoubletBenchmark(TestCase): .setViscosityMode(ViscosityMode.KESTIN) .build()) - # Create an instance of a ThermoGISDoublet thickness = 100 permeability = 175 transmissivity = thickness * permeability @@ -185,7 +179,6 @@ class ThermoGISDoubletBenchmark(TestCase): DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") - # Instantiate the UTC properties class utc_properties = (self.setup_template_utc_properties_builder() .setOpexPerPower(100) .setOpexBase(0) @@ -194,7 +187,7 @@ class ThermoGISDoubletBenchmark(TestCase): .setDhReturnTemp(35) .setViscosityMode(ViscosityMode.KESTIN) .build()) - # Create an instance of a ThermoGISDoublet + thickness = 100 permeability = 175 transmissivity = thickness * permeability @@ -241,39 +234,45 @@ class ThermoGISDoubletBenchmark(TestCase): DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") ViscosityMode = JClass("tno.geoenergy.ViscosityMode") - # Instantiate the UTC properties class - utc_properties = (self.setup_template_utc_properties_builder().setCapexConst(0) + utc_properties = (self.setup_template_utc_properties_builder() + .setCapexConst(0) .setCapexVariable(2300) .setHeatExchangerEfficiency(0.6) .setUseORC(True) .setViscosityMode(ViscosityMode.KESTIN) + .setDhReturnTemp(60) .setHeatExchangerBasetemp(20).build()) - # Create an instance of a ThermoGISDoublet - doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) - - # Setting input values thickness = 100 permeability = 175 transmissivity = thickness * permeability - doublet.setReservoirTemp(100) - doublet.setDepth(2000) - doublet.setInjectionTemp(60) - doublet.setNtg(1.0) - doublet.setNoStimulation() + temperature = 100 + + input = DoubletInput( + -999.0, # unknowninput + thickness, + transmissivity, + 0.0, # transmissivityWithNtg + 1.0, # ntg + 2000.0, # depth + 0.0,# porosity + temperature, + None, # ates input + ) + doublet = ThermoGISDoublet(Mockito.mock(Logger), RNG(0), utc_properties) # Act - doublet.calculateDoubletPerformance(-999, thickness, transmissivity, False) + results = doublet.calculateDoubletPerformance(input) # Assert self.assertTrue(np.isclose(17499.99940, transmissivity, 0.001)) - self.assertTrue(np.isclose(293.6246643066406, doublet.getFlowrate(), 1)) - self.assertTrue(np.isclose(60, doublet.getDrawdownPressure() / 1e5, 0.001)) - self.assertTrue(np.isclose(36.98296076530068, doublet.getUtcPeurctkWh(), 0.001)) - self.assertTrue(np.isclose(0.05274631495788107, doublet.getHeatPowerPerDoublet(), 0.01)) - self.assertTrue(np.isclose(1306.4453125, doublet.getWellDistance(), 0.001)) - self.assertTrue(np.isclose(0.06459403120103477, doublet.getCop(), 0.001)) - self.assertTrue(np.isclose(12.44409167482118, doublet.getSumcapex(), 0.001)) + self.assertTrue(np.isclose(293.6246643066406, results.flow(), 1)) + self.assertTrue(np.isclose(60, results.pres(), 0.001)) + self.assertTrue(np.isclose(36.98296076530068, results.utc(), 0.001)) + self.assertTrue(np.isclose(0.05274631495788107, results.power(), 0.01)) + self.assertTrue(np.isclose(1306.4453125, results.welld(), 0.001)) + self.assertTrue(np.isclose(0.06459403120103477, results.cop(), 0.001)) + self.assertTrue(np.isclose(12.44409167482118, results.capex(), 0.001)) def setup_template_utc_properties_builder(self): return (instantiate_utc_properties_builder() -- GitLab From f8453d2f3f4532b0f3ec09ebd61de5cccdf16859 Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 14:40:59 +0100 Subject: [PATCH 08/13] fixed all other tests --- src/pythermogis/thermogis_classes/doublet.py | 95 +++++++------------- 1 file changed, 30 insertions(+), 65 deletions(-) diff --git a/src/pythermogis/thermogis_classes/doublet.py b/src/pythermogis/thermogis_classes/doublet.py index 5c1e8d7..5cad3f4 100644 --- a/src/pythermogis/thermogis_classes/doublet.py +++ b/src/pythermogis/thermogis_classes/doublet.py @@ -106,13 +106,25 @@ def calculate_performance_of_single_location(mask: float, depth: float, thicknes # Instantiate ThermoGIS doublet doublet = instantiate_thermogis_doublet(utc_properties, rng_seed) - set_doublet_parameters(doublet, transmissivity_with_ntg, depth, porosity, ntg, temperature, utc_properties) + + DoubletInput = JClass("thermogis.calc.utc.doublet.records.DoubletInput") + input = DoubletInput( + -9999.0, # unknowninput + thickness, + transmissivity, + transmissivity_with_ntg, + ntg, + depth, + porosity, + temperature, + None, # ates input + ) # The Java routine which calculates DoubletPerformance, for more detail on the simulation inspect the Java source code - doublet.calculateDoubletPerformance(-9999.0, thickness, transmissivity, False) + results = doublet.calculateDoubletPerformance(input) # If calculation was not successful, return mask value - if doublet.getUtcPeurctkWh() == -9999.0: + if results.utc() == -9999.0: return (mask_value,) * 14 # calculate net-present-value using the utc-cutoffs @@ -121,28 +133,27 @@ def calculate_performance_of_single_location(mask: float, depth: float, thicknes else: utc_cut = utc_properties.utcCutoff() - hprod = doublet.getDiscountedHeatProducedP() - npv = 1e-6 * (utc_cut - doublet.getUtcPeurctkWh()) * 3.6 * hprod * (1 - utc_properties.taxRate()) + hprod = results.hprod() + npv = 1e-6 * (utc_cut - results.utc()) * 3.6 * hprod * (1 - utc_properties.taxRate()) # get values from doublet - output_values = {"power": doublet.getHpP(), - "heat_pump_power": doublet.getHeatPowerPerDoublet(), - "capex": doublet.getSumcapex(), - "opex": doublet.getOpexFirstProdYear(), - "utc": doublet.getUtcPeurctkWh(), + output_values = {"power": results.power(), + "heat_pump_power": results.hppower(), + "capex": results.capex(), + "opex": results.opex(), + "utc": results.utc(), "npv": npv, "hprod": hprod, - "cop": doublet.getCop(), - "cophp": doublet.getCopHpP(), - "pres": doublet.getPresP() / 1e5, - "flow_rate": doublet.getFlowrate(), - "welld": doublet.getWellDistP(), - "inj_temp": doublet.getInjectionTemp(), - "prd_temp": doublet.getProductionTemp() + "cop": results.cop(), + "cophp": results.cophp(), + "pres": results.pres(), + "flow_rate": results.flow(), + "welld": results.welld(), + "inj_temp": 0, #TODO: this is ATES output. Is ATES supported in pytg? + "prd_temp": 0 #TODO: this is ATES output. Is ATES supported in pytg? } # Reset doublet variables for next calculation - doublet.setProjectVariables(False, 0.0) return output_values["power"], output_values["heat_pump_power"], output_values["capex"], output_values["opex"], output_values["utc"], output_values["npv"], output_values["hprod"], output_values["cop"], output_values[ "cophp"], output_values["pres"], output_values["flow_rate"], output_values["welld"], output_values["inj_temp"], output_values["prd_temp"] @@ -186,7 +197,7 @@ def instantiate_thermogis_doublet(utc_properties, rng_seed: int = None) -> JClas Logger = JClass("logging.Logger") Mockito = JClass("org.mockito.Mockito") RNG = JClass("tno.geoenergy.stochastic.RandomNumberGenerator") - ThermoGISDoublet = JClass("thermogis.calc.doublet.ThermoGisDoublet") + ThermoGISDoublet = JClass("thermogis.calc.utc.doublet.ThermoGisDoublet") # Create an instance of a ThermoGISDoublet if rng_seed is not None: @@ -196,50 +207,4 @@ def instantiate_thermogis_doublet(utc_properties, rng_seed: int = None) -> JClas doublet = ThermoGISDoublet(Mockito.mock(Logger), rng, utc_properties) - # Set parameters that do not change across simulations - doublet.setSurfaceTemperature(utc_properties.surfaceTemperature()) - doublet.setDhReturnTemp(utc_properties.dhReturnTemp()) - return doublet - -def set_doublet_parameters(doublet, transmissivity_with_ntg: float, depth: float, porosity: float, ntg: float, temperature: float, utc_properties: JClass): - """ - Set necessary data on the doublet class for a single location prior to simulation. - - Parameters - ---------- - utc_properties : dict - Dictionary containing UTC properties. - doublet : object - An instance of the ThermoGIS doublet class. - transmissivity_with_ntg : float - Product of transmissivity and net-to-gross. - depth : float - Depth of the aquifer in meters. - porosity : float - Porosity of the aquifer (fraction). - ntg : float - Net-to-gross ratio of the aquifer (fraction). - temperature : float - Temperature of the aquifer in degrees Celsius. - - Returns - ------- - None - """ - - if not utc_properties.useStimulation() or transmissivity_with_ntg > utc_properties.stimKhMax(): - doublet.setNoStimulation() - - doublet.setDepth(depth) - doublet.setPorosity(porosity) - doublet.setNtg(ntg) - doublet.setReservoirTemp(temperature) - - if utc_properties.useHeatPump(): - if utc_properties.calculateCop() and not utc_properties.hpApplicationMode(): - doublet.setInjectionTemp(doublet.calculateInjectionTempWithHeatPump(temperature, utc_properties.hpDirectHeatInputTemp())) - else: - doublet.setInjectionTemp(np.max([temperature - utc_properties.maxCoolingTempRange(), utc_properties.hpMinimumInjectionTemperature()])) - else: - doublet.setInjectionTemp(np.max([temperature - utc_properties.maxCoolingTempRange(), utc_properties.dhReturnTemp()])) -- GitLab From 52478b5bb111534ab3771f6a86d43657b5820b63 Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 14:53:20 +0100 Subject: [PATCH 09/13] add .env to gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7a70f18..3790500 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .idea __pycache__ +.env # pixi environments .pixi @@ -7,4 +8,5 @@ __pycache__ build dist -tests/resources/test_output \ No newline at end of file +tests/resources/test_output + -- GitLab From dbd11abf653717f0af796bb5c15f7be2877dbf1f Mon Sep 17 00:00:00 2001 From: knappersfy Date: Tue, 4 Nov 2025 14:59:45 +0100 Subject: [PATCH 10/13] added dotenv and updated docs --- README.md | 5 +++++ docs/images/dotenv_file_example.png | Bin 0 -> 43174 bytes docs/install/install.md | 6 ++++++ pixi.lock | 16 +++++++++++++++- pyproject.toml | 1 + .../thermogis_classes/jvm_start.py | 18 +++++++++++++----- 6 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 docs/images/dotenv_file_example.png diff --git a/README.md b/README.md index 2a4079b..9153aea 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,17 @@ This package requires a Java 17 VM (we recommend using [Amazon Corretto 17](http ### 2. Create Required Environment Variables +Create a file called ``.env`` in your project root folder. +This file should contain the following variables: + - `JAVA_HOME`: Path to the Java 17 installation *(e.g., on Windows: `C:\Program Files\Amazon Corretto\jdk17.0.0_0`)* - `THERMOGIS_JAR`: Path to the ThermoGIS `.jar` file; __including__ the name of the jar file *(e.g. `/path/to/the/thermogis-1.7.0-shaded.jar`)* +Example: +![img.png](docs/images/dotenv_file_example.png) ### 3. Import pyThermoGIS into your own python projects diff --git a/docs/images/dotenv_file_example.png b/docs/images/dotenv_file_example.png new file mode 100644 index 0000000000000000000000000000000000000000..d7f4c88118e53388ff95e66e0dd4298d51fe4c3e GIT binary patch literal 43174 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.5.2,<2 - xarray==2024.9.0.* @@ -4901,6 +4903,18 @@ packages: - pkg:pypi/python-dateutil?source=hash-mapping size: 222505 timestamp: 1733215763718 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + sha256: aa98e0b1f5472161318f93224f1cfec1355ff69d2f79f896c0b9e033e4a6caf9 + md5: 083725d6cd3dc007f06d04bcf1e613a2 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/python-dotenv?source=hash-mapping + size: 26922 + timestamp: 1761503229008 - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.3-h4df99d1_101.conda sha256: 54a19e0ed3be0c3397301482b44008fc8d21058ebb9d17ed7046b14bda0e16f4 md5: 82c2641f2f0f513f7d2d1b847a2588e3 diff --git a/pyproject.toml b/pyproject.toml index c2839a4..13e899d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,6 +66,7 @@ mkdocstrings-python = ">=1.16.12,<2" dask = ">=2025.5.1,<2026" narwhals = ">=1.43.1,<2" pre-commit = ">=4.3.0,<5" +python-dotenv = ">=1.2.1,<2" [tool.ruff] line-length = 88 diff --git a/src/pythermogis/thermogis_classes/jvm_start.py b/src/pythermogis/thermogis_classes/jvm_start.py index 57a02c9..a4f72f3 100644 --- a/src/pythermogis/thermogis_classes/jvm_start.py +++ b/src/pythermogis/thermogis_classes/jvm_start.py @@ -3,14 +3,20 @@ from pathlib import Path import jpype import faulthandler +from dotenv import load_dotenv + +load_dotenv() + def get_thermogis_jar_path() -> str: """ From the resources directory, return the path to the ThermoGIS Jar """ - if os.getenv("THERMOGIS_JAR") is None: - raise ValueError("THERMOGIS_JAR environment variable is not set") + tg_jar = os.getenv("THERMOGIS_JAR") + + if tg_jar is None: + raise ValueError("THERMOGIS_JAR environment variable is not set in the .env file.") - thermogis_jar_path = Path(os.getenv("THERMOGIS_JAR")) + thermogis_jar_path = Path(tg_jar) if not thermogis_jar_path.exists(): raise FileNotFoundError(f"Jar file not found at {thermogis_jar_path}, check that you have downloaded the ThermoGIS Jar and set the THERMOGIS_JAR environment variable.") return str(thermogis_jar_path) @@ -22,8 +28,10 @@ def start_jvm(): This method ensures a clean startup of the jvm packaged with this repo :return: """ - if os.getenv("JAVA_HOME") is None: - raise ValueError("JAVA_HOME environment variable is not set") + java_home = os.getenv("JAVA_HOME") + + if java_home is None: + raise ValueError("JAVA_HOME environment variable is not set in the .env file.") if not jpype.isJVMStarted(): # when running pytest with jpype a "Windows fatal exception" is thrown which in reality has no affect on the outcome or performance. -- GitLab From 07d557b24c37656f3644933baffc3927ed0838c5 Mon Sep 17 00:00:00 2001 From: knappersfy Date: Wed, 5 Nov 2025 15:59:56 +0100 Subject: [PATCH 11/13] fixed inj and prd temop --- pixi.lock | 2 +- resources/thermogis_jar/thermogis-1.7.0-shaded.jar | 4 ++-- src/pythermogis/thermogis_classes/doublet.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pixi.lock b/pixi.lock index fac6c1d..07eb60c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4830,7 +4830,7 @@ packages: - pypi: ./ name: pythermogis version: 1.1.2 - sha256: 043b1dc3f19c94485d7c1c88cfdd65bde30f3cdb479db50a6138630311c3967c + sha256: 6c2c51bb843ce5d8e0fb17a271a6425a468bc6da8718c96355b2132adc2a0c4d requires_dist: - jpype1>=1.5.2,<2 - xarray==2024.9.0.* diff --git a/resources/thermogis_jar/thermogis-1.7.0-shaded.jar b/resources/thermogis_jar/thermogis-1.7.0-shaded.jar index a4c2d4f..adcdf61 100644 --- a/resources/thermogis_jar/thermogis-1.7.0-shaded.jar +++ b/resources/thermogis_jar/thermogis-1.7.0-shaded.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40c921919714169a4a699e16e6544891864b4ca74014317a5df9a832913daeb8 -size 173390196 +oid sha256:12bc27ae3cc36ac4ebf666256587d82c94a580759b87cfdb6d3b1ee261ceb95e +size 173390414 diff --git a/src/pythermogis/thermogis_classes/doublet.py b/src/pythermogis/thermogis_classes/doublet.py index 5cad3f4..013be70 100644 --- a/src/pythermogis/thermogis_classes/doublet.py +++ b/src/pythermogis/thermogis_classes/doublet.py @@ -149,8 +149,8 @@ def calculate_performance_of_single_location(mask: float, depth: float, thicknes "pres": results.pres(), "flow_rate": results.flow(), "welld": results.welld(), - "inj_temp": 0, #TODO: this is ATES output. Is ATES supported in pytg? - "prd_temp": 0 #TODO: this is ATES output. Is ATES supported in pytg? + "inj_temp": results.injectionTemp(), + "prd_temp": results.productionTemp(), } # Reset doublet variables for next calculation -- GitLab From d403dfc7e6adc4edef758170e8d9c60dc49e7c23 Mon Sep 17 00:00:00 2001 From: bretth Date: Thu, 6 Nov 2025 08:40:44 +0100 Subject: [PATCH 12/13] replacing the jar file... as their was a print statment i had accidently put on main in doubletcalc causing lots of blank lines to be printed... --- resources/thermogis_jar/thermogis-1.7.0-shaded.jar | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/thermogis_jar/thermogis-1.7.0-shaded.jar b/resources/thermogis_jar/thermogis-1.7.0-shaded.jar index adcdf61..4e61214 100644 --- a/resources/thermogis_jar/thermogis-1.7.0-shaded.jar +++ b/resources/thermogis_jar/thermogis-1.7.0-shaded.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12bc27ae3cc36ac4ebf666256587d82c94a580759b87cfdb6d3b1ee261ceb95e -size 173390414 +oid sha256:933ccf904942e0e7861d860cbf1c0f7a846a3f98f0ff8570922b3adaa77eb873 +size 173317400 -- GitLab From 63c1c4d14da4fdebf2a92c34c50802a07aad7bdb Mon Sep 17 00:00:00 2001 From: bretth Date: Thu, 6 Nov 2025 09:05:15 +0100 Subject: [PATCH 13/13] replacing the jar file... as their was a print statment i had accidently put on main in doubletcalc causing lots of blank lines to be printed... --- resources/thermogis_jar/thermogis-1.7.0-shaded.jar | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/thermogis_jar/thermogis-1.7.0-shaded.jar b/resources/thermogis_jar/thermogis-1.7.0-shaded.jar index 4e61214..2c274aa 100644 --- a/resources/thermogis_jar/thermogis-1.7.0-shaded.jar +++ b/resources/thermogis_jar/thermogis-1.7.0-shaded.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:933ccf904942e0e7861d860cbf1c0f7a846a3f98f0ff8570922b3adaa77eb873 -size 173317400 +oid sha256:7991ac4e8853db7f863de4c809f0d8035ea8c9ca5a90707e2e93799d6d6f39d6 +size 173317904 -- GitLab