Skip to content

Commit cd9165e

Browse files
[CLEANUP] Release prep 0.3.1 (#141)
* Prepare package for v0.3.1 release * linting * mark downloading tests as slow * copilot review changes * copilot review changes
1 parent 95153e7 commit cd9165e

34 files changed

Lines changed: 2452 additions & 115 deletions

CHANGELOG.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Changelog
2+
3+
All notable changes to AMOCatlas will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Fixed
11+
- Fixed `__version__` import in `__init__.py` for proper package version access
12+
- Enhanced test coverage for `fbc.py` and `noac47n.py` modules
13+
14+
### Added
15+
- Comprehensive test cases for FBC (Faroe Bank Channel) data reader
16+
- Comprehensive test cases for NOAC 47°N array data reader
17+
- CHANGELOG.md file for tracking project changes
18+
19+
## [0.3.0] - YYYY-MM-DD
20+
21+
### Added
22+
- New reader for Le Bras AMOC at 35°N (#139)
23+
- New Sanchez-Franks 2021 Reader (#137)
24+
- New NAC reader (#134)
25+
- Report generation functionality (#140)
26+
27+
### Updated
28+
- Updated FBC transport with new location and extended timeseries (#133)
29+
- Updated metadata for Zheng2024 (#136)
30+
- Updated Zheng2024 plot to 2D (#135)
31+
32+
### Fixed
33+
- Fixed convert lowercase URL into uppercase when case sensitive (#132)
34+
- Fixed standardized sigma coords on RAPID to be <1000 (#126)
35+
36+
## [0.2.0] - YYYY-MM-DD
37+
38+
### Added
39+
- New intuitive API (`amocatlas.read` namespace)
40+
- Automatic data standardization
41+
- Enhanced metadata management system
42+
- Comprehensive documentation and reports
43+
44+
### Changed
45+
- Legacy API (`load_dataset`, `load_sample_dataset`) marked as deprecated
46+
- Improved package architecture with modular data sources
47+
48+
## [0.1.1] - YYYY-MM-DD
49+
50+
### Fixed
51+
- Bug fixes and minor improvements
52+
53+
## [0.1.0] - YYYY-MM-DD
54+
55+
### Added
56+
- Initial stable release
57+
- Basic data loading functionality for major AMOC arrays
58+
- Core plotting and analysis tools
59+
60+
## [0.0.4] - YYYY-MM-DD
61+
62+
### Added
63+
- Early development version
64+
- Basic reader implementations
65+
66+
---
67+
68+
### Legend
69+
70+
- **Added** for new features
71+
- **Changed** for changes in existing functionality
72+
- **Deprecated** for soon-to-be removed features
73+
- **Removed** for now removed features
74+
- **Fixed** for any bug fixes
75+
- **Security** for vulnerability fixes
76+
77+
### Release Process
78+
79+
1. Create a new version section above "Unreleased"
80+
2. Move items from "Unreleased" to the new version section
81+
3. Add release date in YYYY-MM-DD format
82+
4. Create git tag with format `v{version}` (e.g., `v0.3.1`)
83+
5. GitHub Actions will automatically publish to PyPI
84+
85+
### Contributing
86+
87+
When adding changes, please:
88+
1. Add new entries under the "Unreleased" section
89+
2. Use the appropriate category (Added, Changed, Fixed, etc.)
90+
3. Include PR numbers in parentheses when applicable
91+
4. Keep descriptions clear and user-focused

README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,21 @@ from amocatlas import read
9696
ds = read.osnap() # Single standardized dataset
9797
all_files = read.osnap(all_files=True) # Get all files for array
9898

99-
# Or use the legacy API
100-
from amocatlas import readers
101-
datasets = readers.load_dataset("osnap") # Returns list of raw datasets
10299
```
103100

104101
A `*.log` file will be written to `logs/` by default.
105102

106-
Data will be cached in `~/.amocatlas_data/` unless you specify a custom location.
103+
Data will be cached in `~/.amocatlas_data/` unless you specify a custom location with `data_dir="/path/to/custom/data"`.
104+
105+
**Setting Default Data Directory:**
106+
```python
107+
import amocatlas
108+
amocatlas.set_data_dir("~/my_data") # Custom location
109+
amocatlas.set_data_dir("project") # Use project/data (source checkout only)
110+
print(amocatlas.get_data_dir()) # Show current setting
111+
```
112+
113+
**Note:** The `"project"` option only works when running from a source checkout (editable install with `pip install -e .`). For regular pip installations, use an explicit path like `"~/my_data"` instead.
107114

108115
### API Features (v0.2.0+)
109116

@@ -128,11 +135,6 @@ all_rapid = read.rapid(all_files=True) # Get all files for an array
128135
raw_data = read.rapid(raw=True) # Original format for special cases
129136
```
130137

131-
**Legacy API (still supported):**
132-
```python
133-
from amocatlas import readers
134-
datasets = readers.load_dataset("rapid") # Returns raw data as before
135-
```
136138

137139
## Documentation
138140

amocatlas/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@
4040
data_sources, # New data sources package
4141
)
4242

43+
# Import key utilities at top level for convenience
44+
from .utilities import set_data_dir, get_data_dir
45+
4346
# Version information
44-
# from ._version import __version__
47+
from ._version import __version__
4548

4649
__all__ = [
4750
"readers",
@@ -56,5 +59,7 @@
5659
"compliance_checker",
5760
"reader_utils",
5861
"data_sources",
62+
"set_data_dir",
63+
"get_data_dir",
5964
"__version__",
6065
]

amocatlas/data_sources/lebras35n.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
This module provides functions to read and process AMOC transport data at 35°N from deep moorings, floats and satellite altimetry.
44
5-
Dataset one includes the AMOC transport in depth and in density space, the integrated across-basin streamfunction in depth space and the Ekman transport time series derived from CCMP.
5+
Dataset one includes the AMOC transport in depth and in density space, the integrated across-basin streamfunction in depth space and the Ekman transport time series derived from CCMP.
66
Dataset two includes the geostrophic velocities through the section, the potential density anomaly referenced to 2000m, the depth of the sea floor along the section and the area of each grid point.
77
88
Key functions:

amocatlas/data_sources/nac.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""North Atlantic Current (NAC) data reader for AMOCatlas.
22
3-
This module provides functions to read and process the North Atlantic current time series from satellite and float observations.
3+
This module provides functions to read and process the North Atlantic current time series from satellite and float observations.
44
The NAC is a key component of the Atlantic Meridional Overturning Circulation, transporting warm, saline water from the tropics to the high northern latitudes.
55
66
The dataset includes NAC transport estimates from satellite and float observations and an NAC estimation from satellite altimetry alone.

amocatlas/data_sources/rapid26n.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ def read_rapid(
154154
if file == "meridional_transports.nc":
155155
if "sigma0" in ds.coords:
156156
original_values = ds["sigma0"].values
157-
if original_values.max() > 100: # Check if values are in absolute density range (>1000) rather than anomaly range
157+
if (
158+
original_values.max() > 100
159+
): # Check if values are in absolute density range (>1000) rather than anomaly range
158160
log_info(
159161
f"Fixing sigma0 coordinates in {file}: subtracting 1000 to get density anomaly"
160162
)
@@ -166,7 +168,9 @@ def read_rapid(
166168

167169
if "sigma2" in ds.coords:
168170
original_values = ds["sigma2"].values
169-
if original_values.max() > 100: # Check if values are in absolute density range (>1000) rather than anomaly range
171+
if (
172+
original_values.max() > 100
173+
): # Check if values are in absolute density range (>1000) rather than anomaly range
170174
log_info(
171175
f"Fixing sigma2 coordinates in {file}: subtracting 1000 to get density anomaly"
172176
)

amocatlas/data_sources/sf2021.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Sanchez-Franks Satellite proxy for the AMOC at 26N data reader for AMOCatlas.
22
33
This module provides functions to read and process satellite proxy transport data for 26N
4-
from Sanchez-Franks et al. (2021). This dataset provides a satellite reconstruction of the AMOC transport
4+
from Sanchez-Franks et al. (2021). This dataset provides a satellite reconstruction of the AMOC transport
55
at 26N based on satellite altimetry. It also includes the upper-mid-ocean and gulf stream components.
66
The components are derived through a dynamically based method.
77
"""
@@ -46,54 +46,61 @@
4646
"long_name": "Time elapsed since 1970-01-01T00:00:00Z",
4747
"standard_name": "time",
4848
"calendar": "gregorian",
49-
"vocabulary": "http://vocab.nerc.ac.uk/collection/OG1/current/TIME/"
49+
"vocabulary": "http://vocab.nerc.ac.uk/collection/OG1/current/TIME/",
5050
}
5151

5252

53-
def _normalize_sf2021_time_coordinate(ds: xr.Dataset, source_file: str = None) -> xr.Dataset:
53+
def _normalize_sf2021_time_coordinate(
54+
ds: xr.Dataset, source_file: str = None
55+
) -> xr.Dataset:
5456
"""Convert SF2021 TIME coordinate from days since 0000-01-01 to datetime64[ns].
55-
57+
5658
Parameters
5759
----------
5860
ds : xr.Dataset
5961
Dataset with sat_time or TIME coordinate as float (days since 0000-01-01)
6062
source_file : str, optional
6163
Source filename (currently unused, kept for API compatibility)
62-
64+
6365
Returns
6466
-------
6567
xr.Dataset
6668
Dataset with time coordinate converted to datetime64[ns]
69+
6770
"""
6871
# Find time variable (check raw name first, then final name)
6972
time_var = next((var for var in ["sat_time", "TIME"] if var in ds.coords), None)
70-
73+
7174
if not time_var or ds[time_var].dtype.kind not in ["f", "i"]:
72-
log_debug(f"Skipping TIME normalization - {time_var or 'TIME'} not found or not numeric")
75+
log_debug(
76+
f"Skipping TIME normalization - {time_var or 'TIME'} not found or not numeric"
77+
)
7378
return ds
74-
79+
7580
try:
7681
# Convert days since 0000-01-01 to datetime64[ns] without using year 0 in ns resolution.
7782
# Decompose into integer days and fractional nanoseconds relative to 1970-01-01.
7883
time_values = np.asarray(ds[time_var].values, dtype=np.float64)
7984
epoch_days = 719528.0 # Days between 0000-01-01 and 1970-01-01 in proleptic Gregorian calendar.
8085
relative_days = time_values - epoch_days
8186
whole_days = np.floor(relative_days).astype(np.int64)
82-
fractional_ns = np.rint((relative_days - whole_days) * 86400 * 1e9).astype(np.int64)
87+
fractional_ns = np.rint((relative_days - whole_days) * 86400 * 1e9).astype(
88+
np.int64
89+
)
8390

8491
time_datetime = (
8592
np.datetime64("1970-01-01", "ns")
8693
+ whole_days.astype("timedelta64[D]")
8794
+ fractional_ns.astype("timedelta64[ns]")
8895
).astype("datetime64[ns]")
89-
96+
9097
# Use assign_coords to properly set dimension coordinate
9198
ds = ds.assign_coords({time_var: time_datetime})
9299
ds[time_var].attrs = _TIME_METADATA
93100
log_debug(f"Converted SF2021 {time_var} from days to datetime64[ns]")
94101
except (ValueError, TypeError, OverflowError) as e:
95102
log_warning(f"Failed to convert SF2021 TIME coordinate: {e}")
96-
103+
97104
return ds
98105

99106

@@ -188,7 +195,7 @@ def read_sf2021(
188195
# Use ReaderUtils for consistent dataset loading
189196

190197
ds = ReaderUtils.safe_load_dataset(file_path)
191-
198+
192199
# Attach metadata
193200
# Attach metadata with optional tracking
194201

@@ -219,7 +226,7 @@ def read_sf2021(
219226
DATASOURCE_ID,
220227
track_added_attrs=False,
221228
)
222-
229+
223230
# Normalize SF2021 TIME coordinate AFTER metadata attachment
224231
ds = _normalize_sf2021_time_coordinate(ds, source_file=file)
225232
else:

amocatlas/defaults.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@
239239
"wh41n", # Woods Hole 41°N array
240240
"noac47n", # NOAC 47°N array (North Atlantic Ocean Current)
241241
"nac", # North Atlantic Current
242-
"sf2021", # Sanchez-Franks 2021 satellite reconstruction of AMOC transport at 26°N
243-
"lebras35n", # Lebras 2023 AMOC transport at 35°N
242+
"sf2021", # Sanchez-Franks 2021 satellite reconstruction of AMOC transport at 26°N
243+
"lebras35n", # Lebras 2023 AMOC transport at 35°N
244244
]
245245

246246
# Mapping from array names to their full descriptions

amocatlas/plotters.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ def plot_amoc_timeseries(
633633
ax.spines["right"].set_visible(False)
634634
ax.set_title(title)
635635
ax.set_xlabel("Time")
636-
ax.set_ylabel(ylabel if ylabel else "Transport [Sv]")
636+
ax.set_ylabel(ylabel if ylabel else "Transport (Sv)")
637637
ax.legend(loc="best")
638638
ax.grid(True, linestyle="--", alpha=0.5)
639639

@@ -681,7 +681,7 @@ def plot_monthly_anomalies(
681681
axes[i].plot(time, data, color=color, label=label)
682682
axes[i].axhline(0, color="black", linestyle="--", linewidth=0.5)
683683
axes[i].set_title(label)
684-
axes[i].set_ylabel("Transport [Sv]")
684+
axes[i].set_ylabel("Transport (Sv)")
685685
axes[i].legend()
686686
axes[i].grid(True, linestyle="--", alpha=0.5)
687687

@@ -745,7 +745,7 @@ def _add_amocatlas_timestamp(fig: object) -> None:
745745

746746

747747
def plot_moc_timeseries_pygmt(
748-
df: pd.DataFrame, column: str = "moc", label: str = "MOC [Sv]"
748+
df: pd.DataFrame, column: str = "moc", label: str = "MOC (Sv)"
749749
) -> "pygmt.Figure":
750750
"""Plot MOC time series using PyGMT with publication-quality styling.
751751
@@ -755,7 +755,7 @@ def plot_moc_timeseries_pygmt(
755755
DataFrame with 'time_num' (decimal years) and data columns.
756756
column : str, default "moc"
757757
Name of the column to plot.
758-
label : str, default "MOC [Sv]"
758+
label : str, default "MOC (Sv)"
759759
Y-axis label for the plot.
760760
761761
Returns
@@ -885,7 +885,7 @@ def plot_osnap_components_pygmt(data: Union[DataFrame, Dict]) -> "pygmt.Figure":
885885

886886
# Basemap
887887
fig.basemap(
888-
region=region, projection="X15c/7c", frame=["xaf", "yafg5f2+lMOC [Sv]", "WS"]
888+
region=region, projection="X15c/7c", frame=["xaf", "yafg5f2+lMOC (Sv)", "WS"]
889889
)
890890

891891
# --- Shaded error for EAST ---
@@ -977,7 +977,7 @@ def plot_rapid_components_pygmt(df: pd.DataFrame) -> "pygmt.Figure":
977977
fig.basemap(
978978
region=region,
979979
projection="X25c/15c",
980-
frame=["xaf", "yafg5f2+lTransport [Sv]", "WS+tRAPID MOC Components"],
980+
frame=["xaf", "yafg5f2+lTransport (Sv)", "WS+tRAPID MOC Components"],
981981
)
982982

983983
# Plot each component with custom colors
@@ -1067,10 +1067,10 @@ def plot_all_moc_pygmt(
10671067

10681068
# Prepare data and labels
10691069
dfs = [
1070-
(osnap_df, "MOC [Sv]", (5, 25), 5, "OSNAP", green1, "W"),
1071-
(rapid_df, "MOC [Sv]", (5, 30), 6, "RAPID 26°N", red1, "E"),
1072-
(move_df, "MOC [Sv]", (5, 30), 6, "MOVE 16°N", magenta1, "W"),
1073-
(samba_df, "Anomaly [Sv]", (-10, 15), 6, "SAMBA 34.5°S", blue1, "ES"),
1070+
(osnap_df, "MOC (Sv)", (5, 25), 5, "OSNAP", green1, "W"),
1071+
(rapid_df, "MOC (Sv)", (5, 30), 6, "RAPID 26°N", red1, "E"),
1072+
(move_df, "MOC (Sv)", (5, 30), 6, "MOVE 16°N", magenta1, "W"),
1073+
(samba_df, "Anomaly (Sv)", (-10, 15), 6, "SAMBA 34.5°S", blue1, "ES"),
10741074
]
10751075

10761076
# Find global x range
@@ -1210,7 +1210,7 @@ def plot_bryden2005_pygmt() -> "pygmt.Figure":
12101210
fig.basemap(
12111211
region=[1955, 2006, 13, 24],
12121212
projection="X8c/6c",
1213-
frame=["WS", "yaf+lMOC [Sv]", "xccustom_xticks.txt"],
1213+
frame=["WS", "yaf+lMOC (Sv)", "xccustom_xticks.txt"],
12141214
)
12151215

12161216
# Plot red line
@@ -1278,10 +1278,10 @@ def plot_all_moc_overlaid_pygmt(
12781278

12791279
# Prepare data and labels - overlay mode (shiftflag=False)
12801280
dfs = [
1281-
(osnap_df, "MOC [Sv]", (10, 20), 6, "OSNAP", green1, "W"),
1282-
(rapid_df, "MOC [Sv]", (10, 20), 6, "RAPID 26°N", red1, "W"),
1283-
(move_df, "MOC [Sv]", (10, 20), 6, "MOVE 16°N", magenta1, "W"),
1284-
(samba_df, "Anomaly [Sv]", (-5, 5), 6, "SAMBA 34.5°S", blue1, "ES"),
1281+
(osnap_df, "MOC (Sv)", (10, 20), 6, "OSNAP", green1, "W"),
1282+
(rapid_df, "MOC (Sv)", (10, 20), 6, "RAPID 26°N", red1, "W"),
1283+
(move_df, "MOC (Sv)", (10, 20), 6, "MOVE 16°N", magenta1, "W"),
1284+
(samba_df, "Anomaly (Sv)", (-5, 5), 6, "SAMBA 34.5°S", blue1, "ES"),
12851285
]
12861286

12871287
# Find global x range

0 commit comments

Comments
 (0)