Skip to main content

A space dynamics library. This package is a jpype wrapping of the original orekit java code.

Project description

orekit_jpype

PyPI - Version PyPI - Python Version Pipeline passing coverage report

An alternative implementation of orekit python wrapper based on Jpype and stubs generated by stubgenj. The interface is largely similar to the original JCC wrapped orekit version which is a more capable wrapper as it can subclass java classes, which Jpype cannot. The Jpype version is however easy to install, use and for most purposes sufficient.

Orekit (https://www.orekit.org/) is a low level library for space flight dynamics and is written in Java. The java API is located at: https://www.orekit.org/site-orekit-latest/apidocs/index.html

The original Orekit python wrapper based on JCC is located at: https://gitlab.orekit.org/orekit-labs/python-wrapper/-/wikis/home

Please see the repository example notebooks and test cases for usage, as well as jpype documentation https://jpype.readthedocs.io/en/latest/index.html

Installation

  • Requirements:
    • JDK installed on your system (as Orekit is based on Java)
      • Or alternatively jdk4py can be used to provide JDK using pip, see usage instructions below
    • Python >= 3.7 . Orekit Jpype is only tested for Python versions >= 3.9 because the CI pipeline depends on jdk4py, but Python 3.7 and 3.8 should work.
    • pip
    • Recommended: a venv

To install from PyPi:

pip install orekit-jpype

To install from source:

git clone https://gitlab.orekit.org/orekit/orekit_jpype.git
pip install . -vv

Or alternatively:

pip install git+https://gitlab.orekit.org/orekit/orekit_jpype.git

To install the required orekit data from pip:

pip install git+https://gitlab.orekit.org/orekit/orekit-data.git

usage

See the example notebooks (examples folder) and the package test folder (only available in the source distribution or in the git repo) for examples.

Usage difference from JCC based wrapper

Casting

In the JCC version there is sometimes needed a .cast_() method to cast the object to the correct type. This is not needed in the JPype version.

Implementation of Orekit interfaces

In the JCC version, the interfaces are implemented as special classes named PythonClassName that is then subclassed in Python. In the JPype version, the interfaces are implemented as python classes and marked with decorators, @JImplements and @JOverride, see https://jpype.readthedocs.io/en/latest/userguide.html#proxy-method-overloading

Subclassing of Orekit abstract classes

In the JCC version of Orekit it is possible to subclass classes from the set of PythonClassName classes. This is not possible in the Jpype version of Orekit, which is limited to implementation of interfaces only. But instead it is possible to subclass abstract classes in Java, compile the JAR, and add the JAR to Orekit JPype, see section below.

Using custom JARs

You can add your own JARs to Orekit Jpype. For that, you can pass a list of classpaths via the argument additional_classpaths to the method orekit_jpype.initVM. An example can be found in the test case tests-jvm/orekit_jpype_test_init_vm_with_extra_jars.py.

Java imports require the JVM to be started

As jpype loads the Java classes from JAR files on-the-fly, it cannot know which Java packages/classes exist before the JVM is started. Therefore the Java packages/classes can only be imported after the JVM is started.

Hence, the following will not work:

from java.util import ArrayList
from org.orekit.data import DirectoryCrawler, DataContext
import orekit_jpype as orekit

if __name__ == '__main__':
    orekit.initVM()

Instead you should do the following:

import orekit_jpype as orekit
orekit.initVM()

from java.util import ArrayList
from org.orekit.data import DirectoryCrawler, DataContext

if __name__ == '__main__':
    # do stuff

The imports after the orekit.initVM() call will probably trigger PEP8 E402 warnings from flake8 or pylint. As a workaround, you can add the following comment after the import to ignore these warnings:

from java.util import ArrayList  # noqa: E402

The JVM cannot be restarted

If you call the jpype.shutdownJVM() method, you won't be able to restart the JVM afterwards by calling jpype.startJVM() or orekit_jpype.initVM(). This is a known limitation of the JNI used by jpype: https://github.com/jpype-project/jpype/issues/959

No JDK is provided

Installing Orekit JCC via conda also installed an OpenJDK8. Instead, when installing Orekit Jpype via pip, no JDK is installed, so it's up to the user to install JDK on their machine.

Another solution could be installing a JDK with pip thanks to the jdk4py project, and passing the path to the libjvm file to jpype by using the following syntax (taken from the test case tests-jvm/orekit_jpype_test_init_vm_jdk4py.py):

import orekit_jpype as orekit
import os
import jdk4py

libjvm_path = os.path.join(jdk4py.JAVA_HOME, "lib", "server", "libjvm.so")

orekit.initVM(jvmpath=libjvm_path)

Alternatively, you can just set the JAVA_HOME environment variable from jdk4py:

import orekit_jpype as orekit
import os
import jdk4py

os.environ["JAVA_HOME"] = str(jdk4py.JAVA_HOME)

orekit.initVM()

However, using jpype together with jdk4py seems to cause trouble in Windows: https://github.com/jpype-project/jpype/issues/1151

Packaging a project with pyinstaller

This repository contains hooks for pyinstaller so that your project relying on Orekit can be packaged into an executable. No additional arguments are needed to pyinstaller thanks to the hooks, as long as orekit_jpype is installed in your Python packages.

Packaging the orekit data folder or library

Using the orekitdata Python library

If you are using the orekit data repository as a Python library, it already contains hooks for pyinstaller so you don't have to pass any argument, the orekit data folder will be automatically collected:

pyinstaller <your main Python script>

This will create a folder dist/ containing your executable.

Locally managed orekit data folder

If the orekit-data folder is located in the same folder as your main Python script, you can for instance use the following syntax to load the orekit data in your Python code:

from orekit_jpype.pyhelpers import setup_orekit_data
dirpath = os.path.dirname(os.path.abspath(__file__))
setup_orekit_data(filenames=os.path.join(dirpath, "orekit-data"), from_pip_library=False)

Then you can use the following option to package your orekit-data folder in your executable.

pyinstaller --add-data orekit-data/*:./orekit-data/ <your main Python script>

This will create a folder dist/ containing your executable.

For developers: testing the hook

python -m PyInstaller.utils.run_tests --include_only orekit_jpype._pyinstaller

This will package a minimal executable containing orekit_jpype, orekitdata and run the test case test/OrekitDataLoadOnlyLibTest.py to test that everything works as expected. TODO: include this test procedure in a CI pipeline.

Notes on differences between the original JCC-based Orekit Python wrapper and this jpype-based wrapper

The biggest difference between JCC and Jpype is that JCC is a static wrapping, creating c++ classes that are pre-defined when building the package, while JPype is dynamic and investigating the java machine on-the-fly.

Each model has advantages and disadvantages.

JCC Pro's

  • Can (probably) implement anything that is doable in Java in Python using the special classes (subclassing, interfaces)
  • Stable when a compiled version has been created
  • Well tested with orekit
  • Good support for JCC but mainly one person who maintains and understand the code.
    • Easier to add custom java code to the system

JCC Con's

  • Special classes for subclassing.
  • Special classes for interfaces
  • Casting of types for classes with multiple inheritance
  • Can be tricky to get it to compile (but when it does it is stable)
  • Poor docstring support
  • Very hard to understand the inner working of the wrapping code
  • Need to recompile to add user java code pieces

JPype Pro's

  • Support for docstrings, reasonable rendering of javadoc
  • Automatic adaptors enabling things like automatic translation between python datetime and AbsoluteDate.
  • Well documented
  • Medium hard to understand the wrapping code
  • The wrapping is separate from the orekit part, maybe less issues with compiling (using conda)
  • Easy to add user java code pieces as separate jars
  • Direct mapping of numpy arrays to java arrays

JPype Cons

  • No subclassing of Java classes (Abstract classes in orekit cannot be used)
  • Issues with restarting the JVM
  • The unknown issues we don't know.
  • Seems to have a slight performance hit (~10% increase in time in some very basic tests)

Development

Creating a venv

Using a Python virtual environment is highly recommended, do not install packages in your global python environment. To do so (in this example the venv will be located in the root of this git folder, but no worries, it is git-ignored):

python3 -m pip install virtualenv
python3 -m venv .orekitvenv

Activate the venv using:

source .orekitvenv/bin/activate

Downloading JAR files and generating Python stubs

When a new version of one of the Java dependencies is available, the new JAR files must be downloaded and the stubs re-generated:

Downloading JARs requires having maven installed on your system. And to generate stub files, the stubgenj package is required, it should be installed via pip together with the other dependencies of orekit_jpype (https://gitlab.cern.ch/scripting-tools/stubgenj)

  • change the version of the corresponding artifact in pom.xml
  • The following Python script will download new JARs and re-generate the stubs:
python3 dl_jars_and_render_stubs.py

Checklist before uploading a new package version

  • Open a merge request from your private branch into the master branch, containing:
    • your changes, for instance newer versions of the Orekit JARs, changes to stubs, changes to test suites, etc.
    • bump the package version in pyproject.toml.
      • The package version should follow the Orekit versions: for example if based on Orekit 12.0.2, the orekit_jpype package version should start with 12.0.2, for instance 12.0.2.0
      • Follow the Python version specifiers conventions
  • The merge request should be merged only if the CI pipeline passes. In particular, major releases of Orekit introduce breaking changes so it's likely that unit tests will fail and need to be modified
  • After your branch is merged into the master branch, switch to the master branch and create a tag with the same name as the package version, for example 12.0.2.0
  • Stay in the master branch in the same tagged commit, and follow the instructions below to build and upload the wheel to PyPi

Building wheel

  • Make sure you have the latest version of PyPA’s build installed:
python3 -m pip install --upgrade build
  • To build the wheel:
python3 -m build

Uploading wheel

Make sure you have the latest version of twine:

python3 -m pip install --upgrade twine

Uploading wheel to test PyPi server

  • To upload to the PyPi test server:
python3 -m twine upload --repository testpypi dist/*
  • To try to install the newly uploaded wheel from the PyPi test server (in this example for version 12.0.2.0):
python3 -m pip install -i https://test.pypi.org/simple/ orekit-jpype==12.0.2.0

Uploading wheel to the real PyPi server

  • To upload the wheel to PyPi:
python3 -m twine upload dist/*
  • To install orekit_jpype from PyPi:
python3 -m pip install --upgrade orekit-jpype

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

orekit_jpype-12.1.3.0.tar.gz (36.9 MB view details)

Uploaded Source

Built Distribution

orekit_jpype-12.1.3.0-py3-none-any.whl (9.4 MB view details)

Uploaded Python 3

File details

Details for the file orekit_jpype-12.1.3.0.tar.gz.

File metadata

  • Download URL: orekit_jpype-12.1.3.0.tar.gz
  • Upload date:
  • Size: 36.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.10.12

File hashes

Hashes for orekit_jpype-12.1.3.0.tar.gz
Algorithm Hash digest
SHA256 725f04daf793112857c0890be600cdf6467c105170606b2b8b32fe7536926f73
MD5 f3354e47b32abdd1cfaebf71a757f19e
BLAKE2b-256 0ef44f48a3485f87980be2f1954a83ef29286967e73002e7e91aaa83b1f42c33

See more details on using hashes here.

File details

Details for the file orekit_jpype-12.1.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for orekit_jpype-12.1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ddc034964cd20cbb2ff432722a97012341880ed841509cd4eac11747a36bb515
MD5 e9f060289e3ab8e1b28dbf87e8c61a83
BLAKE2b-256 24b258a795e63531b2905dcee3cdf203c634c790998e91ab624d975442d73205

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page