A space dynamics library. This package is a jpype wrapping of the original orekit java code.
Project description
orekit_jpype
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
- JDK installed on your system (as Orekit is based on Java)
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
, theorekit_jpype
package version should start with12.0.2
, for instance12.0.2.0
- Follow the Python version specifiers conventions
- The package version should follow the Orekit versions: for example if based on Orekit
- 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 themaster
branch and create a tag with the same name as the package version, for example12.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
Built Distribution
File details
Details for the file orekit_jpype-12.1.2.0.tar.gz
.
File metadata
- Download URL: orekit_jpype-12.1.2.0.tar.gz
- Upload date:
- Size: 36.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 309330ddf06189d379208e502af4e1b31fab5b3bfc0a5a4d068b0af2e855703a |
|
MD5 | 009cb0b6bb958d6026e20550df5011e1 |
|
BLAKE2b-256 | 785db3b1d0cfe6d690c85536585a9b328bae9619f4aff75679a7f02d129c7a62 |
File details
Details for the file orekit_jpype-12.1.2.0-py3-none-any.whl
.
File metadata
- Download URL: orekit_jpype-12.1.2.0-py3-none-any.whl
- Upload date:
- Size: 9.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 46c5fc21bd78a5215bfd21365361f1df5be8a67c24c86087c731b56bfe9b90ac |
|
MD5 | c688fdefc7372d8554dda4f1f644fbff |
|
BLAKE2b-256 | 057da226727ab752fb5d4763b7b2775539d6af2f18aaabb3cbee99f06265580d |