Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return a copy of mapped data to protect cached data from being modified #164

Merged
merged 2 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/general_python_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -236,5 +236,39 @@ Backplanes can also be generated for observations which do not exist using :clas
:alt: Plot of Jupiter's rotation


Cache behaviour
===============
The generation of backplanes and projected mapped data can be slow for larger datasets. Therefore, :class:`planetmapper.BodyXY` and :class:`planetmapper.Observation` objects automatically cache the results of various expensive function calls so that they do not have to be recalculated. This cache management happens automatically behind the scenes, so you should never have to worry about dealing with it directly. For example, when any disc parameters are changed, the cache is automatically cleared as the cached results will no longer be valid.

::

# Create a new object
body = planetmapper.BodyXY('Jupiter', '2000-01-01', sz=500)
body.set_disc_params(x0=250, y0=250, r0=200)
# At this point, the cache is completely empty

# The intermediate results used in generating the incidence angle backplane
# are cached, speeding up any future calculations which use these
# intermediate results:
body.get_backplane_img('INCIDENCE') # Takes ~10s to execute
body.get_backplane_img('INCIDENCE') # Executes instantly
body.get_backplane_img('EMISSION') # Executes instantly

# When any of the disc parameters are changed, the xy <-> radec conversion
# changes so the cache is automatically cleared (as the cached intermediate
# results are no longer valid):
body.set_r0(190) # This automatically clears the cache
body.get_backplane_img('EMISSION') # Takes ~10s to execute
body.get_backplane_img('INCIDENCE') # Executes instantly

The methods which cache their results include...

- :func:`planetmapper.BodyXY.get_backplane_img`
- :func:`planetmapper.BodyXY.get_backplane_map`
- :func:`planetmapper.Observation.get_mapped_data`
- :func:`planetmapper.Observation.save_observation` and equivalent option in the GUI
- :func:`planetmapper.Observation.save_mapped_observation` and equivalent option in the GUI


.. note::
The Python script used to generate all the figures shown on this page can be found `here <https://github.com/ortk95/planetmapper/blob/main/examples/general_python_api.py>`_
1 change: 1 addition & 0 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ To upgrade an existing PlanetMapper installation to the latest version, run: ::

pip install planetmapper --upgrade

You can view the release notes for each version `on GitHub <https://github.com/ortk95/planetmapper/releases>`__.

First steps
===========
Expand Down
21 changes: 19 additions & 2 deletions planetmapper/observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,6 @@ def fit_disc_radius(self) -> None:
self.set_disc_method('fit_r0')

# Mapping
@_cache_clearable_result_with_args
@progress_decorator
def get_mapped_data(
self,
degree_interval: float = 1,
Expand All @@ -499,6 +497,13 @@ def get_mapped_data(
Projects the observed :attr:`data` onto a lon/lat grid using
:func:`BodyXY.map_img`.

For larger datasets, it can take some time to map every wavelength. Therefore,
the mapped data is automatically cached (in a similar way to backplanes - see
:class:`BodyXY`) so that subsequent calls to this function do not have to
recompute the mapped data. As with cached backplanes, the cached mapped data is
automatically cleared if any disc parameters are changed (i.e. you shouldn't
need to worry about the cache, it all happens 'magically' behind the scenes).

Args:
degree_interval: Interval in degrees between the longitude/latitude points.
Passed to :func:`BodyXY.map_img`.
Expand All @@ -511,6 +516,18 @@ def get_mapped_data(
each location on the surface of the target body. Locations which are not
visible have a value of NaN.
"""
# Return a copy so that the cached value isn't tainted by any modifications
return self._get_mapped_data(
degree_interval=degree_interval, interpolation=interpolation
).copy()

@_cache_clearable_result_with_args
@progress_decorator
def _get_mapped_data(
self,
degree_interval: float = 1,
interpolation: Literal['nearest', 'linear', 'quadratic', 'cubic'] = 'linear',
):
projected = []
if interpolation == 'linear' and np.any(np.isnan(self.data)):
data = np.nan_to_num(self.data)
Expand Down
2 changes: 1 addition & 1 deletion planetmapper/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def __init__(self, n_wavelengths: int, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.n_wavelengths = n_wavelengths
self.weights: dict[str, float] = {
'Observation.get_mapped_data': n_wavelengths * 5,
'Observation._get_mapped_data': n_wavelengths * 5,
'BodyXY._get_targvec_map': 1,
'BodyXY._get_illumf_map': 5,
'BodyXY._get_radec_map': 10,
Expand Down
26 changes: 13 additions & 13 deletions scratchpad.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
# body.run_gui()
# body.save_mapped_observation('~/Desktop/map_nearest.fits', interpolation='nearest')

# ax = body.plot_wireframe_xy()
# img = np.nanmedian(body.data, axis=0)
# ax.imshow(img, origin='lower')
# plt.show()
# img = np.nan_to_num(img)
ax = body.plot_wireframe_xy()
img = np.nanmedian(body.data, axis=0)
ax.imshow(img, origin='lower')
plt.show()
img = np.nan_to_num(img)

# for interpolation in ('nearest', 'linear', 'quadratic', 'cubic'):
# map_img = body.map_img(img, interpolation=interpolation)
# fig, ax = plt.subplots(figsize=(5,5))
# body.imshow_map(map_img, ax=ax)
# ax.set_title(interpolation)
# plt.show()
for interpolation in ('nearest', 'linear', 'quadratic', 'cubic'):
map_img = body.map_img(img, interpolation=interpolation)
fig, ax = plt.subplots(figsize=(5,5))
body.imshow_map(map_img, ax=ax)
ax.set_title(interpolation)
plt.show()


gui = planetmapper.gui.GUI()
gui.run()
# gui = planetmapper.gui.GUI()
# gui.run()