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
Kernels | Type | Size |
---|---|---|
$KERNELS/lsk/naif0012.tls | ⏱️ LSK | 5 kB |
$KERNELS/pck/pck00010.tpc | 🪐 PCK | 123 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/stable/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/stable/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/stable/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
Types | Count | Size |
---|---|---|
⏱️ LSK | 1 | 5 kB |
🪐 PCK | 1 | 123 kB |
Total | 2 | 128 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
Kernels | Type | Size |
---|---|---|
/tmp/metakernel-wqf7uvvy.tm | 📚 MK | 0 B |
kernels/lsk/naif0012.tls | ⏱️ LSK | 5 kB |
kernels/pck/pck00010.tpc | 🪐 PCK | 123 kB |
for i in range(nb_kernels_loaded):
print(spiceypy.kdata(i, 'ALL')[0])
/tmp/metakernel-wqf7uvvy.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-lsxmh5uy.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-cg0joe9x.tm
- kernels/lsk/naif0012.tls
- kernels/pck/pck00010.tpc,
2993050038225606487)
SpicePool.add(mk, purge=True)
SpicePool, hash(SpicePool)
(<SpicePool> 3 kernels loaded:
- /tmp/metakernel-tbnwhodg.tm
- kernels/lsk/naif0012.tls
- kernels/pck/pck00010.tpc,
2993050038225606487)