Skip to content
Open
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
23 changes: 23 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "profsea-v3"
schedule:
interval: "weekly"
day: "monday"
groups:
actions-updates:
patterns:
- "*"

- package-ecosystem: "pip"
directory: "/"
target-branch: "profsea-v3"
schedule:
interval: "weekly"
day: "monday"
groups:
python-dependencies:
patterns:
- "*"
38 changes: 38 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Pytest Suite

on:
push:
branches:
- 'profsea-v3'

pull_request:
branches:
- profsea-v3

jobs:
test:
name: Run Unit Tests
runs-on: ubuntu-latest

steps:
# 1. Grab the repository code
- name: Checkout Repository
uses: actions/checkout@v4

# 2. Set up the Python environment
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12" # Adjust this to match your project's target version
cache: 'pip' # Speeds up subsequent runs

# 3. Install the package and test dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[test]"

# 4. Execute the test suite
- name: Run Pytest
run: |
pytest tests/ -v
35 changes: 35 additions & 0 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Ruff Linting

on:
push:
branches:
- 'profsea-v3'
pull_request:
branches:
- profsea-v3

jobs:
ruff:
name: Code Quality (Ruff)
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: 'pip'

- name: Install Ruff
run: |
python -m pip install --upgrade pip
pip install ruff

- name: Run Ruff Linter
run: ruff check --output-format=github .

- name: Run Ruff Formatter
run: ruff format --check .
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
*__pycache__*
profsea.egg-info
data/
.pytest_cache/
.ipynb_checkpoints/
.ruff_cache/

# Ignore the documentation build directory
docs/build
*.DS_Store
.ipynb_checkpoints/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ProFSea <img width="100" alt="logo" src="https://github.com/user-attachments/assets/cba78630-9965-4b66-8be1-065ae975229e" />

[![DOI](https://zenodo.org/badge/713453340.svg)](https://zenodo.org/doi/10.5281/zenodo.10255467)
[![DOI](https://zenodo.org/badge/713453340.svg)](https://zenodo.org/doi/10.5281/zenodo.10255467) [![Tests](https://github.com/MetOffice/ProFSea-tool/actions/workflows/pytest.yml/badge.svg?branch=profsea-v3)](https://github.com/MetOffice/ProFSea-tool/actions/workflows/pytest.yml) [![Lint](https://github.com/MetOffice/ProFSea-tool/actions/workflows/ruff.yml/badge.svg?branch=profsea-v3)](https://github.com/MetOffice/ProFSea-tool/actions/workflows/ruff.yml) [![Docs](https://github.com/MetOffice/ProFSea-tool/actions/workflows/deploy-docs.yml/badge.svg?branch=profsea-v3)](https://github.com/MetOffice/ProFSea-tool/actions/workflows/deploy-docs.yml) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

ProFSea is sea-level rise simulator based on statistical emulations of physical modelling experiments and lines of evidence from the IPCC and across the literature. It's modular, easy to setup and self-contained - all you need is global mean surface temperature and ocean heat content forcing anomalies, using any baseline period, and you're good to go.

Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,4 @@
}
html_static_path = ["_static"]
html_logo = "_static/profsea-logo.png"
html_favicon = '_static/favicon.png'
html_favicon = "_static/favicon.png"
2 changes: 2 additions & 0 deletions profsea-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ dependencies:
- pandoc
- pre-commit
- pydata-sphinx-theme
- pytest
- python=3.12
- pyyaml
- requests
- rich
- ruff
- scipy
- sphinx
- sphinx-design
Expand Down
2 changes: 2 additions & 0 deletions profsea/components/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from .global_model import Global
from .spatial_model import Spatial

__all__ = ["Global", "Spatial"]
1 change: 1 addition & 0 deletions profsea/components/core/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from abc import ABC, abstractmethod

import numpy as np

from .state import ClimateState
Expand Down
18 changes: 9 additions & 9 deletions profsea/components/core/global_model.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
from __future__ import annotations

import concurrent.futures
from pathlib import Path
import os
from typing import Dict
from pathlib import Path

import numpy as np
from rich.console import Console
import xarray as xr
from rich.console import Console

from profsea.utils import check_shapes, sample_members_2D

from .state import ClimateState
from .base import Component
from profsea.utils import sample_members_2D, check_shapes
from .state import ClimateState

console = Console()

Expand Down Expand Up @@ -58,7 +58,7 @@ class Global:

def __init__(
self,
components: Dict[str, Component],
components: dict[str, Component],
end_yr: int,
nt: int = 100,
num_members: int = 1000,
Expand Down Expand Up @@ -87,7 +87,7 @@ def run(
scenario: str,
T_change: np.ndarray,
member_seed: int = 42,
) -> Dict[str, np.ndarray]:
) -> dict[str, np.ndarray]:
"""Run the emulator to project GMSLR components for a specific state.
Parameters
----------
Expand Down Expand Up @@ -175,15 +175,15 @@ def run(

return results

def sum_components(self, components: Dict[str, np.ndarray]) -> np.ndarray:
def sum_components(self, components: dict[str, np.ndarray]) -> np.ndarray:
"""Sum the components to get total GMSLR."""
components["gmslr"] = np.sum(
[np.atleast_2d(c) for c in components.values()], axis=0
)
return components["gmslr"]

def save_components(
self, components: Dict[str, np.ndarray], output_dir: str, scenario_name: str
self, components: dict[str, np.ndarray], output_dir: str, scenario_name: str
) -> None:
"""Save SLR components as nc files to a directory.

Expand Down
21 changes: 10 additions & 11 deletions profsea/components/core/spatial_model.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
from __future__ import annotations

import os
from pathlib import Path
from typing import Dict
import warnings
import zipfile
from pathlib import Path

import dask.array as da
import numpy as np
import requests
import xarray as xr
from rich.console import Console
from rich.progress import (
Progress,
TextColumn,
BarColumn,
DownloadColumn,
TransferSpeedColumn,
Progress,
TextColumn,
TimeRemainingColumn,
TransferSpeedColumn,
track,
)
import xarray as xr

from .state import SpatialState
from .base import Component
from .state import SpatialState

console = Console()
warnings.filterwarnings("ignore")
Expand All @@ -38,7 +37,7 @@ class Spatial:

def __init__(
self,
components: Dict[str, Component],
components: dict[str, Component],
grid_config: dict = None,
grid_interpolation: str = "linear",
end_year: int = 2301,
Expand Down Expand Up @@ -133,7 +132,7 @@ def __init__(
f"resolution or take percentiles.[/bold red]"
)

def _arr_to_xr(self, arr_dict: Dict[str, da.Array]) -> Dict[str, xr.DataArray]:
def _arr_to_xr(self, arr_dict: dict[str, da.Array]) -> dict[str, xr.DataArray]:
"""
Convert a dictionary of Dask arrays to a dictionary of xarray DataArrays with appropriate coordinates and metadata.

Expand Down Expand Up @@ -222,7 +221,7 @@ def run(self, member_seed: int = 42) -> None:
self.results = spatial_projections_xr
return self.results

def sum_components(self, components: Dict[str, xr.DataArray]) -> xr.DataArray:
def sum_components(self, components: dict[str, xr.DataArray]) -> xr.DataArray:
"""
Sum the spatial components to get total sea-level change.

Expand Down Expand Up @@ -252,7 +251,7 @@ def sum_components(self, components: Dict[str, xr.DataArray]) -> xr.DataArray:

def save_components(
self,
components: Dict[str, xr.DataArray],
components: dict[str, xr.DataArray],
scenario_name: str,
output_dir: str = ".",
output_format: str = "zarr",
Expand Down
1 change: 1 addition & 0 deletions profsea/components/core/state.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from dataclasses import dataclass

import numpy as np


Expand Down
16 changes: 14 additions & 2 deletions profsea/components/global_/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
from .antarctica import AntarcticaDynAR5, AntarcticaSMBAR5, AntarcticaISMIP6
from .expansion import ThermalExpansion
from .antarctica import AntarcticaDynAR5, AntarcticaISMIP6, AntarcticaSMBAR5
from .expansion import ThermalExpansion
from .glacier import Glacier
from .greenland import GreenlandAR6, GreenlandDynAR5, GreenlandSMBAR5
from .landwater import LandwaterAR5, LandwaterAR6

__all__ = [
"AntarcticaDynAR5",
"AntarcticaISMIP6",
"AntarcticaSMBAR5",
"ThermalExpansion",
"Glacier",
"GreenlandAR6",
"GreenlandDynAR5",
"GreenlandSMBAR5",
"LandwaterAR5",
"LandwaterAR6",
]
3 changes: 2 additions & 1 deletion profsea/components/global_/antarctica.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

from pathlib import Path

import numpy as np
import xarray as xr
from scipy.signal import fftconvolve
from scipy.stats import norm
import xarray as xr

from profsea.components.core.base import Component
from profsea.components.core.global_model import ClimateState
Expand Down
3 changes: 2 additions & 1 deletion profsea/components/global_/expansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def project(self, state: ClimateState, rng: np.random.Generator) -> np.ndarray:
std_eff = 0.013 * self.distribution_scaler

exp_efficiency = (
rng.normal(loc=mean_eff, scale=std_eff, size=(state.nt, state.num_members)) * 1e-24
rng.normal(loc=mean_eff, scale=std_eff, size=(state.nt, state.num_members))
* 1e-24
) # m/YJ

ohc_3d = self.OHC_change[:, None, :]
Expand Down
4 changes: 2 additions & 2 deletions profsea/components/global_/greenland.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

import functools

from pathlib import Path

import numpy as np
import pandas as pd
from scipy.stats import truncnorm
Expand Down Expand Up @@ -71,7 +71,7 @@ def project(self, state: ClimateState, rng: np.random.Generator) -> np.ndarray:
)
trend_sle = (trend[:, :, None] * time_delta[None, None, :]) * 1e-3

tas_3d = tas[:, None, :]
tas_3d = tas[:, None, :]

# Calculate GIS contribution rate
dsle = (
Expand Down
6 changes: 4 additions & 2 deletions profsea/components/global_/landwater.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

import functools

from pathlib import Path

import numpy as np
import xarray as xr

Expand Down Expand Up @@ -50,7 +50,9 @@ def project(self, state: ClimateState, rng: np.random.Generator) -> np.ndarray:
n_samples_nc = lw_base.shape[0]

# Sample!
sample_indices = rng.integers(0, n_samples_nc, size=(state.nt, state.num_members))
sample_indices = rng.integers(
0, n_samples_nc, size=(state.nt, state.num_members)
)

# Resulting shape: (nt, nm, nyr)
lw_ens = lw_base[sample_indices, 1 : state.nyr + 1]
Expand Down
6 changes: 4 additions & 2 deletions profsea/components/spatial/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from .sterodynamic import SterodynamicCMIP5, SterodynamicCMIP6
from .fingerprint import Fingerprint
from .gia import GIA
from .gia import GIA
from .sterodynamic import SterodynamicCMIP5, SterodynamicCMIP6

__all__ = ["Fingerprint", "GIA", "SterodynamicCMIP5", "SterodynamicCMIP6"]
5 changes: 4 additions & 1 deletion profsea/components/spatial/sterodynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ def _load_CMIP6_slopes(self) -> da.Array:

# Lazily load all files keeping metadata intact
datasets = [
xr.open_dataset(f, chunks={"lat": 45, "lon": 45})["zos_zostoga_regression_slope"] for f in slope_files
xr.open_dataset(f, chunks={"lat": 45, "lon": 45})[
"zos_zostoga_regression_slope"
]
for f in slope_files
]

# Concatenate along a new dimension (representing the ensemble/models)
Expand Down
Loading
Loading