diff --git a/.gitignore b/.gitignore index 37905002381e4d47e31fcc450d81bf593ee71a2d..c5ddeb51313f4def3b96dbd3d844ab7b9b3293db 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ dist tests/resources/test_output +/src/pythermogis/jvm/JVM17 \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd6a776a1a21cad6296d92aa09fa406b59bdac51..2043acf05074acf4f7dbc4098aec849ffe114c63 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,9 +12,7 @@ Run tests: - export TZ=Europe/Amsterdam - ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - apt-get update - - apt-get install -y build-essential gcc tk openjdk-17-jdk - - export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 - - export PATH=$JAVA_HOME/bin:$PATH + - apt-get install -y build-essential gcc tk - pixi install - pixi run pytest -rx tests/ - rm -rf /var/lib/apt/lists/* diff --git a/README.md b/README.md index f771aa804542c809f0fa6c384a4245879099015d..a2823067c653fa143a0dd1ab797dc289c0c5b0eb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Read the full documenation [here](https://pythermogis-15909e.ci.tno.nl/). -If you wish to build the documentation locally, follow the installation steps and run: +If you wish to build the documentation locally, follow the installation from source step and run: ```bash pixi run mkdocs serve @@ -15,33 +15,18 @@ pixi run mkdocs serve # Installation pythermogis has been designed to be used as a python package you import into your own python projects. -It works by creating a python API access to the ThermoGIS techno-economic application, which is written in Java. -Because of this dependency you need to Install a Java 17 VM. +It works by creating a python API access to the Core ThermoGIS techno-economic application, which is written in Java. -### 1. Install Java 17 +The ThermoGIS techno-economic Core application is packaged within pythermogis, this is the reason the package is quite large (~150mb). +Additionally the first time you run a simulation with pythermogis, a Java 17 Virtual machine (JVM) will be automatically installed (using the package [install-jdk](https://pypi.org/project/install-jdk/)). -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)). +The Java 17 virtual machine is an additional ~350mb so there should be at least 500mb of space on any machine you install pythermogis on (and preferably more). -*(You can find the JAR in this repository's resources folder: /resources/thermogis_jar)* +Installation of the JVM can take some time (0.5 - 2 minutes) depending on the speed of your internet connection, but will only be done once per pythermogis installation. -### 2. Ensure JAVA_HOME is set +pythermogis has been tested on Windows and Linux operating systems. -When installing the Java 17 Virtual Machine (JVM), a global environment variable _should_ have been set: `JAVA_HOME`. This stores the path to the JVM that was installed in step 1 and enables pythermogis to read the core Java code. - -If, for some reason, this was not done during installation you can either set it yourself or create a ``.env`` file in your project root folder. - -This `.env` file should contain the following variable: - -- `JAVA_HOME`: Path to the Java 17 installation - *(e.g., on Windows: `C:\Program Files\Amazon Corretto\jdk17.0.0_0`)* - - -Example `.env` contents (The specific path could be different depending on your OS, or file architecture): -``` -JAVA_HOME = C:\Program Files \Amazon Corretto\jdk17.0.0_0 -``` - -### 3. Import pythermogis into your own python projects +### 1. Import pythermogis into your own python projects You can use pip to install the package directly from the gitlab project: ``` @@ -58,7 +43,7 @@ If you are using uv as your dependency manager: uv add pythermogis --default-index https://ci.tno.nl/gitlab/api/v4/projects/18271/packages/pypi/simple ``` -### Installation from source +### 2. Installation from source Clone and install from source with Pixi: @@ -67,7 +52,6 @@ git clone git@ci.tno.nl:ags_public/pythermogis.git cd pythermogis pixi install ``` -(Make sure you have followed step 1 and 2: installing a Java 17 VM) --- diff --git a/docs/changelog.md b/docs/changelog.md index 56cfc0846e9dc349135a6a6b0646f00b9bda8ec5..6bd4abb8f20b749ebdffb7ee00f50657b6ae66ed 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,8 @@ # Change log +## v1.2.5 (26-1-2026) +A java 17 virtual machine is now automatically installed the first time a pythermogis simulation is run. This uses the [install-jdk](https://pypi.org/project/install-jdk/) python project. + ## v1.2.4 (23-1-2026) The ThermoGIS Jar is now packaged within pythermogis, this reduces the number of installation steps from users (no longer having to set a THERMOGIS_JAR environment variable) and means that multiple installations of pythermogis on the same system won't break each-other (previously two or more pythermogis installations would use the same Jar, which could cause version conflicts). diff --git a/docs/install/install.md b/docs/install/install.md index 128cefcb41caf091136ef1545eaec6c9b638f1bb..8c293fc4c73225c5cd1d4222407d6754c4dc3033 100644 --- a/docs/install/install.md +++ b/docs/install/install.md @@ -1,33 +1,31 @@ # Installation +**pythermogis** is a Python package that provides API access to the [ThermoGIS](https://www.thermogis.nl/en) doublet simulations and economic calculations, ThermoGIS is developed and maintained by the [Geological Survey of the Netherlands](https://www.geologischedienst.nl/en/) which is part of [TNO](https://www.tno.nl/en/), additionally, pythermogis was also partially developed by and for the [GoForward](https://go-forward-project.eu/) project. -pythermogis has been designed to be used as a python package you import into your own python projects. -It works by creating a python API access to the ThermoGIS techno-economic application, which is written in Java. -Because of this dependency you need to Install a Java 17 VM. - -### 1. Install Java 17 +Read the full documenation [here](https://pythermogis-15909e.ci.tno.nl/). -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)). +If you wish to build the documentation locally, follow the installation from source step and run: -*(You can find the JAR in this repository's resources folder: /resources/thermogis_jar)* +```bash +pixi run mkdocs serve +``` -### 2. Ensure JAVA_HOME is set +--- -When installing the Java 17 Virtual Machine (JVM), a global environment variable _should_ have been set: `JAVA_HOME`. This stores the path to the JVM that was installed in step 1 and enables pythermogis to read the core Java code. +# Installation -If, for some reason, this was not done during installation you can either set it yourself or create a ``.env`` file in your project root folder. +pythermogis has been designed to be used as a python package you import into your own python projects. +It works by creating a python API access to the Core ThermoGIS techno-economic application, which is written in Java. -This `.env` file should contain the following variable: +The ThermoGIS techno-economic Core application is packaged within pythermogis, this is the reason the package is quite large (~150mb). +Additionally the first time you run a simulation with pythermogis, a Java 17 Virtual machine (JVM) will be automatically installed (using the package [install-jdk](https://pypi.org/project/install-jdk/)). -- `JAVA_HOME`: Path to the Java 17 installation - *(e.g., on Windows: `C:\Program Files\Amazon Corretto\jdk17.0.0_0`)* +The Java 17 virtual machine is an additional ~350mb so there should be at least 500mb of space on any machine you install pythermogis on (and preferably more). +Installation of the JVM can take some time (0.5 - 2 minutes) depending on the speed of your internet connection, but will only be done once per pythermogis installation. -Example `.env` contents (The specific path could be different depending on your OS, or file architecture): -``` -JAVA_HOME = C:\Program Files \Amazon Corretto\jdk17.0.0_0 -``` +pythermogis has been tested on Windows and Linux operating systems. -### 3. Import pythermogis into your own python projects +### 1. Import pythermogis into your own python projects You can use pip to install the package directly from the gitlab project: ``` @@ -44,7 +42,7 @@ If you are using uv as your dependency manager: uv add pythermogis --default-index https://ci.tno.nl/gitlab/api/v4/projects/18271/packages/pypi/simple ``` -### Installation from source +### 2. Installation from source Clone and install from source with Pixi: @@ -53,6 +51,5 @@ git clone git@ci.tno.nl:ags_public/pythermogis.git cd pythermogis pixi install ``` -(Make sure you have followed step 1 and 2: installing a Java 17 VM) --- diff --git a/pixi.lock b/pixi.lock index 61f5e03e1937f6ac8801da8c3ff9c4d1509bbdfb..d771c6c405d80e84f00fc3cfb4a4cf19128046f1 100644 --- a/pixi.lock +++ b/pixi.lock @@ -74,6 +74,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/install-jdk-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.1.0-pyhd8ed1ab_0.conda @@ -321,6 +322,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/install-jdk-1.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda @@ -1989,6 +1991,17 @@ packages: - pkg:pypi/iniconfig?source=hash-mapping size: 11474 timestamp: 1733223232820 +- conda: https://conda.anaconda.org/conda-forge/noarch/install-jdk-1.1.0-pyhd8ed1ab_1.conda + sha256: 3bfea87bc5ab5c310c64f4c03249f2eea98b20764f5160be0089f02c9144de97 + md5: 247673a294ba3bd0efd5c201db70f908 + depends: + - python >=3.9,<4.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/install-jdk?source=hash-mapping + size: 19322 + timestamp: 1735255158126 - conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda sha256: 0fd2b0b84c854029041b0ede8f4c2369242ee92acc0092f8407b1fe9238a8209 md5: 2d89243bfb53652c182a7c73182cce4f @@ -4921,7 +4934,7 @@ packages: - pypi: ./ name: pythermogis version: 1.2.4 - sha256: 41970125f848a057eda9f00cac0c2046df6c0b149f67081432922484a4320b9d + sha256: 4406d3c4da3a9e6ee46a39b7ce7b61063e676db495638f1f9d992ac174589913 requires_dist: - jpype1>=1.5.2,<2 - xarray==2024.9.0.* @@ -4931,6 +4944,7 @@ packages: - pygridsio>=1.0,<2.0 - python-dotenv>=1.2.1,<2 - dask>=2025.5.1,<2026 + - install-jdk==1.1.0 requires_python: '>=3.11' editable: true - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.3-hf636f53_101_cp313.conda diff --git a/pyproject.toml b/pyproject.toml index 4054d06a1d0c7f8f5028c9e81d382c27c34498ed..90e15525c5614fcb18552074fbe0b39794afd44d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pythermogis" -version = "1.2.4" +version = "1.2.5" description = "This repository is used as a python API for the ThermoGIS Doublet simulations" authors = [ { name = "Hen Brett", email = "hen.brett@tno.nl" }, @@ -22,7 +22,8 @@ dependencies = [ "build>=1.2.2.post1,<2", "pygridsio>=1.0,<2.0", "python-dotenv>=1.2.1,<2", - "dask>=2025.5.1,<2026" + "dask>=2025.5.1,<2026", + "install-jdk==1.1.0" ] @@ -75,6 +76,7 @@ pre-commit = ">=4.3.0,<5" python-dotenv = ">=1.2.1,<2" numba = ">=0.63.1,<0.64" ruff = ">=0.14.10,<0.15" +install-jdk = "==1.1.0" [tool.ruff] line-length = 88 diff --git a/src/pythermogis/__init__.py b/src/pythermogis/__init__.py index 6d77ee87309129c3994843f48a796cb2e98a1ee5..54e857759cf30d0cf3470f1e082773dff8ce3d38 100644 --- a/src/pythermogis/__init__.py +++ b/src/pythermogis/__init__.py @@ -1,4 +1,4 @@ -from pythermogis.thermogis_classes.jvm_start import * +from pythermogis.jvm.jvm_start import * from pythermogis.thermogis_classes.doublet import * from pythermogis.thermogis_classes.utc_properties import * from pythermogis.transmissivity.calculate_thick_perm_trans import * diff --git a/src/pythermogis/constants.py b/src/pythermogis/constants.py index 1b0c72f2fea07cad131dd067793fb88d834c481c..34cb3e9a47b5d9c368e2ffc88e829ee70cbd5dac 100644 --- a/src/pythermogis/constants.py +++ b/src/pythermogis/constants.py @@ -1,5 +1,18 @@ from pathlib import Path +import platform THERMOGIS_JAR_PATH = ( Path(__file__).parent / "thermogis_jar" / "thermogis-1.7.0-shaded.jar" ) + +JVM17_PATH = Path(__file__).parent / "jvm" / "JVM17" + +os_name = platform.system() +if os_name == "Linux": + JVM17_DLL_PATH = JVM17_PATH / "jdk17" / "lib" / "server" / "libjvm.so" +elif os_name == "Darwin": # mac + JVM17_DLL_PATH = JVM17_PATH / "jdk17" / "lib" / "server" / "libjvm.dylib" +elif os_name == "Windows": + JVM17_DLL_PATH = JVM17_PATH / "jdk17" / "bin" / "server" / "jvm.dll" +else: + raise SyntaxError(f"Operating System not supported: {os_name}") diff --git a/src/pythermogis/jvm/__init__.py b/src/pythermogis/jvm/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/pythermogis/jvm/jvm_start.py b/src/pythermogis/jvm/jvm_start.py new file mode 100644 index 0000000000000000000000000000000000000000..64252140f4c84f73862e097b97b96b0e826796e0 --- /dev/null +++ b/src/pythermogis/jvm/jvm_start.py @@ -0,0 +1,71 @@ +import faulthandler + +import jdk +import jpype +import shutil + +from pythermogis.constants import JVM17_DLL_PATH, JVM17_PATH, THERMOGIS_JAR_PATH + + +def install_jvm(): + """ + Installs a Corretto build of Java 17 JDK, renames it from jdk17_XX_XX to jdk17 + and checks that the executable exists. + + :return: + """ + print( + "Installing Java Virtual Machine, this can take some time but will only be " + "run once." + ) + + # delete path first to ensure fresh installation + if JVM17_PATH.exists(): + shutil.rmtree(JVM17_PATH) + + # create path + JVM17_PATH.mkdir(parents=True, exist_ok=True) + + # Installs a Corretto build of Java 17 JDK + jdk.install("17", vendor="Corretto", path=JVM17_PATH) + + # search for the path of the installed JDK, it can be different depending on the + # version number + jdk_dirs = [p for p in JVM17_PATH.glob("*") if p.is_dir()][0] + + # rename to the standard path to be used throughout pythermogis + shutil.move(jdk_dirs, JVM17_PATH / "jdk17") + + if not JVM17_DLL_PATH.exists(): + raise FileExistsError( + f"Installation of JVM unsuccessful." + f"Java virtual machine executable not found at :{JVM17_DLL_PATH}" + ) + + +def start_jvm(): + """ + To use the JPype package a java .jar file has to be instantiated with a java virtual + machine (jvm). + This method ensures a clean startup of the jvm packaged with this repo + :return: + """ + if jpype.isJVMStarted(): + return + + # when running pytest with jpype a "Windows fatal exception" is thrown which in + # reality has no affect on the outcome or performance. + # the current workaround from the jpype package is to disable the pytest + # faulthandler. (This will not prevent python or java errors in the test). + faulthandler.disable() + + if not JVM17_DLL_PATH.exists(): + # If the JVM has not been installed (ie. JVM17_DLL_PATH does not exist, then + # install using the install-jdk package + install_jvm() + + if not THERMOGIS_JAR_PATH.exists(): + raise FileNotFoundError(f"Jar file not found at {THERMOGIS_JAR_PATH}.") + + # starts the java virtual machine and loads the thermogis classes + jpype.startJVM(str(JVM17_DLL_PATH), classpath=[str(THERMOGIS_JAR_PATH)]) diff --git a/src/pythermogis/thermogis_classes/jvm_start.py b/src/pythermogis/thermogis_classes/jvm_start.py deleted file mode 100644 index cabbdb7d3e1f89b77c5b48cb9eda1ba35416b851..0000000000000000000000000000000000000000 --- a/src/pythermogis/thermogis_classes/jvm_start.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import jpype -import faulthandler - -from dotenv import load_dotenv -from pythermogis.constants import THERMOGIS_JAR_PATH -load_dotenv() - -def get_thermogis_jar_path() -> str: - """ - From within thermogis, find the thermogis-jar, return the path to the ThermoGIS Jar - """ - if not THERMOGIS_JAR_PATH.exists(): - raise FileNotFoundError(f"Jar file not found at {THERMOGIS_JAR_PATH}.") - return str(THERMOGIS_JAR_PATH) - - -def start_jvm(): - """ - To use the JPype package a java .jar file has to be instantiated with a java virtual machine (jvm) - This method ensures a clean startup of the jvm packaged with this repo - :return: - """ - 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. - # the current workaround from the jpype package is to disable the pytest faulthandler. (This will not prevent python or java errors in the test). - faulthandler.disable() - - # automatically looks up the java version in JAVA_HOME - jpype.startJVM(classpath=[get_thermogis_jar_path()]) - -def close_jvm(): - """ - To use the JPype package a java .jar file has to be instantiated with a java virtual machine (jvm) - This method ensures a clean shutdown of the jvm - :return: - """ - if jpype.isJVMStarted(): - jpype.shutdownJVM() - -def print_java_path(): - """ - This method prints the path to the JVM being used - :return: - """ - print(os.getenv("JAVA_HOME")) - -def print_thermogis_jar_path(): - """ - This method prints the path to the Thermogis Jar being used - :return: - """ - print(get_thermogis_jar_path()) diff --git a/src/pythermogis/thermogis_classes/potential_properties.py b/src/pythermogis/thermogis_classes/potential_properties.py index db3afee3ae376b3117fa0b6131f1a3799c8513c5..5f514ddb98b85c2a1e8e9f7a06edacce99bbdea0 100644 --- a/src/pythermogis/thermogis_classes/potential_properties.py +++ b/src/pythermogis/thermogis_classes/potential_properties.py @@ -1,6 +1,6 @@ from jpype import JClass -from pythermogis.thermogis_classes.jvm_start import start_jvm +from pythermogis import start_jvm def instantiate_potential_properties_builder() -> JClass: diff --git a/src/pythermogis/thermogis_classes/utc_properties.py b/src/pythermogis/thermogis_classes/utc_properties.py index 9a641d2e05b9fede7d16294c19d6c339c00c88d5..50308d4fa67cdb97151f44bcbb67a24e6aed3930 100644 --- a/src/pythermogis/thermogis_classes/utc_properties.py +++ b/src/pythermogis/thermogis_classes/utc_properties.py @@ -1,9 +1,10 @@ -from pathlib import Path import typing as t import xml.etree.ElementTree as ET +from pathlib import Path + from jpype import JClass -from pythermogis.thermogis_classes.jvm_start import start_jvm +from pythermogis import start_jvm def instantiate_utc_properties_from_xml(xml_file: str | Path) -> JClass: diff --git a/tests/java/test_java_installation.py b/tests/java/test_java_installation.py index 1dea75f33c3a547cb8e7cb20d0bd0e50b76ff032..30e0b35d549810477058da4496908104311be3d9 100644 --- a/tests/java/test_java_installation.py +++ b/tests/java/test_java_installation.py @@ -1,9 +1,13 @@ -from pythermogis import print_java_path, print_thermogis_jar_path, start_jvm +from pythermogis import ( + start_jvm, + JVM17_DLL_PATH, + THERMOGIS_JAR_PATH, +) def test_JVM_start(): start_jvm() def test_JVM_JAR_print(): - print_java_path() - print_thermogis_jar_path() \ No newline at end of file + print(JVM17_DLL_PATH) + print(THERMOGIS_JAR_PATH) \ No newline at end of file