Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/rustychris/stompy
Browse files Browse the repository at this point in the history
  • Loading branch information
ed committed Aug 27, 2024
2 parents 6b51240 + 5ef707f commit cb60bb7
Show file tree
Hide file tree
Showing 69 changed files with 7,617 additions and 1,153 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/publish-to-test-pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name: Publish Python distribution to PyPI and TestPyPI

on: push

3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions .idea/stompy.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2

#build:
# tools:


sphinx:
configuration: docs/conf.py

python:
install:
- requirements-rtd.txt

45 changes: 20 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
# stompy
# stompy: spatial tools for ocean modeling in python

Various python modules related to modeling and oceanographic data analysis.

## Installation
## Prerequisites

There is not yet a pip or conda installer setup. (if you use this code and would find that useful, please
add an issue on github so I know somebody cares).
`stompy` requires a Python 3 environment with `gdal` installed. There are many ways to set this up. The recommended method would be creating a `mamba` (or `conda`) environment:

### Requirements
```
mamba create -n <env-name> python "gdal<3.9" "numpy<2.0"
mamba activate <env-name>
```

`stompy` makes extensive use of the core packages of a modern scientific python installation,
plus a few slightly more specialized modules:
## Installation

* python 2.7 or 3
* six
* numpy
* scipy
* gdal
* shapely
* matplotlib
* xarray
* pandas
* netCDF

### Installation

Python must be able to find the `stompy` subdirectory of the repository. So on Linux, this might look like:
### Installing with `pip`

```
cd $HOME/src
git clone https://github.com/rustychris/stompy.git
export PYTHONPATH=$PYTHONPATH:$HOME/src/stompy
pip install stompy-ocean
```

At this point, you should be able to start python and successfully run `import stompy.grid.unstructured_grid`, for example.
### Installing with `mamba`/`conda`

Coming soon

## Documentation

See the [Documentation pages](https://stompy.readthedocs.io/en/latest/) for descriptions of the various modules.

## Issues

If you run into any bugs using `stompy`, you are encouraged to submit an [Issue](https://github.com/rustychris/stompy/issues) or PR to this repo.
47 changes: 47 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# pyproject.toml

[build-system]
requires = ["setuptools>=67.0.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "stompy-ocean"
version = "0.0.3"
description = "Various python modules related to modeling and oceanographic data analysis."
readme = {file = "README.md", content-type = "text/markdown"}
authors = [{ name = "Rusty Holleman", email = "[email protected]" }]
license = { file = "LICENSE" }
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
]
keywords = ["modeling", "oceanography", "grid"]
dependencies = ["cmocean",
"matplotlib",
"netCDF4",
"pandas",
"requests",
"scipy",
"Shapely",
"six",
"xarray",
"seawater",
"gdal<3.9",
"numpy<2.0",
"cgal"
]
requires-python = ">=3.0"

[tool.setuptools]
packages=['stompy', 'stompy.grid', 'stompy.io', 'stompy.io.local',
'stompy.model', 'stompy.model.delft', 'stompy.model.fvcom',
'stompy.model.pypart', 'stompy.model.suntans',
'stompy.plot', 'stompy.plot.cmaps',
'stompy.spatial']

[tool.setuptools.package-data]
stompy = ["tide_consts.txt"]

[project.urls]
Homepage = "https://github.com/rustychris/stompy"
2 changes: 0 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
cmocean
future
gdal
matplotlib
pyqt
netCDF4
numpy
pandas
Expand Down
77 changes: 57 additions & 20 deletions stompy/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def lowpass_gotin(data,in_t_days,*args,**kwargs):
return lowpass_godin(data,in_t_days,*args,**kwargs)

def lowpass_godin(data,in_t_days=None,ends='pass',
mean_dt_h = None,
mean_dt_h = None,axis=-1,
*args,**kwargs):
""" Approximate Godin's tidal filter
Note that in preserving the length of the dataset, the ends aren't really
Expand Down Expand Up @@ -80,22 +80,35 @@ def lowpass_godin(data,in_t_days=None,ends='pass',
A24 = np.ones(N24) / float(N24)
A25 = np.ones(N25) / float(N25)

if ends=='nan':
# Add nan at start/end, which will carry through
# the convolution to mark any samples affected
# by the ends
data=np.concatenate( ( [np.nan],data,[np.nan] ) )
data = np.convolve(data,A24,'same')
data = np.convolve(data,A24,'same')
data = np.convolve(data,A25,'same')

if ends=='nan':
data=data[1:-1]
# numpy convolve only takes 1D inputs.
# so iterate over any extra dimensions
iter_shape=list(data.shape)
del iter_shape[axis]
data=data.copy()
for idx in np.ndindex(*iter_shape):
expanded=list(idx)
expanded.insert(axis,slice(None))
expanded=tuple(expanded)
slc=data[expanded]

if ends=='nan':
# Add nan at start/end, which will carry through
# the convolution to mark any samples affected
# by the ends
slc=np.concatenate( ( [np.nan],slc,[np.nan] ) )
slc=np.convolve(slc,A24,'same')
slc=np.convolve(slc,A24,'same')
slc=np.convolve(slc,A25,'same')

if ends=='nan':
slc=slc[1:-1]
data[expanded]=slc

return data

def lowpass_fir(x,winsize,ignore_nan=True,axis=-1,mode='same',use_fft=False,
nan_weight_threshold=0.49999,window='hanning'):
nan_weight_threshold=0.49999,window='hanning',
dim='time'):
"""
In the absence of exact filtering needs, choose the window
size to match the cutoff period. Signals with a frequency corresponding to
Expand All @@ -115,6 +128,8 @@ def lowpass_fir(x,winsize,ignore_nan=True,axis=-1,mode='same',use_fft=False,
nan_weight_threshold: items with a weight less than this will be marked nan
the default value is slightly less than half, to avoid numerical roundoff
issues with 0.49999999 < 0.5
dim: if x is a xarray.DataArray, gives the dimension name along which to
filter.
N.B. In the case of an input with nan only at the beginning and end, to
guarantee that the output will have the same nans as the input winsize must
Expand All @@ -123,22 +138,39 @@ def lowpass_fir(x,winsize,ignore_nan=True,axis=-1,mode='same',use_fft=False,
# hanning windows have first/last elements==0.
# but it's counter-intuitive - so force a window with nonzero
# elements matching the requested size
import xarray as xr
if isinstance(winsize,np.ndarray):
win=winsize
winsize=len(win)
elif window=='hanning':
win=np.hanning(winsize+2)[1:-1]
elif window=='boxcar':
win=np.ones(winsize)
else:
raise Exception("window must be one of hanning, boxcar")
if isinstance(winsize, np.timedelta64):
assert isinstance(x,xr.DataArray)
dts=np.diff(x.time)
dts.sort()
dt=dts[len(dts)//2]
winsize=max(1,int(winsize / dt))

if window=='hanning':
win=np.hanning(winsize+2)[1:-1]
elif window=='boxcar':
win=np.ones(winsize)
else:
raise Exception("window must be one of hanning, boxcar")
win/=win.sum()

if use_fft:
convolve=scipy.signal.fftconvolve
else:
convolve=scipy.signal.convolve

if isinstance(x,xr.DataArray):
x_da=x
x=x_da.values
if dim is not None:
axis=x_da.get_axis_num(dim)
else:
x_da=None

slices=[None]*x.ndim
slices[axis]=slice(None)
win=win[tuple(slices)] # expand to get the right broadcasting
Expand All @@ -156,8 +188,13 @@ def lowpass_fir(x,winsize,ignore_nan=True,axis=-1,mode='same',use_fft=False,
result[has_weight] = result[has_weight] / weights[has_weight]
result[~has_weight]=0 # redundant, but in case of roundoff.
result[weights<nan_weight_threshold]=np.nan

return result

if x_da is not None:
result_da=x_da.copy()
result_da.values[...] = result
return result_da
else:
return result

# xarray time series versions:
def lowpass_xr(da,cutoff,**kw):
Expand Down
5 changes: 4 additions & 1 deletion stompy/grid/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ def format_list(cls):
lambda fmt,path: unstructured_grid.UnstructuredGrid.read_dfm(fn=path)]
ReadGrid.formats['sms']=['SMS grd',
lambda fmt,path: unstructured_grid.UnstructuredGrid.read_sms(path)]

ReadGrid.formats['ras2d']=(
['RAS2D h5', lambda fmt,path: unstructured_grid.UnstructuredGrid.read_ras2d(path,subedges='subedges') ]
)

class Dualify(Op):
def run(self,args):
g=stack.pop()
Expand Down
Loading

0 comments on commit cb60bb7

Please sign in to comment.