Skip to content
Snippets Groups Projects
Verified Commit a520b710 authored by Benoit Seignovert's avatar Benoit Seignovert
Browse files

Add load_kernels method to MetaKernel object (!41)

parent c1357480
No related branches found
No related tags found
No related merge requests found
Pipeline #644 passed
......@@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
[new]: https://juigitlab.esac.esa.int/datalab/moon-coverage/-/compare/0.12.0...main
### Added
- `TourConfig` and `Trajectory` object can now be used to load the SPICE pool directly with the `load_kernels()` method ([!41](https://juigitlab.esac.esa.int/datalab/moon-coverage/-/merge_requests/41)).
- `TourConfig`, `Trajectory` and `MetaKernel` objects can now be used to load the SPICE pool directly with the `load_kernels()` method ([!41](https://juigitlab.esac.esa.int/datalab/moon-coverage/-/merge_requests/41)).
[Release 0.12.0 - 2022/10/31][0.12.0]
......
......@@ -138,7 +138,15 @@ MetaKernel(
## Load the kernels
The `MetaKernel` object can be used with [`spiceypy.furnsh()`](https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.furnsh) to load its kernels list into the SPICE pool.
Similarly to [`TourConfig`](../trajectory/spacecraft/tour_config.md#spice-pool-content) and [`Trajectory`](../api/trajectory.md#trajectory) objects, you can load the kernels in a `MetaKernel` at initialization (with `load_kernels=True` attribute) or later on with the `.load_kernels()` function:
```{code-cell}
MetaKernel('metakernel.tm', kernels='kernels', load_kernels=True)
# or
mk.load_kernels()
```
The `MetaKernel` object can also be used with [`spiceypy.furnsh()`](https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.furnsh) to load its kernels list into the SPICE pool.
__However__, contrary to a regular metakernel, the tool will create a temporary copy of the metakernel on the fly with an edited set of `PATH_VALUES`. The original metakernel is kept untouched, the new file is stored in the system temporary folder. This allow the tool to redefined the location of the kernels at runtime (which was not possible with the default SPICE toolkit).
......
......@@ -193,10 +193,11 @@ tour.load_kernels()
```
This method could be very handy if you need to switch between different set of kernels.
It is also available on [`Trajectory`](../../api/trajectory.md#trajectory) objects as well:
It is also available on [`Trajectory`](../../api/trajectory.md#trajectory) and [`MetaKernel`](../../spice/mk.md) objects as well:
```python
traj.load_kernels()
mk.load_kernels()
```
:::
......
"""Abstract base class module."""
class ABCMetaKernel:
"""Abstract base class for MetaKernel objects.
See Also
--------
moon_coverage.spice.metakernel.MetaKernel
"""
......@@ -12,14 +12,16 @@ from multiprocessing import Pool
from pathlib import Path
from tempfile import NamedTemporaryFile
from .abc import ABCMetaKernel
from .kernel import format_data, kernel_parser
from .pool import SpicePool, log_spice_pool
from ..misc import wget
KERNEL_MAX_LENGTH = 255
class MetaKernel:
class MetaKernel(ABCMetaKernel):
"""Metakernel object.
Parameters
......@@ -35,6 +37,9 @@ class MetaKernel:
You can provide an integer to choose which one you want to use.
This value is not required if all the kernel are present locally (it is only
used to download the missing kernels).
load_kernels: bool, optional
Load the kernels listed in the metakernel into the SPICE pool.
If other kernels are present in the SPICE pool, they will be flushed.
**kwargs: dict, optional
Path key(s) and value(s) to be substituted in ``KERNELS_TO_LOAD``.
......@@ -53,7 +58,7 @@ class MetaKernel:
"""
_tmp_mk = None
def __init__(self, mk, download=False, remote=0, **kwargs):
def __init__(self, mk, download=False, remote=0, load_kernels=False, **kwargs):
self.__content, self.data = None, None
self.fname = mk
self.remote = remote
......@@ -63,6 +68,9 @@ class MetaKernel:
else:
self.check(download=download)
if load_kernels:
self.load_kernels()
def __str__(self):
return self.fname.name
......@@ -287,6 +295,20 @@ class MetaKernel:
with Pool() as p:
p.starmap(wget, set(missing)) # SET => avoid duplicates
def load_kernels(self):
"""Load the kernels listed in the metakernel into the SPICE pool.
Note
----
If the SPICE pool already contains these kernels, nothing will append.
If not, the pool is flushed and only the metakernels kernels are reloaded.
"""
if SpicePool != SpicePool.hash(self):
log_spice_pool.info(
'The content of the pool changed -> the metakernel will be reloaded.')
SpicePool.add(self, purge=True)
class MissingKernel(FileNotFoundError):
"""Missing kernel locally."""
......
......@@ -6,8 +6,8 @@ import numpy as np
import spiceypy as sp
from .abc import ABCMetaKernel as MetaKernel
from .kernel import get_item
from .metakernel import MetaKernel
from .references import SpiceRef
from .times import tdb, utc
from ..misc import depreciated, logger
......@@ -130,8 +130,14 @@ class MetaSpicePool(type):
if kernel not in cls:
raise ValueError(f'Kernel `{kernel}` is not in the pool.')
log_spice_pool.debug('Remove %s', kernel)
sp.unload(kernel)
if isinstance(kernel, MetaKernel):
mk_hash = hash(kernel)
for key, value in cls.MK_HASH.items():
if value == mk_hash and key in cls.kernels:
cls.remove(key)
else:
log_spice_pool.debug('Remove %s', kernel)
sp.unload(kernel)
def purge(cls):
"""Purge the pool from all its content."""
......
......@@ -7,6 +7,7 @@ from pytest import fixture, raises
import moon_coverage.spice.metakernel as _metakernel
from moon_coverage import MetaKernel
from moon_coverage.spice import SpicePool, debug_spice_pool
from moon_coverage.spice.metakernel import MissingKernel, MissingKernelsRemote
......@@ -329,3 +330,41 @@ def test_metakernel_duplicate_download(monkeypatch, mock_mk_content):
'https://example.org/baz.bc',
'https://example.org/foo.bc',
]
def test_metakernel_load_kernels(caplog):
"""Test MetaKernel kernels loader."""
debug_spice_pool(True)
SpicePool.purge()
caplog.clear()
mk = MetaKernel(KERNELS + '/metakernel.tm', kernels=KERNELS, load_kernels=True)
assert len(SpicePool) == 3
assert mk in SpicePool
assert KERNELS + '/naif0012.tls' in SpicePool
assert KERNELS + '/dummy.tf' in SpicePool
# Check metakernel hash
assert hash(mk) in SpicePool.MK_HASH.values()
# Check debugger message
reload_msg = [
'The content of the pool changed -> the metakernel will be reloaded.',
'Purge the pool',
'Add `metakernel.tm` in the SPICE pool',
'Cache metakernel original hash.',
]
assert caplog.messages == reload_msg
# Re-load the pool with the same metakernel (no reload)
caplog.clear()
mk.load_kernels()
assert not caplog.messages
# Remove metakernel
SpicePool.remove(mk)
assert len(SpicePool) == 0
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment