Skip to content
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
2 changes: 2 additions & 0 deletions docs/guide/executing_experiment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ The `<command>` can be one of the following:
- `stage`: Prepare the environment and models.
- `plot`: Plot forecasts, catalogs, and results.
- `reproduce`: (see folllowing\ section) Reproduce the results of a previously run experiment.
- `view`: Export the experiment as a web dashboard and open it in the browser (see :ref:`dashboard`).
- `export`, `serve`: The two steps of `view`, run separately.

Each command requires a configuration file (in `YAML` format), which defines the parameters of the experiment.

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ Collaborators
guide/evaluation_config.rst
guide/postprocess_config.rst
guide/executing_experiment.rst
guide/dashboard.rst


.. sidebar-links::
Expand Down
1 change: 0 additions & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ dependencies:
- gitpython
- h5py
- matplotlib
- panel
- pip
- pyshp
- pyyaml
Expand Down
93 changes: 74 additions & 19 deletions floatcsep/commands/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import argparse
import logging
from pathlib import Path

from floatcsep import __version__
from floatcsep.experiment import Experiment, ExperimentComparison
Expand All @@ -11,7 +12,7 @@
plot_custom,
)
from floatcsep.postprocess.reporting import generate_report, reproducibility_report
from floatcsep.postprocess.panel import run_app
from floatcsep.postprocess.web import export_experiment, serve as serve_dashboard

setup_logger()
log = logging.getLogger("floatLogger")
Expand Down Expand Up @@ -130,36 +131,85 @@ def plot(config: str, **kwargs) -> None:
log.debug("")


def view(config: str, **kwargs) -> None:
def view(config: str, out: str = None, port: int = 8765, no_browser: bool = False, **kwargs) -> None:
"""
Launch an interactive Panel-based data viewer for an existing experiment.
Exports an existing experiment as a static web dashboard and opens it in the browser.

This function loads the experiment configuration, reconstructs the model and
evaluation file tree, and starts a Panel server so that the user can explore the
catalogs, forecasts, and test results in a web browser.
It always re-exports, so the dashboard reflects the current results. Use
:func:`export` and :func:`serve` to do the two steps separately.

Example usage from a terminal:
::

floatcsep view <config>
floatcsep view <config> --port 8080

Args
----
config : str
Path to the experiment configuration file (YAML format).
**kwargs :
Additional configuration parameters forwarded to `Experiment.from_yml`.

Returns
-------
None
Args:
config (str): Path to the experiment configuration file (YAML format).
out (str): Dashboard folder. Defaults to ``<run_dir>/dashboard``.
port (int): TCP port to serve on.
no_browser (bool): Do not open the browser automatically.
**kwargs: Additional configuration parameters forwarded to `Experiment.from_yml`.
"""
log.info(f"floatCSEP v{__version__} | View")
exp = Experiment.from_yml(config_yml=config, **kwargs)
exp.stage_models()
exp.set_tree()
log.info(f"Creating Panel App")
run_app(experiment=exp, **kwargs)
folder = export_experiment(exp, out_dir=out)
serve_dashboard(folder, port=port, open_browser=not no_browser)


def export(config: str, out: str = None, **kwargs) -> None:
"""
Exports an existing experiment as a static web dashboard (``<run_dir>/dashboard``).

The folder holds the experiment manifest, catalog, forecasts and results as JSON, plus
the front-end files, and can be served by any static web server or published as is.

Example usage from a terminal:
::

floatcsep export <config>

Args:
config (str): Path to the experiment configuration file (YAML format).
out (str): Output folder. Defaults to ``<run_dir>/dashboard``.
**kwargs: Additional configuration parameters forwarded to `Experiment.from_yml`.
"""
log.info(f"floatCSEP v{__version__} | Export")
kwargs.pop("port", None)
kwargs.pop("no_browser", None)
exp = Experiment.from_yml(config_yml=config, **kwargs)
exp.stage_models()
exp.set_tree()
export_experiment(exp, out_dir=out)
log.info("Finalized")


def serve(config: str, out: str = None, port: int = 8765, no_browser: bool = False, **kwargs) -> None:
"""
Serves the static dashboard of an experiment, exporting it first if it does not exist.

Example usage from a terminal:
::

floatcsep serve <config>
floatcsep serve <config> --port 8080

Args:
config (str): Path to the experiment configuration file (YAML format).
out (str): Dashboard folder. Defaults to ``<run_dir>/dashboard``.
port (int): TCP port to serve on.
no_browser (bool): Do not open the browser automatically.
"""
log.info(f"floatCSEP v{__version__} | Serve")
exp = Experiment.from_yml(config_yml=config, **kwargs)
folder = Path(out) if out else exp.registry.run_dir / "dashboard"
if not (folder / "manifest.json").exists():
exp.stage_models()
exp.set_tree()
folder = export_experiment(exp, out_dir=out)
serve_dashboard(folder, port=port, open_browser=not no_browser)


def reproduce(config: str, **kwargs) -> None:
Expand Down Expand Up @@ -225,7 +275,7 @@ def floatcsep() -> None:
parser.add_argument(
"func",
type=str,
choices=["run", "stage", "plot", "view", "reproduce"],
choices=["run", "stage", "plot", "view", "export", "serve", "reproduce"],
help="Run a calculation",
)
parser.add_argument("config", type=str, help="Experiment Configuration file")
Expand All @@ -236,6 +286,11 @@ def floatcsep() -> None:
"-t", "--timestamp", action="store_true", default=False, help="Timestamp results"
)

parser.add_argument("-o", "--out", type=str, help="Dashboard output folder (export/serve)")
parser.add_argument("-p", "--port", type=int, help="Port for `serve` (default 8765)")
parser.add_argument(
"--no-browser", action="store_true", help="Do not open the browser on `serve`"
)
parser.add_argument(
"-d",
"--debug",
Expand Down
10 changes: 5 additions & 5 deletions floatcsep/infrastructure/repositories.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from csep.core.catalogs import CSEPCatalog
from csep.core.forecasts import GriddedForecast, CatalogForecast
from csep.models import EvaluationResult
from csep.utils.time_utils import decimal_year
from csep.utils.time_utils import decimal_year, datetime_to_utc_epoch

from floatcsep.infrastructure.registries import (
ExperimentRegistry,
Expand Down Expand Up @@ -233,9 +233,9 @@ def get_input_cat(self, tstring: str) -> CSEPCatalog:
"""
start, end = str2timewindow(tstring)
cfg = self.input_cat_config
filters = [f"origin_time < {start.timestamp() * 1000}"]
filters = [f"origin_time < {datetime_to_utc_epoch(start)}"]
if cfg["start_date"]:
filters.append(f"origin_time >= {cfg['start_date'].timestamp() * 1000}")
filters.append(f"origin_time >= {datetime_to_utc_epoch(cfg['start_date'])}")
if cfg["mag_min"] is not None:
filters.append(f"magnitude >= {cfg['mag_min']}")
if cfg["mag_max"] is not None:
Expand Down Expand Up @@ -283,8 +283,8 @@ def set_test_cats(self, tstring: str, fmt: str = "json") -> None:
)
start, end = str2timewindow(tstring)
filters = [
f"origin_time < {end.timestamp() * 1000}",
f"origin_time >= {start.timestamp() * 1000}",
f"origin_time < {datetime_to_utc_epoch(end)}",
f"origin_time >= {datetime_to_utc_epoch(start)}",
f"magnitude >= {self.mag_min}",
f"magnitude < {self.mag_max}",
]
Expand Down
8 changes: 5 additions & 3 deletions floatcsep/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from floatcsep.infrastructure.environments import EnvironmentFactory
from floatcsep.infrastructure.registries import ModelRegistry
from floatcsep.infrastructure.repositories import ForecastRepository
from floatcsep.infrastructure.repositories import ForecastRepository, GriddedForecastRepository
from floatcsep.utils.accessors import from_zenodo, from_git
from floatcsep.utils.helpers import timewindow2str, str2timewindow, parse_nested_dicts

Expand Down Expand Up @@ -383,10 +383,12 @@ def get_forecast(
If None, will return a forecast for all regions.

Note:
The number of synthetic catalogs is taken from ``func_kwargs['n_sims']`` (or a
model-level ``n_sims``). Without it, pyCSEP infers it from the largest
For catalog-based forecasts, the number of synthetic catalogs is taken from
``func_kwargs['n_sims']`` (or a model-level ``n_sims``). Without it, pyCSEP infers it from the largest
``catalog_id`` in the file, which misses trailing empty catalogs.
"""
if isinstance(self.repository, GriddedForecastRepository):
return self.repository.load_forecast(tstring, name=self.name, region=region)
n_sims = self.func_kwargs.get("n_sims") or self.__dict__.get("n_sims")
return self.repository.load_forecast(
tstring, name=self.name, region=region, n_sims=n_sims
Expand Down
3 changes: 0 additions & 3 deletions floatcsep/postprocess/panel/__init__.py

This file was deleted.

38 changes: 0 additions & 38 deletions floatcsep/postprocess/panel/app.py

This file was deleted.

Loading
Loading