TNO Intern

Commit 0c7bfcb4 authored by Hen Brett's avatar Hen Brett 🐔
Browse files

Merge branch '22-enable-the-reading-of-the-ga4a-configuration-files' into 'main'

Resolve "Enable the reading of the GA4A configuration files"

Closes #22

See merge request AGS/pythermogis!22
parents d4effc35 d85f1926
Loading
Loading
Loading
Loading
+77 −11
Original line number Diff line number Diff line
# pyThermoGIS

**pyThermoGIS** is a Python package that provides API access to the [ThermoGIS](https://www.thermogis.nl/en) geothermal simulation software. The simulations are conducted in Java, and this package uses [JPype](https://jpype.readthedocs.io/en/latest/userguide.html) to create a Python-Java binding.
## What can this package do?
**pyThermoGIS** is a Python package that provides API access to the [ThermoGIS](https://www.thermogis.nl/en) doublet simulations and economic calculations. 

It uses [xarray](https://docs.xarray.dev/en/stable/index.html) `Dataset` as input and can be combined with the [pygridsio](https://pypi.org/project/pygridsio/) package to read and process 2D raster data.
This package allows a user to simulate geothermal doublets providing the following parameters: 
- Top depth
- Thickness (mean & standard deviation)
- Porosity
- Permeability (mean & standard deviation)
- Temperature (Optional: if not provided, a temperature gradient will be used)


The code will simulate a Geothermal doublet, utilizing ThermoGIS with DoubletCalc1D as the engine to produce values of:

- power [Mega Watt Hour]
- heat pump power [Mega Watt Hour]
- capex (Capital expenditure) [Million €]
- opex (Operational expenditure) [€/kW]
- utc (Unit Technical Cost [€cent/kWH])
- npv (Net-present-value)
- hprod (Discounted Heat Produced)
- cop 
- cophp
- pressure 
- flow rate [m³/hr]
- well depth [m]

For details on how these parameters are calculated we refer users to the [Thermogis calculation webpage](https://www.thermogis.nl/en/calculation-model)

The simulations are conducted in Java, and this package uses [JPype](https://jpype.readthedocs.io/en/latest/userguide.html) to create a Python-Java binding.

It uses [xarray](https://docs.xarray.dev/en/stable/index.html) to handle input and output parameters, a User should inform themselves and learn the usage of the Xarray package.

This module can be combined with the [pygridsio](https://pypi.org/project/pygridsio/) package to read and process 2D raster data.

## What can this *NOT* package do?
This package provides a narrow access point to the doublet simulations and UTC economic calculations from ThermoGIS, it does not implement the following ThermoGIS processes/methodologies:

- Stacking of Aquifers
- Calculation of Potential maps
- Calculation of Resources
- Calculation of Overview maps
- HTO-ATES simulations
- Property Modelling
- Temperature Modelling
- Reading, and writing of grid files (Checkout [pygridsio](https://pypi.org/project/pygridsio/))

---

## Installation
## Installation for use in your own projects

### 1. Install Java 17 and Download the ThermoGIS JAR

This module requires a Java 17 VM. We recommend using [Amazon Corretto 17](https://docs.aws.amazon.com/corretto/latest/corretto-17-ug/downloads-list.html).
This package requires a Java 17 VM (we recommend using [Amazon Corretto 17](https://docs.aws.amazon.com/corretto/latest/corretto-17-ug/downloads-list.html)) and a ThermoGIS Jar file (Version >=1.7).

*(You can find the JAR in this repository's resources folder or request it via the [ThermoGIS website](https://www.thermogis.nl/).)*

#### 🧱 Required Environment Variables

@@ -18,7 +62,6 @@ This module requires a Java 17 VM. We recommend using [Amazon Corretto 17](https
  *(e.g., on Windows: `C:\Program Files\Amazon Corretto\jdk17.0.0_0`)*

- `THERMOGIS_JAR`: Path to the ThermoGIS `.jar` file
  *(You can find the JAR in this repository's resources folder or request it via the [ThermoGIS website](https://www.thermogis.nl/).)*

---

@@ -26,6 +69,8 @@ This module requires a Java 17 VM. We recommend using [Amazon Corretto 17](https

This repository is currently hosted privately on [ci.tno.nl](https://ci.tno.nl) and will become publicly available in the future. 

Until then you will need to use Gitlab API keys to use this module:

#### 🔐 Step-by-step Installation

##### a. Create a Personal Access Token (PAT)
@@ -37,9 +82,9 @@ This repository is currently hosted privately on [ci.tno.nl](https://ci.tno.nl)
   - `read_package_registry`
4. Copy and store the token securely (you won’t be able to see it again).

##### b. Install the Package
##### b. Install pyThermoGIS using pip

```bash
```
pip install pythermogis --index-url https://__token__:<your_personal_token>@ci.tno.nl/gitlab/api/v4/projects/18271/packages/pypi/simple
```

@@ -113,11 +158,11 @@ results = calculate_doublet_performance(input_grids)
print(results)
```

### Running Doublet simulations with custom simulation parameters
### 🧰 Running Doublet simulations with custom simulation parameters
To adjust properties of the simulation, you can pass a `utc_properties` instance to the calculate_doublet_performance function.
A `utc_properties` instance is a JClass implementation of the Java UTCProperties class. It is generated by using the `utc_properties_builder`, upon which custom properties can be set, and used to build an instance of the `utc_properties`.

Be aware, that you will need access to the ThermoGIS java source code to fully understand what these properties do and not all properties are actually utilised in this python api (especially those which refer to paths to files).
Be aware: that you will need access to the ThermoGIS java source code to fully understand what these properties do and not all properties are actually utilised in this python api (especially those which refer to paths to files).

Common properties to change include:
- `setUseHeatPump(Boolean)`: if true, include a heat-pump when modelling
@@ -148,6 +193,27 @@ results = calculate_doublet_performance(input_data, utc_properties=utc_propertie
print(results)
```

If you have a valid configuration file, you can parse a utc_properties class using the method: `instantiate_utc_properties_from_xml`, some example configuration xml files are found in `tests/resources/scenarios`. 

```python
from pythermogis import calculate_doublet_performance, instantiate_utc_properties_from_xml
import xarray as xr

input_data = xr.Dataset({
    "thickness_mean": ((), 300),
    "thickness_sd": ((), 50),
    "ntg": ((), 0.5),
    "porosity": ((), 0.5),
    "depth": ((), 5000),
    "ln_permeability_mean": ((), 5),
    "ln_permeability_sd": ((), 0.5),
})

utc_properties = instantiate_utc_properties_from_xml("path/to/valid/xml/file")
results = calculate_doublet_performance(input_data, utc_properties=utc_properties)
print(results)
```

---

## Contributing
@@ -162,7 +228,7 @@ This project is licensed under the MIT License. See the `LICENSE` file for detai

---

## Installation for Development
## 🪛 Installation for Development

### 🛠️ Install from Source with Pixi

+6 −6
Original line number Diff line number Diff line
@@ -6,18 +6,18 @@ import os

def instantiate_utc_properties_from_xml(xml_file: str | Path) -> JClass:
    """Provided the path to an xml scenario file, parse this file for the utc properties; beware, the xml parses does some validation checks, checking that files exist.
    Even if these parameters are not needed by the pyThermoGIS module, if these validation checks fail, the xml parser will fail."""
    Even if these parameters are not needed by the pyThermoGIS module, if these validation checks fail, the xml parser will fail. To get around this set these variables to null"""
    start_jvm()
    root = ET.parse(xml_file).getroot() # Parsing the xml file to a string
    root = ET.parse(xml_file).getroot()

    # change the parameters input_data_directory, results_directory, and temperature_voxet_file to null
    # change the parameters input_data_directory, results_directory, and temperature_voxet_file to null; these parameters are not used by pyThermoGIS, and if they are not null then the UTCXmlParser will check for directory and file
    #   existence and cause the parsing to fail if they do not exist.
    variables = [root.find(variable) for variable in ["input_data_directory", "results_directory", "temperature_voxet_file"]]
    for var in variables:
        var.text = ""

    # parse to string and pass to the utc xml parser
    xmlstr = ET.tostring(root, encoding='utf8', method='xml')
    return JClass("thermogis.properties.parsers.UTCXmlParser")().parse(xmlstr)
    # parse to string and pass to the Java utc xml parser
    return JClass("thermogis.properties.parsers.UTCXmlParser")().parse(ET.tostring(root, encoding='utf8', method='xml'))

def instantiate_utc_properties_builder() -> JClass:
    """
+35 −38
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project hto="false">
   <application_version>1.4.2</application_version>
<project ATES="false">
   <application_version>1.7.0</application_version>
   <application_name>ThermoGIS</application_name>
   <input_data_directory/>
   <maps_prefix__can_be_empty_/>
   <results_directory/>
   <compare_results>0.0</compare_results>
   <comparison_directory/>
   <output_maps_for_petrel>0.0</output_maps_for_petrel>
   <max_number_of_processors_for_calculations>64.0</max_number_of_processors_for_calculations>
   <output_grid_file_type___zmap___asc___nc_>.nc</output_grid_file_type___zmap___asc___nc_>
   <remove_padding_from_input_grids>1.0</remove_padding_from_input_grids>
   <aquifer_list__comma_separated__excluding_stacked_aquifers_>NMVFS, NMVFV, NMRFT, NMRFV, NLFFS, NLFFD, NLLFR, NLLFS, KNGLG_KNGLS, KNNSG, KNNSL, KNNSY, KNNSB, KNNSR, KNNSF_KNNSP, SLDNA, SLDND, RNROF, RNSOB, RBMH, RBMDU, RBMDL, RBMVU, RBMVL, RBSHN, ROSL_ROSLU, ROSLL, DCH, DCD</aquifer_list__comma_separated__excluding_stacked_aquifers_>
   <property_grids_file_extension>.asc</property_grids_file_extension>
   <property_grids__in_input_data_directory__stacked_grids_will_be_created_>
      <row_1>Permeability;no;__k.zmap;__K.zmap</row_1>
      <row_2>PermeabilityLNSD;no;__k_lnsd.zmap;__K_lnsd.zmap</row_2>
      <row_3>Porosity;no;__phi.zmap;__PHI.zmap</row_3>
      <row_4>Thickness;no;__thick.zmap;__THICK.zmap</row_4>
      <row_5>ThicknessSD;no;__thick_sd.zmap;__THICK_sd.zmap</row_5>
      <row_6>Depth;no;__top.zmap;__TOP.zmap</row_6>
      <row_7>NetToGross;no;__ntg.zmap;__NTG.zmap</row_7>
      <row_8>Temperature;yes;__temperature.zmap;__TEMPERATURE.zmap</row_8>
      <row_9>HCAccum;yes;__hc_accum.zmap;__HC_ACCUM.zmap</row_9>
      <row_10>BoundaryShapefile;yes;__BoundaryShapefile.shp;__BOUNDARYSHAPEFILE.shp</row_10>
      <row_1>Permeability;no;__k.zmap</row_1>
      <row_2>PermeabilityLNSD;no;__k_lnsd.zmap</row_2>
      <row_3>Porosity;no;__phi.zmap</row_3>
      <row_4>Thickness;no;__thick.zmap</row_4>
      <row_5>ThicknessSD;no;__thick_sd.zmap</row_5>
      <row_6>Depth;no;__top.zmap</row_6>
      <row_7>NetToGross;no;__ntg.zmap</row_7>
      <row_8>Temperature;yes;__temperature.zmap</row_8>
      <row_9>HCAccum;yes;__hc_accum.zmap</row_9>
      <row_10>BoundaryShapefile;yes;__BoundaryShapefile.shp</row_10>
   </property_grids__in_input_data_directory__stacked_grids_will_be_created_>
   <calculate_stacked_aquifers>1.0</calculate_stacked_aquifers>
   <thickness_correlation_factor___1_to_1__0__no_corr_>0.0</thickness_correlation_factor___1_to_1__0__no_corr_>
@@ -49,9 +52,10 @@
   <minimum_production_temperature>70.0</minimum_production_temperature>
   <heating_return_temperature>60.0</heating_return_temperature>
   <maximum_cooling_temperature_range>300.0</maximum_cooling_temperature_range>
   <number_of_samples_for_kh_distribution>20000.0</number_of_samples_for_kh_distribution>
   <exclude_hydrocarbon_areas>1.0</exclude_hydrocarbon_areas>
   <use_boundary_shapefile>0.0</use_boundary_shapefile>
   <validate_input_grids>1.0</validate_input_grids>
   <validate_output_grids>1.0</validate_output_grids>
   <optimize_well_distance>1.0</optimize_well_distance>
   <minimum_well_distance>100.0</minimum_well_distance>
   <maximum_well_distance>3000.0</maximum_well_distance>
@@ -62,7 +66,6 @@
   <allowed_temperature_drop_as_fraction_of_deltat>0.1</allowed_temperature_drop_as_fraction_of_deltat>
   <maximum_flow>500.0</maximum_flow>
   <well_distance>1500.0</well_distance>
   <target_cop>15.0</target_cop>
   <hydraulic_gradient_injection_water__sodm_max_inj_pres_>0.105</hydraulic_gradient_injection_water__sodm_max_inj_pres_>
   <minimum_pump_pressure>1.0</minimum_pump_pressure>
   <maximum_pump_pressure>600.0</maximum_pump_pressure>
@@ -71,11 +74,10 @@
   <use_3d_temperature_voxet_model>1.0</use_3d_temperature_voxet_model>
   <temperature_voxet_file/>
   <temp_gradient__surface_temp__below__also_used_>31.0</temp_gradient__surface_temp__below__also_used_>
   <net_to_gross>1.0</net_to_gross>
   <surface_temperature>10.0</surface_temperature>
   <kh_cutoff__speed_up_calculation_>1.0</kh_cutoff__speed_up_calculation_>
   <anistropy__kv_kh__for_doubletcalc1d>1.0</anistropy__kv_kh__for_doubletcalc1d>
   <salinity_at_surface__ppm_>0</salinity_at_surface__ppm_>
   <salinity_at_surface__ppm_>0.0</salinity_at_surface__ppm_>
   <salinity_gradient__ppm_m_>47.0</salinity_gradient__ppm_m_>
   <pump_efficiency>0.6</pump_efficiency>
   <pump_depth>300.0</pump_depth>
@@ -88,12 +90,7 @@
   <producer_skin>-1.0</producer_skin>
   <economic_lifetime>15.0</economic_lifetime>
   <drilling_time>1.0</drilling_time>
   <heat_exchanger_construction_time>0.0</heat_exchanger_construction_time>
   <heat_exchanger_capex>0.1</heat_exchanger_capex>
   <annual_load_hours>8000.0</annual_load_hours>
   <pump_costs>0.8</pump_costs>
   <pump_replacement_time>5.0</pump_replacement_time>
   <pump_replacement_work_over_costs>0.5</pump_replacement_work_over_costs>
   <annual_load_hours>6000.0</annual_load_hours>
   <annual_opex_base>10000.0</annual_opex_base>
   <annual_opex_per_unit_power>50.0</annual_opex_per_unit_power>
   <annual_opex_per_unit_energy_produced>0.0</annual_opex_per_unit_energy_produced>
@@ -111,8 +108,6 @@
   <inflation>2.0</inflation>
   <required_return_on_equity>15.0</required_return_on_equity>
   <debt_equity>80.0</debt_equity>
   <eia_percentage>0.0</eia_percentage>
   <transmission_costs>0.0</transmission_costs>
   <use_orc>1.0</use_orc>
   <heat_conversion_efficiency>0.6</heat_conversion_efficiency>
   <parasitic_power_fraction_of_net_power>0.0</parasitic_power_fraction_of_net_power>
@@ -135,19 +130,20 @@
   <alternative_heating_price>2.8</alternative_heating_price>
   <application_mode>0.0</application_mode>
   <rosim_settings_file__must_contain__aquifer__layer_/>
   <hto_aquifer_anisotropy>5.0</hto_aquifer_anisotropy>
   <ates_aquifer_anisotropy>5.0</ates_aquifer_anisotropy>
   <thermal_radius_factor>2.0</thermal_radius_factor>
   <hto_charge_temperature>80.0</hto_charge_temperature>
   <hto_injection_production_period__max_182_days_>120.0</hto_injection_production_period__max_182_days_>
   <ates_charge_temperature>80.0</ates_charge_temperature>
   <ates_injection_production_period__max_182_days_>120.0</ates_injection_production_period__max_182_days_>
   <rosim_simulation_time__constant_power_after_>5.0</rosim_simulation_time__constant_power_after_>
   <use_power_from_last_rosim_year_for_all_years>0.0</use_power_from_last_rosim_year_for_all_years>
   <hto_minimum_flow_rate__speed_up_calculation_>0.0</hto_minimum_flow_rate__speed_up_calculation_>
   <hto_minimum_depth__speed_up_calculation_>0.0</hto_minimum_depth__speed_up_calculation_>
   <hto_maximum_depth__speed_up_calculation_>500.0</hto_maximum_depth__speed_up_calculation_>
   <hto_filter_fraction__of_aquifer_thickness_>0.8</hto_filter_fraction__of_aquifer_thickness_>
   <hto_clogging_velocity>0.3</hto_clogging_velocity>
   <hto_membrane_filter_index>1.0</hto_membrane_filter_index>
   <hto_depth_multiplication_factor>0.01</hto_depth_multiplication_factor>
   <use_values_from_last_rosim_year_for_all_years>0.0</use_values_from_last_rosim_year_for_all_years>
   <calculate_mean_over_last_nyears_for_efficiency__energyin_and_energyout_>1.0</calculate_mean_over_last_nyears_for_efficiency__energyin_and_energyout_>
   <ates_minimum_flow_rate__speed_up_calculation_>0.0</ates_minimum_flow_rate__speed_up_calculation_>
   <ates_minimum_depth__speed_up_calculation_>0.0</ates_minimum_depth__speed_up_calculation_>
   <ates_maximum_depth__speed_up_calculation_>500.0</ates_maximum_depth__speed_up_calculation_>
   <ates_filter_fraction__of_aquifer_thickness_>0.8</ates_filter_fraction__of_aquifer_thickness_>
   <ates_clogging_velocity>0.3</ates_clogging_velocity>
   <ates_membrane_filter_index>1.0</ates_membrane_filter_index>
   <ates_depth_multiplication_factor>0.01</ates_depth_multiplication_factor>
   <input__power_utc__scenario_name__must_exist_>BaseCase</input__power_utc__scenario_name__must_exist_>
   <aquifers_for_potential_calculation>NMVFS, NMVFV, NMRFT, NMRFV, NLFFS, NLFFD, NLLFR, NLLFS, KNGLG_KNGLS, KNNSG, KNNSL, KNNSY, KNNSB, KNNSR, KNNSF_KNNSP, SLDNA, SLDND, RNROF, RNSOB, RBMH, RBMDU, RBMDL, RBMVU, RBMVL, RBSHN, ROSL_ROSLU, ROSLL, DCH, DCD, N_STACKED, KN_STACKED, SLDN_STACKED, TR_STACKED, RO_STACKED, DC_STACKED</aquifers_for_potential_calculation>
   <probability_maps__basename__treshold_value_>npv:0, utc:5, power:1, cop:10, kh:5, flowr:100, h:20</probability_maps__basename__treshold_value_>
@@ -165,10 +161,11 @@
   <rock_heat_capacity_hip>1000.0</rock_heat_capacity_hip>
   <exclude_hc_areas_for_recov_heat>1.0</exclude_hc_areas_for_recov_heat>
   <min_prod_temperature__potential__recoverable_heat>0.0</min_prod_temperature__potential__recoverable_heat>
   <generate_resources_spreadsheets>0.0</generate_resources_spreadsheets>
   <aquifer_identifier_for_overview_resources__empty__all_>stacked</aquifer_identifier_for_overview_resources__empty__all_>
   <well_distance__0__from__calc_power_utc_>0.0</well_distance__0__from__calc_power_utc_>
   <doublet_geographical_sorting_factor>1.0</doublet_geographical_sorting_factor>
   <annual_load_hours>6000.0</annual_load_hours>
   <annual_resource_load_hours>6000.0</annual_resource_load_hours>
   <doublet_lifetime>30.0</doublet_lifetime>
   <delete_resources_within_shapefile_s___per_aquifer_>0.0</delete_resources_within_shapefile_s___per_aquifer_>
   <delete_shapefiles__in_input_grids_directory_>
@@ -203,7 +200,7 @@
   <additional_p_values_for_the_overview_power_probability_map_and_recoverable_heat_maps>90, 70</additional_p_values_for_the_overview_power_probability_map_and_recoverable_heat_maps>
   <power_value_for_overview_power_probability_map>10.0</power_value_for_overview_power_probability_map>
   <input_scenario_name>BaseCase</input_scenario_name>
   <aquifers_to_use_in_overview_calculation>RO_STACKED</aquifers_to_use_in_overview_calculation>
   <aquifers_to_use_in_voi_calculation>RO_STACKED</aquifers_to_use_in_voi_calculation>
   <minimum_npv_for_portfolio_repeat>1.0</minimum_npv_for_portfolio_repeat>
   <upward_and_downward_change_of_pos>15.0</upward_and_downward_change_of_pos>
   <general_upward_change_of_pos>5.0</general_upward_change_of_pos>
+35 −38

File changed and moved.

Preview size limit exceeded, changes collapsed.

Loading