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

Adapt pysteps to allow for nowcast plugins. #418

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
08de598
adding the dgmr model to be directly imported by pysteps.nowcasts
Loickemajou Aug 8, 2024
f26bef5
added the discover nowcasts and the nowcasts_info methods
Loickemajou Aug 8, 2024
ab1b082
added the discover nowcasts at the end so that the new nowcasts metho…
Loickemajou Aug 8, 2024
08ea3cd
added the discover nowcasts and the nowcasts_info methods, to discove…
Loickemajou Aug 8, 2024
bcb7ba9
corrected the discover_nowcast method import
Loickemajou Aug 8, 2024
6f36867
reformatted interface.py
Loickemajou Aug 8, 2024
24d6c41
reformatted __init__.py
Loickemajou Aug 8, 2024
3f5d31a
reformatted interface.py
Loickemajou Aug 8, 2024
b37ce1d
added the test for the modified nowccast interfaces
Loickemajou Aug 12, 2024
cd36341
adding the test for the modified nowcasts methods
Loickemajou Aug 12, 2024
0cd6aa2
reformatted interface.py
Loickemajou Aug 12, 2024
b9dc46d
testing the modified nowcasts interface
Loickemajou Aug 12, 2024
a8a401f
fix some bugs in the discover nowcasts
Loickemajou Aug 19, 2024
d50b78c
added some test to pass the code coverage
Loickemajou Aug 19, 2024
998d076
fi
Loickemajou Aug 19, 2024
aa81fe8
fix some bugs in the discover nowcasts
Loickemajou Aug 19, 2024
54e2dd8
remove the dgmr module package
Loickemajou Aug 26, 2024
4b12402
removed the dgmr in the implimented methods
Loickemajou Aug 26, 2024
c063be5
adding a test for the nowcast pluging discovery
Loickemajou Aug 26, 2024
c790b02
reformatted __init__.py
Loickemajou Aug 26, 2024
ac9f65e
Remove nowcast plugin testing code. The code can be tested from the d…
ladc Aug 27, 2024
45070d4
Remove debug message in discover_nowcasts(); fix comment.
ladc Sep 9, 2024
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
1 change: 1 addition & 0 deletions pysteps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,4 @@ def load_config_file(params_file=None, verbose=False, dryrun=False):

# After the sub-modules are loaded, register the discovered importers plugin.
io.interface.discover_importers()
nowcasts.interface.discover_nowcasts()
7 changes: 6 additions & 1 deletion pysteps/nowcasts/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
"""Implementations of deterministic and ensemble nowcasting methods."""

from pysteps.nowcasts.interface import get_method
from pysteps.nowcasts.interface import *

try:
from dgmr_module_plugin import dgmr
except ImportError:
dgmr = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this block? the dgmr plugin, if installed, will be discovered below in discover_nowcasts()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I see, is it because the function is called forecast and it should be added to a module named dgmr? https://github.com/pySTEPS/pysteps-dgmr-nowcasts/blob/c8903bfa689951c555cfb275fb2100e36a683e40/setup.py#L86

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I see, is it because the function is called forecast and it should be added to a module named dgmr? https://github.com/pySTEPS/pysteps-dgmr-nowcasts/blob/c8903bfa689951c555cfb275fb2100e36a683e40/setup.py#L86

Also, in the get_method, actually when the plugin is installed, the dgmr is discovered by pysteps and it is possible to

dgmr=get_method('dgmr').

Though is not quite a nice way, included this block so that when the plugin is installed we could called it directly by doing

from pysteps.nowcasts import dgmr.

Had no idea on how to do that differently and will be very delighted to have some suggestions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you removed that part, @Loickemajou , does it mean that you found an alternative solution?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you removed that part, @Loickemajou , does it mean that you found an alternative solution?

Hello @dnerini

I have removed the conflicting dependencies and model weights to ensure that they do not interfere with PySTEPS during execution.

However, I have not yet found an alternative solution and would appreciate any suggestions or insights you might have to help improve the integration process.

102 changes: 102 additions & 0 deletions pysteps/nowcasts/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
get_method
"""

from pprint import pprint
from pysteps import nowcasts
import importlib
from pysteps.extrapolation.interface import eulerian_persistence
from pysteps.nowcasts import (
anvil,
Expand All @@ -39,7 +42,10 @@
steps,
sseps,
)

from pysteps.nowcasts import lagrangian_probability
import os


_nowcast_methods = dict()
_nowcast_methods["anvil"] = anvil.forecast
Expand All @@ -54,6 +60,97 @@
_nowcast_methods["steps"] = steps.forecast


def discover_nowcasts():
"""
Search for installed nowcasts plugins in the entrypoint 'pysteps.plugin.nowcasts'

The nowcasts method found are added to the `pysteps.nowcasts.interface_nowcasts_methods`
dictionary containing the available nowcasts_methods.
"""

# The pkg resources needs to be reload to detect new packages installed during
# the execution of the python application. For example, when the plugins are
# installed during the tests
import pkg_resources

importlib.reload(pkg_resources)
entry_points = list(
pkg_resources.iter_entry_points(group="pysteps.plugins.nowcasts", name=None)
)
if not entry_points:
print("No entry points found in 'pysteps.plugins.nowcasts'.")
return

for entry_point in pkg_resources.iter_entry_points(

Check warning on line 84 in pysteps/nowcasts/interface.py

View check run for this annotation

Codecov / codecov/patch

pysteps/nowcasts/interface.py#L84

Added line #L84 was not covered by tests
group="pysteps.plugins.nowcasts", name=None
):
_module = entry_point.load()
nowcast_module_name = entry_point.name

Check warning on line 88 in pysteps/nowcasts/interface.py

View check run for this annotation

Codecov / codecov/patch

pysteps/nowcasts/interface.py#L87-L88

Added lines #L87 - L88 were not covered by tests

if nowcast_module_name not in _nowcast_methods:
_nowcast_methods[nowcast_module_name] = _module

Check warning on line 91 in pysteps/nowcasts/interface.py

View check run for this annotation

Codecov / codecov/patch

pysteps/nowcasts/interface.py#L90-L91

Added lines #L90 - L91 were not covered by tests

else:
RuntimeWarning(

Check warning on line 94 in pysteps/nowcasts/interface.py

View check run for this annotation

Codecov / codecov/patch

pysteps/nowcasts/interface.py#L94

Added line #L94 was not covered by tests
f"The Nowcasts methode '{nowcast_module_name}' is already available in"
"'pysteps.nowcasts._nowcasts_methods'.\n"
f"Skipping {entry_point.module_name}:{'.'.join(entry_point.attrs)}"
)
if hasattr(nowcasts, nowcast_module_name):
RuntimeWarning(

Check warning on line 100 in pysteps/nowcasts/interface.py

View check run for this annotation

Codecov / codecov/patch

pysteps/nowcasts/interface.py#L99-L100

Added lines #L99 - L100 were not covered by tests
f"The nowcasts method '{nowcast_module_name}' is already an attribute"
"of 'pysteps.nowcasts'.\n"
f"Skipping {entry_point.module_name}:{'.'.join(entry_point.attrs)}"
)
else:
setattr(nowcasts, nowcast_module_name, _module)

Check warning on line 106 in pysteps/nowcasts/interface.py

View check run for this annotation

Codecov / codecov/patch

pysteps/nowcasts/interface.py#L106

Added line #L106 was not covered by tests


def nowcasts_info():
"""Print all the available importers."""

# nowcasts methods available in the `nowcasts` package
available_nowcasts = [
attr.split(".")[0]
for attr in os.listdir(" ".join(nowcasts.__path__))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Loickemajou Is there a more elegant way to detect all the nowcast methods that doesn't use os.listdir?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ladc, for the remark.

I will take time to look into alternative approaches and see if I can find a more elegant way to achieve this.

if not attr.startswith("__") and attr != "interface.py"
]
print("\nMethods available in the pysteps.nowcasts")
pprint(available_nowcasts)
# nowcasts declared in the pysteps.nowcast interface

nowcasts_in_the_interface = [f for f in _nowcast_methods.keys()]

print("\nMethods available in the pysteps.nowcasts.get_method interface")
pprint([(short_name, f.__name__) for short_name, f in _nowcast_methods.items()])

# Let's use sets to find out if there are importers present in the importer module
# but not declared in the interface, and viceversa.
available_nowcasts = set(available_nowcasts)
nowcasts_in_the_interface = set(nowcasts_in_the_interface)

difference = available_nowcasts ^ nowcasts_in_the_interface
if len(difference) > 0:
print("\nIMPORTANT:")
_diff = available_nowcasts - nowcasts_in_the_interface
if len(_diff) > 0:
print(
"\nIMPORTANT:\nThe following importers are available in pysteps.nowcasts module "
"but not in the pysteps.nowcasts.get_method interface"
)
pprint(_diff)
_diff = nowcasts_in_the_interface - available_nowcasts
if len(_diff) > 0:
print(
"\nWARNING:\n"
"The following importers are available in the pysteps.nowcasts.get_method "
"interface but not in the pysteps.nowcasts module"
)
pprint(_diff)

return available_nowcasts, nowcasts_in_the_interface


def get_method(name):
"""
Return a callable function for computing nowcasts.
Expand Down Expand Up @@ -90,6 +187,11 @@
| sseps | short-space ensemble prediction system (SSEPS). |
| | Essentially, this is a localization of STEPS |
+-----------------+-------------------------------------------------------+
| dgmr | a deep generative model for the probabilistic . |
| | nowcasting of precipitation from radar developed by |
| | researchers from DeepMind |
+-----------------+-------------------------------------------------------+

"""
if isinstance(name, str):
name = name.lower()
Expand Down
14 changes: 14 additions & 0 deletions pysteps/tests/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,20 @@ def test_nowcasts_interface():
for i in range(num_timesteps):
assert numpy.all(forecast[i] == precip)

# Test for invalid method types
with pytest.raises(ValueError):
pysteps.nowcasts.interface.get_method("linear")

assert isinstance(
pysteps.nowcasts.interface.nowcasts_info()[0],
set,
)

assert isinstance(
pysteps.nowcasts.interface.nowcasts_info()[1],
set,
)


def test_utils_interface():
"""Test utils module interface."""
Expand Down
Loading