Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .github/RELEASE_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ conda install -c conda-forge neqsim=<version>
Requirements:

- Python 3.9 or newer.
- Java 11 or newer for pip installs.
- Java 17 or newer for pip installs. Download from [Adoptium](https://adoptium.net/).
- Conda installs include OpenJDK through conda-forge.

Verify the installed package version without starting the JVM:
Expand Down Expand Up @@ -62,6 +62,6 @@ printFrame(natural_gas)
## Maintainer Checklist

- Confirm `pyproject.toml` and `conda/meta.yaml` use this version.
- Confirm bundled JAR files match this version for each supported Java runtime.
- Confirm the bundled JAR at `src/neqsim/lib/neqsim-<version>.jar` matches this version.
- Run the relevant tests or packaging checks before publishing.
- Publish the GitHub release only when ready to trigger the PyPI release workflow.
28 changes: 27 additions & 1 deletion .github/workflows/runtests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,37 @@
- master

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Install uv
run: pipx install uv

- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.13"

- name: Install dependencies
run: uv sync

- name: Check formatting with black
run: uv run black --check --include '\.py$' src/neqsim tests

- name: Type check with mypy (advisory, non-blocking)
continue-on-error: true
run: uv run mypy src/neqsim

build:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
strategy:
fail-fast: false
matrix:
version: [3.9, 3.12, 3.13]
version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
runs-on: ubuntu-latest
# 3.14 is very new; don't fail the whole matrix while the ecosystem catches up.
continue-on-error: ${{ matrix.version == '3.14' }}
steps:
- uses: actions/checkout@v7

Expand Down
13 changes: 12 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,21 @@ Please note we have a code of conduct, please follow it in all your interactions
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you.

## Development Checks

Before opening a PR, run the same checks CI runs:

```bash
uv sync
uv run black --check --include '\.py$' src/neqsim tests # required — CI fails on violations
uv run mypy src/neqsim # advisory — CI reports but does not block
uv run pytest -p no:faulthandler # full test suite
```

## Release Process

1. Update the release version in `pyproject.toml` and `conda/meta.yaml`.
2. Update the bundled NeqSim JAR files under `src/neqsim/lib/` for each supported Java runtime.
2. Update the bundled NeqSim JAR file at `src/neqsim/lib/neqsim-<version>.jar` (Java 17+ only).
3. Draft the GitHub release body from `.github/RELEASE_TEMPLATE.md`, keeping the installation and quick-start sections in the release notes.
4. Use GitHub's generated release notes to include categorized pull requests. The categories are configured in `.github/release.yml`.
5. Publish the GitHub release only when ready. A published release triggers the PyPI publishing workflow and the Java stub generation workflow.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ It provides Python toolboxes such as [thermoTools](https://github.com/equinor/ne
### Install

<table>
<tr><td><strong>pip</strong> (requires Java 11+)</td><td><strong>conda</strong> (Java included)</td></tr>
<tr><td><strong>pip</strong> (requires Java 17+)</td><td><strong>conda</strong> (Java included)</td></tr>
<tr>
<td>

Expand All @@ -65,7 +65,7 @@ conda install -c conda-forge neqsim
</tr>
</table>

> **Prerequisites:** Python 3.9+ and Java 11+. The conda package automatically installs OpenJDK — no separate Java setup needed. For pip, install Java from [Adoptium](https://adoptium.net/).
> **Prerequisites:** Python 3.9+ and Java 17+ (NeqSim 3.15+ requires Java 17 or higher; earlier NeqSim releases required Java 11+). The conda package automatically installs OpenJDK — no separate Java setup needed. For pip, install Java from [Adoptium](https://adoptium.net/).

### Try it now

Expand Down Expand Up @@ -246,6 +246,29 @@ Explore ready-to-run examples in the [examples folder](https://github.com/equino

The full list of Python dependencies is on the [dependencies page](https://github.com/equinor/neqsim-python/network/dependencies).

### JVM Startup Control

By default, `import neqsim` starts the JVM immediately. This can be tuned via environment variables:

| Variable | Default | Purpose |
|----------|---------|---------|
| `NEQSIM_JVM_AUTOSTART` | `1` | Set to `0`/`false`/`no` to disable automatic JVM startup on import. Call `init_jvm()` explicitly before using `jneqsim`. |
| `NEQSIM_JVM_ARGS` | *(none)* | Extra JVM startup arguments (space separated), appended after the default `-Xrs`. |
| `NEQSIM_JVM_MAX_HEAP` | *(none)* | Max JVM heap size, e.g. `2g` — passed as `-Xmx2g`. |

```python
import os
os.environ["NEQSIM_JVM_AUTOSTART"] = "0" # must be set before `import neqsim`

from neqsim.neqsimpython import init_jvm, is_jvm_started

print(is_jvm_started()) # False
init_jvm(jvm_args=["-Xrs"]) # start explicitly, e.g. with custom args
print(is_jvm_started()) # True
```

`init_jvm()` is safe to call multiple times — it is a no-op if the JVM is already running.

---

## 🏗️ Contributing
Expand Down
2 changes: 1 addition & 1 deletion conda/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ requirements:
- jpype1 >=1.7.0,<2.0.0
- numpy >1.25.2
- pandas >=2.0.3,<3.0.0
- openjdk >=11
- openjdk >=17

test:
imports:
Expand Down
5 changes: 4 additions & 1 deletion docs/release-notes/v3.15.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ NeqSim Python is the Python interface to the NeqSim engine for thermodynamic cal
- Aligns Python package metadata and the conda recipe with version 3.15.0.
- Improves editor code completion by shipping typing stubs for `jneqsim` through the `neqsim` package export.
- Ensures `jneqsim` and `jpype` stub packages are included in built source distributions.
- Adds JVM startup control: set `NEQSIM_JVM_AUTOSTART=0` to defer JVM startup and call the new `init_jvm()` explicitly, with `NEQSIM_JVM_ARGS`/`NEQSIM_JVM_MAX_HEAP` to customize startup arguments and heap size. See the [JVM Startup Control](https://github.com/equinor/neqsim-python#jvm-startup-control) section of the README.
- Removes the unused bundled Java 8 JAR (~52 MB) and flattens the remaining JAR to `src/neqsim/lib/neqsim-3.15.0.jar` (no more `java11/` subfolder), reducing the installed package size.
- **Raises the minimum Java version from 11+ to 17+.** Starting with NeqSim 3.15, importing `neqsim` with an older JVM raises a clear `NeqSimJVMError` telling you to upgrade. Download Java 17+ from [Adoptium](https://adoptium.net/).

## Install

Expand All @@ -25,7 +28,7 @@ conda install -c conda-forge neqsim=3.15.0
Requirements:

- Python 3.9 or newer.
- Java 11 or newer for pip installs.
- Java 17 or newer for pip installs (raised from Java 11+ in earlier NeqSim releases). Download from [Adoptium](https://adoptium.net/).
- Conda installs include OpenJDK through conda-forge.

Verify the installed package version without starting the JVM:
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ channels:
- defaults
dependencies:
- python>=3.9
- openjdk>=11
- openjdk>=17
- jpype1>=1.7.0,<2.0.0
- numpy>1.25.2
- pandas>=2.0.3,<3.0.0
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dev = [
"pytest>=8.0.0,<9",
"pre-commit>=3.6.0,<4",
"stubgenj>=0.2.12,<0.3",
"mypy>=1.10,<3",
]

[tool.uv]
Expand Down
3 changes: 1 addition & 2 deletions src/jneqsim-stubs/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import sys

if sys.version_info >= (3, 8):
from typing import Protocol
else:
Expand All @@ -23,7 +23,6 @@ import jneqsim.thermodynamicoperations
import jneqsim.util
import typing


class __module_protocol__(Protocol):
# A module protocol which reflects the result of ``jp.JPackage("neqsim")``.

Expand Down
3 changes: 1 addition & 2 deletions src/jneqsim-stubs/api/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import sys

if sys.version_info >= (3, 8):
from typing import Protocol
else:
Expand All @@ -8,7 +8,6 @@ else:
import jneqsim.api.ioc
import typing


class __module_protocol__(Protocol):
# A module protocol which reflects the result of ``jp.JPackage("jneqsim.api")``.

Expand Down
13 changes: 8 additions & 5 deletions src/jneqsim-stubs/api/ioc/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import sys

if sys.version_info >= (3, 8):
from typing import Protocol
else:
Expand All @@ -9,16 +9,19 @@ import java.lang
import jpype
import typing



class CalculationResult:
fluidProperties: typing.MutableSequence[typing.MutableSequence[float]] = ...
calculationError: typing.MutableSequence[java.lang.String] = ...
def __init__(self, doubleArray: typing.Union[typing.List[typing.MutableSequence[float]], jpype.JArray], stringArray: typing.Union[typing.List[java.lang.String], jpype.JArray]): ...
def __init__(
self,
doubleArray: typing.Union[
typing.List[typing.MutableSequence[float]], jpype.JArray
],
stringArray: typing.Union[typing.List[java.lang.String], jpype.JArray],
): ...
def equals(self, object: typing.Any) -> bool: ...
def hashCode(self) -> int: ...


class __module_protocol__(Protocol):
# A module protocol which reflects the result of ``jp.JPackage("jneqsim.api.ioc")``.

Expand Down
63 changes: 50 additions & 13 deletions src/jneqsim-stubs/blackoil/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import sys

if sys.version_info >= (3, 8):
from typing import Protocol
else:
Expand All @@ -12,24 +12,42 @@ import jneqsim.blackoil.io
import jneqsim.thermo.system
import typing



class BlackOilConverter:
def __init__(self): ...
@staticmethod
def convert(systemInterface: jneqsim.thermo.system.SystemInterface, double: float, doubleArray: typing.Union[typing.List[float], jpype.JArray], double3: float, double4: float) -> 'BlackOilConverter.Result': ...
def convert(
systemInterface: jneqsim.thermo.system.SystemInterface,
double: float,
doubleArray: typing.Union[typing.List[float], jpype.JArray],
double3: float,
double4: float,
) -> "BlackOilConverter.Result": ...

class Result:
pvt: 'BlackOilPVTTable' = ...
blackOilSystem: 'SystemBlackOil' = ...
pvt: "BlackOilPVTTable" = ...
blackOilSystem: "SystemBlackOil" = ...
rho_o_sc: float = ...
rho_g_sc: float = ...
rho_w_sc: float = ...
bubblePoint: float = ...
def __init__(self): ...

class BlackOilFlash(java.io.Serializable):
def __init__(self, blackOilPVTTable: 'BlackOilPVTTable', double: float, double2: float, double3: float): ...
def flash(self, double: float, double2: float, double3: float, double4: float, double5: float) -> 'BlackOilFlashResult': ...
def __init__(
self,
blackOilPVTTable: "BlackOilPVTTable",
double: float,
double2: float,
double3: float,
): ...
def flash(
self,
double: float,
double2: float,
double3: float,
double4: float,
double5: float,
) -> "BlackOilFlashResult": ...

class BlackOilFlashResult(java.io.Serializable):
O_std: float = ...
Expand All @@ -52,7 +70,9 @@ class BlackOilFlashResult(java.io.Serializable):
def __init__(self): ...

class BlackOilPVTTable(java.io.Serializable):
def __init__(self, list: java.util.List['BlackOilPVTTable.Record'], double: float): ...
def __init__(
self, list: java.util.List["BlackOilPVTTable.Record"], double: float
): ...
def Bg(self, double: float) -> float: ...
def Bo(self, double: float) -> float: ...
def Bw(self, double: float) -> float: ...
Expand All @@ -63,6 +83,7 @@ class BlackOilPVTTable(java.io.Serializable):
def mu_g(self, double: float) -> float: ...
def mu_o(self, double: float) -> float: ...
def mu_w(self, double: float) -> float: ...

class Record(java.io.Serializable):
p: float = ...
Rs: float = ...
Expand All @@ -73,11 +94,28 @@ class BlackOilPVTTable(java.io.Serializable):
Rv: float = ...
Bw: float = ...
mu_w: float = ...
def __init__(self, double: float, double2: float, double3: float, double4: float, double5: float, double6: float, double7: float, double8: float, double9: float): ...
def __init__(
self,
double: float,
double2: float,
double3: float,
double4: float,
double5: float,
double6: float,
double7: float,
double8: float,
double9: float,
): ...

class SystemBlackOil(java.io.Serializable):
def __init__(self, blackOilPVTTable: BlackOilPVTTable, double: float, double2: float, double3: float): ...
def copyShallow(self) -> 'SystemBlackOil': ...
def __init__(
self,
blackOilPVTTable: BlackOilPVTTable,
double: float,
double2: float,
double3: float,
): ...
def copyShallow(self) -> "SystemBlackOil": ...
def flash(self) -> BlackOilFlashResult: ...
def getBg(self) -> float: ...
def getBo(self) -> float: ...
Expand All @@ -102,7 +140,6 @@ class SystemBlackOil(java.io.Serializable):
def setStdTotals(self, double: float, double2: float, double3: float) -> None: ...
def setTemperature(self, double: float) -> None: ...


class __module_protocol__(Protocol):
# A module protocol which reflects the result of ``jp.JPackage("jneqsim.blackoil")``.

Expand Down
Loading
Loading