Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9e52dba
Add initial DataLoader ABC and NetCDFDataLoader implementation.
oj-tooth Apr 28, 2026
cb23d10
Add initial DataWriter ABC and NetCDFDataWriter implementation.
oj-tooth Apr 28, 2026
71f1479
Add initial ObsSampler & ErrorKernel ABCs and TestObsSampler & TestEr…
oj-tooth Apr 28, 2026
e7f03b0
Add initial Regridder ABC and TestRegridder implementation.
oj-tooth Apr 28, 2026
de4240f
Add initial OceanOSSE pipeline run and describe drivers & utility fun…
oj-tooth Apr 28, 2026
3ae7e80
Add initial Pydantic validation for OceanOSSE configuration .toml files.
oj-tooth Apr 28, 2026
1b57004
Update OceanOSSE CLI & __init__ with latest modules.
oj-tooth Apr 28, 2026
6b46b89
Add example_config.toml configuration file to perform a dummy-run of …
oj-tooth Apr 28, 2026
5ae14ba
Update pyproject.toml and pixi.toml to include new dependencies and a…
oj-tooth Apr 28, 2026
5497a7e
Added a simple sampler that finds the nearest neighbour model point t…
b-barton May 6, 2026
0d5fa89
Added tests for nearest neighbour sampler with analytical test data
b-barton May 6, 2026
b189961
Removed comment
b-barton May 6, 2026
4fc2e16
Updating docstrings
b-barton May 6, 2026
46ccb86
Added tie break
b-barton May 6, 2026
0cd6085
Removing print
b-barton May 6, 2026
d978eb5
Adding time to existing tests
b-barton May 13, 2026
a412c65
Added function for finding nearest time in model
b-barton May 14, 2026
d55b740
Updated tests with time dimension and added test for time in middle o…
b-barton May 14, 2026
0015e27
There was still a bug in rounding near the centre
b-barton May 14, 2026
3ff303f
Merge remote-tracking branch 'origin/003-feature-nn-sampler' into 007…
b-barton May 14, 2026
cc199a9
Added catch for profiles that are a threshold outside of the time bou…
b-barton May 14, 2026
2b35fc4
Test exception message occurs if profiles is outside of time bounds f…
b-barton May 14, 2026
56caba7
Profiles in the model time bounds are subset before finding nearest t…
b-barton May 15, 2026
d232fed
Added test for all out of bounds time and test for partial out of tim…
b-barton May 15, 2026
2b2c44e
Making temperature over depth more realistic
b-barton May 15, 2026
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
26 changes: 18 additions & 8 deletions OceanOSSE/__init__.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
"""
OceanOSSE

Python toolbox for performing Observing System Simulation Experiments (OSSEs) in ocean general circulation models.
Python toolbox for performing Observing System Simulation Experiments (OSSEs)
in ocean general circulation models.
"""

__author__ = "Ollie Tooth (oliver.tooth@noc.ac.uk)"
__credits__ = "National Oceanography Centre (NOC), Southampton, UK"

from importlib.metadata import version as _version

from OceanOSSE import (
cli,
pipeline,
)
from OceanOSSE import cli, pipeline
from OceanOSSE.gridding.regridder import Regridder
from OceanOSSE.io.dataloader import DataLoader
from OceanOSSE.io.datawriter import DataWriter
from OceanOSSE.sampling.sampler import ErrorKernel, ObsSampler
from OceanOSSE.sampling.sampler_nearest_neighbour import NNSampler

try:
__version__ = _version("OceanOSSE")
except Exception:
# Local copy or not installed with setuptools.
# Disable minimum version checks on downstream libraries.
__version__ = "9999.0.0"

__all__ = ("cli", "pipeline")
__all__ = (
"cli",
"pipeline",
"DataLoader",
"DataWriter",
"ErrorKernel",
"ObsSampler",
"Regridder",
)
117 changes: 116 additions & 1 deletion OceanOSSE/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,119 @@
Created By: Ollie Tooth (oliver.tooth@noc.ac.uk)
"""

# -- Import Dependencies -- #
# -- Import dependencies -- #
import logging
import sys

import typer
from typing_extensions import Annotated, Optional

from OceanOSSE.pipeline import describe_pipeline, run_pipeline

from .__init__ import __version__

app = typer.Typer()
logger = logging.getLogger(__name__)


# -- Define CLI Functions -- #
def create_header(
config_path: str,
log_path: str,
) -> None:
"""
Add OceanOSSE header to log.

Parameters:
-----------
config_path : str
Filepath to OceanOSSE config .toml file.
log_path : str
Filepath to OceanOSSE log file.
"""
logger.info(
f"""
╔══════════════════════════════════════════════════════════════╗
║ OceanOSSE ║
║ Ocean Observing System Simulation Experiment Tool ║
╠══════════════════════════════════════════════════════════════╣
OceanOSSE Version : {__version__}
Python Version : {sys.version.split()[0]}
Config File : {config_path}
Log File : {log_path}
╚══════════════════════════════════════════════════════════════╝
""",
extra={"simple": True},
)


def init_logging(log_path: str) -> None:
"""
Initialise OceanOSSE logging.

Parameters:
-----------
log_path : str
Filepath to log file. If None, logs to 'ocean_osse.log'.
"""
# === Validate Inputs === #
if not isinstance(log_path, str):
raise TypeError("log_path must be a string.")

logging.basicConfig(
format="⦿══⦿ OceanOSSE ⦿══⦿ ║ %(levelname)10s ║ %(asctime)s ║ %(message)s",
level=logging.INFO,
datefmt="%Y-%m-%d %H:%M:%S",
handlers=[logging.FileHandler(log_path), logging.StreamHandler()],
)


# === Create Typer App === #
@app.callback()
def main() -> None:
"""
Main callback for Typer app to allow
single run command to be defined.
"""
pass


@app.command()
def run(
config: Annotated[str, typer.Argument(help="Path to OceanOSSE config .toml file")],
log: Annotated[
Optional[str],
typer.Option(
help="Path to write OceanOSSE log file", rich_help_panel="Options"
),
] = "ocean_osse.log",
dry_run: Annotated[
Optional[bool],
typer.Option(
help="Describe OceanOSSE workflow without execution.",
rich_help_panel="Options",
),
] = False,
) -> None:
"""
Run OceanOSSE workflow defined by configuration (.toml) file in current process.
"""
# === Initialise Logging === #
init_logging(log_path=log)
create_header(config_path=config, log_path=log)

# === Run OceanOSSE === #
args = {
"config_file": config,
"log_filepath": log,
}
if dry_run:
describe_pipeline(args=args)
else:
run_pipeline(args=args)

logging.info("✔ OceanOSSE Completed ✔")


if __name__ == "__main__":
app()
122 changes: 122 additions & 0 deletions OceanOSSE/gridding/regridder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""
regridder.py

Description: Regridding module for OceanOSSE package.

Created By: OceanOSSE Development Team (NOC, UK)
"""

# -- Import Dependencies -- #
from __future__ import annotations

import abc
import logging
from typing import Self

import xarray as xr

logger = logging.getLogger(__name__)


# -- Regridder Abstract Base Class -- #
class Regridder(abc.ABC):
"""
Abstract base class for regridding synthetic ocean observations onto
the original model grid, using methods such as objective analysis
or interpolation.

Parameters
----------
target_grid : xarray.Dataset or None, optional
Dataset describing the target grid (coordinates, masks, etc.).
"""

def __init__(
self,
target_grid: xr.Dataset | None = None,
) -> None:
if target_grid is not None and not isinstance(target_grid, xr.Dataset):
raise TypeError("``target_grid`` must be an xarray.Dataset or None.")
self._target_grid = target_grid

def __repr__(self) -> str:
has_grid = self._target_grid is not None
return f"{type(self).__name__}(target_grid={'<Dataset>' if has_grid else None})"

@classmethod
@abc.abstractmethod
def from_config(cls, config: dict) -> Self:
"""
Construct a Regridder from the from the `[regridding]` table of
the .toml configuration file.

Parameters
----------
config : dict
Configuration dictionary containing input parameters from .toml
configuration file.

Returns
-------
Self
Initialised Regridder instance.
"""
...

@abc.abstractmethod
def regrid(self, ds: xr.Dataset) -> xr.Dataset:
"""
Regrid the synthetic observation dataset onto the target grid.

Parameters
----------
ds : xarray.Dataset
Synthetic observations dataset.

Returns
-------
xarray.Dataset
Dataset of synthetic observations regridded onto target grid.
"""
...


# -- Regridder Implementations -- #


class TestRegridder(Regridder):
"""
Regridder used for testing and scaffold validation.

Returns the the synthetic observations dataset unchanged.
"""

@classmethod
def from_config(cls, config: dict) -> Self:
"""
Instantiate a TestRegridder from the `[regridding]` table of
the .toml configuration file.
"""
return cls()

def regrid(self, ds: xr.Dataset) -> xr.Dataset:
"""
Regrid the synthetic observation dataset onto the target grid.

Parameters
----------
ds : xarray.Dataset
Synthetic observations dataset.

Returns
-------
xarray.Dataset
Dataset of synthetic observations (unchanged from input).
"""
logger.debug(
"Regridding synthetic observations with TestRegridder -> returns input dataset unchanged."
)
logging.info(
"--> Completed: Regridded synthetic observations with TestRegridder."
)
return ds
Loading
Loading