SPICE metakernel#

A metakernel is a configuration text-kernel with a required KERNELS_TO_LOAD key containing a list of kernel files to load in the SPICE pool. Most of the time, the exact location the kernels are abbreviated with a PATH_SYMBOLS and its PATH_VALUES keys. The values are usually hard coded in the file to a relative or an absolute path.

  PATH_VALUES       = ( '.' )

  PATH_SYMBOLS      = ( 'KERNELS' )

  KERNELS_TO_LOAD   = (

    '$KERNELS/lsk/naif0012.tls'
    '$KERNELS/pck/pck00010.tpc'
    ...

  )

Read the metakernel#

The MetaKernel object allows the user to parse these file and if needed can change the PATH_VALUES at runtime:

Hint

You can provide any path:

MetaKernel(
  'metakernel.tm',
  kernels='~/kernels/'
)
from planetary_coverage import MetaKernel

mk = MetaKernel('metakernel.tm', kernels='kernels')

mk

📚 Dummy meta kernel

The kernels listed below can be obtained from NAIF generic kernels:

https://naif.jpl.nasa.gov/pub/naif/generic_kernels/


Location:
metakernel.tm
$KERNELS
kernels

KernelsTypeSize
$KERNELS/lsk/naif0012.tls⏱️ LSK5 kB
$KERNELS/pck/pck00010.tpc🪐 PCK123 kB

In the example above, we have replace the initial value of $KERNELS (equal to '.') by a custom value which corresponds to the actual location of our kernels (ie. the current directly 'kernels/' in our case).

mk.kernels
['kernels/lsk/naif0012.tls', 'kernels/pck/pck00010.tpc']

If, at least one of the kernels listed is not available locally, the tool will throw a MissingKernel error:

MetaKernel('metakernel.tm', kernels='/invalid/path')
---------------------------------------------------------------------------
MissingKernel                             Traceback (most recent call last)
Cell In[4], line 1
----> 1 MetaKernel('metakernel.tm', kernels='/invalid/path')

File ~/checkouts/readthedocs.org/user_builds/planetary-coverage/checkouts/latest/src/planetary_coverage/spice/metakernel.py:80, in MetaKernel.__init__(self, mk, download, remote, load_kernels, n_threads, **kwargs)
     77 self.n_threads = n_threads
     79 if kwargs:
---> 80     self.update_path_values(**kwargs, download=download)
     81 else:
     82     self.check(download=download)

File ~/checkouts/readthedocs.org/user_builds/planetary-coverage/checkouts/latest/src/planetary_coverage/spice/metakernel.py:215, in MetaKernel.update_path_values(self, download, **kwargs)
    212     index = symbols.index(symbol)
    213     self.data['PATH_VALUES'][index] = str(value).replace('~', HOME)
--> 215 self.check(download=download)

File ~/checkouts/readthedocs.org/user_builds/planetary-coverage/checkouts/latest/src/planetary_coverage/spice/metakernel.py:301, in MetaKernel.check(self, download)
    299 if not Path(kernel).exists():
    300     if not download:
--> 301         raise MissingKernel(kernel, remote=self.remote,
    302                             symbols=self.data.get('PATH_SYMBOLS'))
    304     if url is None:
    305         raise MissingKernelsRemote(kernel)

MissingKernel: `/invalid/path/lsk/naif0012.tls` was not found locally. You can add `download=True` to download it automatically from `https://naif.jpl.nasa.gov/pub/naif/generic_kernels/` or/and you can change the kernel path value(s) by adding `kernels='path/to/my/kernels'`.

Note

To speed us the kernel retrieval multiple download threads are started at the same time. By default it is equal to the number of CPU seen by the multiprocessing library but you can provide a custom value by yourself:

MetaKernel(
  'metakernel.tm',
  kernels='/data/kernels/',
  download=True,
  n_threads=2,
)

Warning

On Windows, due to a RuntimeError (see #80), multi-thread download is disabled (n_thread=1) but you can still increase the number of thread if you use it, with if __name__ == "__main__": syntax or in a Jupyter notebook.

Tip

If a remote location (https/http/ftp) is present in the content of the metakernel (in the text or in the data fields) you can add a download=True argument to download the missing kernels:

MetaKernel(
  'metakernel.tm',
  kernels='/data/kernels/',
  download=True,
)
[Download] https://naif.jpl.nasa.gov/pub/naif/generic_kernels/lsk/naif0012.tls
[Download] https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/pck00010.tpc

You can also manually set a custom remote location (as a string) to pull your kernels from there:

MetaKernel(
  'metakernel.tm',
  kernels='/data/kernels/',
  remote='https://remote.location.tld/',
  download=True,
)
[Download] https://remote.location.tld/lsk/naif0012.tls
[Download] https://remote.location.tld/pck/pck00010.tpc

These new kernels will stored at their expected location:

  • /data/kernels/lsk/naif0012.tls

  • /data/kernels/pck/pck00010.tpc

If multiple remote urls are present in the content (for example https and ftp), by default, the tool will use the first listed (here https). If you need to use an other one, you need to provide its index (as a integer) in the remote argument:

MetaKernel(
  'metakernel.tm',
  kernels='/data/kernels/',
  remote=1,
  download=True,
)
[Download] ftp://remote.location.tld/naif0012.tls
[Download] ftp://remote.location.tld/pck00010.tpc

To get a summary of the kernels listed in KERNELS_TO_LOAD, you can do:

mk.summary
TypesCountSize
⏱️ LSK15 kB
🪐 PCK1123 kB
Total2128 kB

Load the kernels#

Similarly to TourConfig and 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:

MetaKernel('metakernel.tm', kernels='kernels', load_kernels=True)
# or
mk.load_kernels()

The MetaKernel object can also be used with 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).

import spiceypy

spiceypy.kclear()    # Clear the kernel pool
spiceypy.furnsh(mk)  # Load the MetaKernel object

Now we can inspect the pool to see its content:

Tip

The same check can be performed even quicker with the SpicePool object (see SPICE toolbox for details).

nb_kernels_loaded = spiceypy.ktotal('ALL')
nb_kernels_loaded
3
from planetary_coverage import SpicePool

SpicePool
KernelsTypeSize
/tmp/metakernel-c0wo3cq7.tm📚 MK0 B
kernels/lsk/naif0012.tls⏱️ LSK5 kB
kernels/pck/pck00010.tpc🪐 PCK123 kB
for i in range(nb_kernels_loaded):
    print(spiceypy.kdata(i, 'ALL')[0])
/tmp/metakernel-c0wo3cq7.tm
kernels/lsk/naif0012.tls
kernels/pck/pck00010.tpc

Caution

The temporary file, /tmp/metakernel-xxxxxxxx.tm, is automatically removed after being loaded in the pool. If you need to keep this file open (for example to inspect it), you should use the python context manager (with with syntax):

from pathlib import Path

with mk as f:
    print(f)                 # New tmp mk file
    print(Path(f).exists())  # The tmp mk exists only here

print(Path(f).exists())      # The tmp mk no longer exists
/tmp/metakernel-qyz25z4a.tm
True
False

Important

Even if the name of the temporary metakernel file change each time you load it, its hash is always based on its kernels content and will remain constant, even if you load and purge the pool multiple times. Therefore, it is possible to perform a comparison between the SPICE pool content and the new content provided by the metakernel (to avoid to reload the pool if the list of the kernels to load is the same).

SpicePool.add(mk, purge=True)

SpicePool, hash(SpicePool)
(<SpicePool> 3 kernels loaded:
  - /tmp/metakernel-dlnum8hi.tm
  - kernels/lsk/naif0012.tls
  - kernels/pck/pck00010.tpc,
 8857868203528396108)
SpicePool.add(mk, purge=True)

SpicePool, hash(SpicePool)
(<SpicePool> 3 kernels loaded:
  - /tmp/metakernel-1p1aui6v.tm
  - kernels/lsk/naif0012.tls
  - kernels/pck/pck00010.tpc,
 8857868203528396108)