├── .github └── workflows │ └── publish-to-test-pypi.yml ├── .gitignore ├── .idea ├── .gitignore ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── modules.xml ├── stompy.iml └── vcs.xml ├── .readthedocs.yaml ├── LICENSE ├── MANIFEST ├── README.md ├── docs ├── .gitignore ├── Makefile ├── conf.py ├── images │ ├── composite-shp-table.png │ ├── composite-shp-zoom.png │ ├── composite-shp.png │ ├── dcc-dredged.png │ └── dcc-original.png ├── index.rst ├── make.bat ├── stompy.filters.rst ├── stompy.grid.rst ├── stompy.harm_decomp.rst ├── stompy.io.local.rst ├── stompy.io.rst ├── stompy.model.delft.rst ├── stompy.model.pypart.rst ├── stompy.model.rst ├── stompy.model.suntans.rst ├── stompy.nanpsd.rst ├── stompy.parse_interval.rst ├── stompy.plot.rst ├── stompy.priority_queue.rst ├── stompy.rst ├── stompy.scriptable.rst ├── stompy.spatial.rst ├── stompy.tidal_datum.rst ├── stompy.tide_consts.rst ├── stompy.undoer.rst ├── stompy.utils.rst ├── stompy.weighted_kde.rst └── stompy.xr_utils.rst ├── examples ├── .gitignore ├── Stream Quiver.ipynb ├── convert_sun_to_dfm.py ├── curvilinear_grids.py ├── dev_openfoam.py ├── dfm_dwaq │ ├── .gitignore │ ├── README.md │ ├── basic_test.py │ ├── dflow-template.mdu │ ├── local_config.py.in │ └── sample_output.png ├── dwaq_adjust_bc_fluxes.py ├── dwaq_data_handling.ipynb ├── dwaq_map_to_nc.py ├── filtering.ipynb ├── filtering.pdf ├── openfoam.ipynb ├── plot_dflow_map.ipynb ├── plot_dwaq_output.py ├── shp_lines_to_polygons.py ├── transects_0.ipynb └── unstructured_grid_plotting.ipynb ├── pyproject.toml ├── requirements-rtd.txt ├── requirements.txt ├── setup.py ├── stompy ├── .gitignore ├── __init__.py ├── filters.py ├── grid │ ├── __init__.py │ ├── cgal_line_walk.py │ ├── cli.py │ ├── depth_connectivity.py │ ├── exact_delaunay.py │ ├── extrude_openfoam.py │ ├── front.py │ ├── geom_types.py │ ├── gmsh_scale_helper.py │ ├── live_dt.py │ ├── merge_ugrid_subgrids.py │ ├── multi_ugrid.py │ ├── optimize_grid.py │ ├── orthogonalize.py │ ├── orthomaker.py │ ├── paver.py │ ├── paver_opt_mixin.py │ ├── quad_grower.py │ ├── quad_laplacian.py │ ├── quad_refine.py │ ├── rebay.py │ ├── sample_to_rectilinear.py │ ├── shadow_cdt.py │ ├── smoother.py │ ├── tom.py │ ├── topojson.py │ ├── triangulate_hole.py │ ├── trigrid.py │ ├── ugrid.py │ └── unstructured_grid.py ├── harm_decomp.py ├── io │ ├── __init__.py │ ├── dss.py │ ├── landxml.py │ ├── local │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── asos.py │ │ ├── cdec.py │ │ ├── cdip_mop.py │ │ ├── cimis.py │ │ ├── coamps.py │ │ ├── common.py │ │ ├── hycom.py │ │ ├── jodc_mooring.py │ │ ├── ndbc.py │ │ ├── noaa_coops.py │ │ ├── polaris.py.old │ │ ├── sfsu_wind.py │ │ ├── uk_tides.py │ │ ├── usgs_nwis.py │ │ ├── usgs_sfbay.py │ │ └── usgs_sfbay_station_locations.csv │ ├── match_datasets.py │ ├── nmea.py │ ├── qnc.py │ ├── rbr.py │ ├── rdb.py │ ├── rdb_codes.py │ ├── rdb_datadescriptors.py │ ├── rdradcp.py │ ├── rdradcp_xr.py │ ├── seaduck.py │ ├── sontek.py │ ├── usgs_parm_codes.tsv │ ├── vtk.py │ └── wocss │ │ ├── .gitignore │ │ ├── GSwoxB1.f │ │ ├── Makefile │ │ ├── NOTES │ │ ├── plots.py │ │ ├── raob_soundings25653.cdf │ │ ├── rundat.dat │ │ ├── terrain.dat │ │ └── winds.dat ├── memoize.py ├── model │ ├── __init__.py │ ├── data_comparison.py │ ├── delft │ │ ├── .gitignore │ │ ├── CODE_GUIDE.txt │ │ ├── __init__.py │ │ ├── custom_process.py │ │ ├── data │ │ │ ├── defaults-2021.03.mdu │ │ │ ├── defaults-2023.02.mdu │ │ │ ├── defaults-r53925.mdu │ │ │ ├── trim-tut_fti_waq.dat │ │ │ ├── trim-tut_fti_waq.def │ │ │ ├── tut_fti_waq-bal.his │ │ │ ├── tut_fti_waq.hda │ │ │ └── tut_fti_waq.hdf │ │ ├── dflow_grid.py │ │ ├── dflow_model.py │ │ ├── dfm_bc.py │ │ ├── dfm_grid.py │ │ ├── dfm_to_ptm.py │ │ ├── hydro_utils.py │ │ ├── io.py │ │ ├── map_merge.py │ │ ├── map_merge_ondemand.py │ │ ├── nefis.py │ │ ├── nefis_dev.py │ │ ├── nefis_nc.py │ │ ├── paradelft.py │ │ ├── process_diagram.py │ │ ├── splice_waq.py │ │ ├── sundwaq.py │ │ ├── test_agg.py │ │ ├── test_dflow_grid.py │ │ ├── test_nefis.py │ │ ├── test_waq_process.py │ │ ├── test_waq_scenario.py │ │ ├── waq_hydro_editor.py │ │ ├── waq_process.py │ │ ├── waq_scenario.py │ │ └── z_layer_aggregator.py │ ├── dem_to_csgraph.py │ ├── dsm2.py │ ├── fish_ptm │ │ ├── ptm_config.py │ │ ├── ptm_tools.py │ │ └── shp2polygons.py │ ├── fvcom │ │ ├── __init__.py │ │ └── fvcom.py │ ├── hydro_model.py │ ├── openfoam │ │ ├── cli.py │ │ ├── depth_average.py │ │ └── mesh_ops.py │ ├── otps │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── otps_model.py │ │ └── read_otps.py │ ├── pypart │ │ ├── __init__.py │ │ ├── basic.py │ │ └── ketefian.py │ ├── ras │ │ ├── ras.py │ │ └── result_reader.py │ ├── schism │ │ ├── data │ │ │ ├── param.nml │ │ │ └── sediment.nml │ │ └── schism_model.py │ ├── slurm_mixin.py │ ├── stream_tracer.py │ ├── suntans │ │ ├── __init__.py │ │ ├── data │ │ │ └── suntans.dat │ │ ├── depender.py │ │ ├── domain.py │ │ ├── forcing.py │ │ ├── store_file.py │ │ ├── sun_driver.py │ │ ├── sunreader.py │ │ ├── timeseries.py │ │ └── transect.py │ ├── unstructured_diffuser.py │ └── water_column.py ├── nanpsd.py ├── parse_interval.py ├── plot │ ├── __init__.py │ ├── cmap.py │ ├── cmaps │ │ ├── 001-fire.cpt │ │ ├── 00_bw_linear.cpt │ │ ├── BkBlAqGrYeOrReViWh200.cpt │ │ ├── BlueYellowRed.cpt │ │ ├── Blue_Cyan_Yellow.cpt │ │ ├── Blue_Yellow_CCW.cpt │ │ ├── DEM_screen.cpt │ │ ├── GMT_cyclic.cpt │ │ ├── GMT_drywet.cpt │ │ ├── Primary_04.cpt │ │ ├── RdYlGn.ggr │ │ ├── Spectral.ggr │ │ ├── Split_Complementary_01a.cpt │ │ ├── Split_Complementary_07.cpt │ │ ├── StepSeq25.cpt │ │ ├── Tertiary_01b.cpt │ │ ├── ViBlGrWhYeOrRe.cpt │ │ ├── WhViBlGrYeOrReWh.cpt │ │ ├── YlOrRd_09.ggr │ │ ├── __init__.py │ │ ├── arctic.cpt │ │ ├── aspectcolr.cpt │ │ ├── bath_114.cpt │ │ ├── brightsong2.cpt │ │ ├── butterflyfairy.cpt │ │ ├── caleblack.cpt │ │ ├── cbcSpectral.cpt │ │ ├── cequal.cpt │ │ ├── colormaps.png │ │ ├── cyanotype_01.cpt │ │ ├── diverging.cpt │ │ ├── dkbluered.cpt │ │ ├── es_platinum_06.cpt │ │ ├── f-30-31-32.cpt │ │ ├── haxby.cpt │ │ ├── hot_and_cold.cpt │ │ ├── hot_desaturated.cpt │ │ ├── iris1.cpt │ │ ├── ncview_banded.cpt │ │ ├── nonlinear_gray01.ggr │ │ ├── nrl_sirkes.cpt │ │ ├── nrl_sirkes_nowhite.cpt │ │ ├── oc-sst.cpt │ │ ├── parrot.cpt │ │ ├── pm3d14.cpt │ │ ├── poison.cpt │ │ ├── saga-01.cpt │ │ ├── saga-02.cpt │ │ ├── saga-13.cpt │ │ ├── saga-16.cpt │ │ ├── salinity_bgy1.ggr │ │ ├── salinity_bgy2.ggr │ │ ├── salinity_bgy3.ggr │ │ ├── scoutie.cpt │ │ ├── slyphide.cpt │ │ ├── srtGreys05.cpt │ │ ├── temperature.cpt │ │ ├── turbo.cpt │ │ └── wave.cpt │ ├── contour_transect.py │ ├── ggr.py │ ├── mkanim.py │ ├── mpl15.py │ ├── nbviz.py │ ├── plot_utils.py │ ├── plot_wkb.py │ └── stream_quiver.py ├── priority_queue.py ├── scriptable.py ├── spatial │ ├── __init__.py │ ├── algorithms.py │ ├── constrained_delaunay.py │ ├── field.py │ ├── gen_spatial_index.py │ ├── generate_dem.py │ ├── geom_types.py │ ├── interpXYZ.py │ ├── interp_4d.py │ ├── interp_concentric.py │ ├── interp_coverage.py │ ├── interp_nn.py │ ├── interp_orthogonal.py │ ├── join_features.py │ ├── kdtree_spatialindex.py │ ├── linestring_utils.py │ ├── medial_axis.py │ ├── proj_utils.py │ ├── qgis_spatialindex.py │ ├── rasterize_stl.py │ ├── robust_predicates.py │ └── wkb2shp.py ├── tidal_datum.py ├── tide_consts.py ├── tide_consts.txt ├── undoer.py ├── utils.py ├── weighted_kde.py ├── xr_transect.py └── xr_utils.py └── test ├── .gitignore ├── data ├── .gitignore ├── Ex1.g01.hdf ├── WET.grd ├── WET_DEM.dep ├── coyote.rdb ├── dem_sources.dbf ├── dem_sources.prj ├── dem_sources.qpj ├── dem_sources.shp ├── dem_sources.shx ├── depth.xyz ├── dumbarton.dbf ├── dumbarton.prj ├── dumbarton.shp ├── dumbarton.shx ├── lsb_combined_v14_net.nc ├── scale-lines.dbf ├── scale-lines.prj ├── scale-lines.qpj ├── scale-lines.shp ├── scale-lines.shx ├── scale.dbf ├── scale.prj ├── scale.qpj ├── scale.shp ├── scale.shx ├── section_hydro.txt ├── sfbay │ ├── cells.dat │ ├── edges.dat │ └── points.dat ├── temp-transect.nc └── test_rdb.rdb ├── dev_cgal_shadow.py ├── dev_cost.py ├── dev_crash.py ├── dev_dfm_to_ptm.py ├── dev_front.py ├── dev_grayscott.py ├── dev_mpi_ugrid.py ├── dev_sms.py ├── dev_tidal_fill.py ├── dev_waq_hydro_editor.py ├── dflow-template.mdu ├── test_cgal_line_walk.py ├── test_cgal_triangulation.py ├── test_cimis.py ├── test_cmap.py ├── test_coamps.py ├── test_constrained_delaunay.py ├── test_dfm.py ├── test_dfm_grid.py ├── test_dfm_to_ptm.py ├── test_exact_delaunay.py ├── test_exact_delaunay2.py ├── test_field.py ├── test_filters.py ├── test_front.py ├── test_front_speed.py ├── test_gen_spatial_index.py ├── test_harm_decomp.py ├── test_lag.py ├── test_live_dt.py ├── test_merge_grids.py ├── test_noaa_coops.py ├── test_otps.py ├── test_paver.py ├── test_paver_purepy.py ├── test_py_apollonius.py ├── test_quad_front.py ├── test_rbr.py ├── test_rdb.py ├── test_read_ras.py ├── test_shadow_cdt.py ├── test_tom.sh ├── test_transect.py ├── test_transect_contour.py ├── test_trigrid.py ├── test_uk_tides.py ├── test_unstructured_grid.py ├── test_unstructured_grid_plot.py ├── test_usgs_nwis.py ├── test_utils.py ├── test_waq_hydro_editor.py ├── test_waq_scenario.py ├── test_wkb2shp.py └── test_xr_transect.py /.github/workflows/publish-to-test-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python distribution to PyPI and TestPyPI 2 | 3 | on: push 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | dist 3 | build 4 | *~ 5 | dev 6 | .ipynb_checkpoints 7 | /stompy_ocean.egg-info/ 8 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/stompy.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | #build: 4 | # tools: 5 | 6 | 7 | sphinx: 8 | configuration: docs/conf.py 9 | 10 | python: 11 | install: 12 | - requirements-rtd.txt 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Rusty Holleman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | setup.py 3 | stompy/__init__.py 4 | stompy/filters.py 5 | stompy/harm_decomp.py 6 | stompy/nanpsd.py 7 | stompy/parse_interval.py 8 | stompy/priority_queue.py 9 | stompy/scriptable.py 10 | stompy/tidal_datum.py 11 | stompy/tide_consts.py 12 | stompy/undoer.py 13 | stompy/utils.py 14 | stompy/weighted_kde.py 15 | stompy/xr_utils.py 16 | stompy/grid/__init__.py 17 | stompy/grid/depth_connectivity.py 18 | stompy/grid/exact_delaunay.py 19 | stompy/grid/front.py 20 | stompy/grid/merge_ugrid_subgrids.py 21 | stompy/grid/orthogonalize.py 22 | stompy/grid/orthomaker.py 23 | stompy/grid/paver.py 24 | stompy/grid/tom.py 25 | stompy/grid/trigrid.py 26 | stompy/grid/ugrid.py 27 | stompy/grid/unstructured_grid.py 28 | stompy/io/__init__.py 29 | stompy/io/match_datasets.py 30 | stompy/io/qnc.py 31 | stompy/io/rbr.py 32 | stompy/io/rdb.py 33 | stompy/io/rdb_codes.py 34 | stompy/io/rdb_datadescriptors.py 35 | stompy/io/rdradcp.py 36 | stompy/io/local/__init__.py 37 | stompy/io/local/noaa_coops.py 38 | stompy/io/local/usgs_sfbay.py 39 | stompy/model/__init__.py 40 | stompy/model/unstructured_diffuser.py 41 | stompy/model/delft/__init__.py 42 | stompy/model/delft/dflow_grid.py 43 | stompy/model/delft/dfm_grid.py 44 | stompy/model/delft/hydro_utils.py 45 | stompy/model/delft/io.py 46 | stompy/model/delft/nefis.py 47 | stompy/model/delft/nefis_dev.py 48 | stompy/model/delft/nefis_nc.py 49 | stompy/model/delft/paradelft.py 50 | stompy/model/delft/paraview_prof.py 51 | stompy/model/delft/process_diagram.py 52 | stompy/model/delft/splice_waq.py 53 | stompy/model/delft/sundwaq.py 54 | stompy/model/delft/test_agg.py 55 | stompy/model/delft/test_dflow_grid.py 56 | stompy/model/delft/test_nefis.py 57 | stompy/model/delft/test_waq_process.py 58 | stompy/model/delft/test_waq_scenario.py 59 | stompy/model/delft/waq_process.py 60 | stompy/model/delft/waq_scenario.py 61 | stompy/model/delft/z_layer_aggregator.py 62 | stompy/model/fvcom/__init__.py 63 | stompy/model/fvcom/fvcom.py 64 | stompy/model/pypart/__init__.py 65 | stompy/model/pypart/basic.py 66 | stompy/model/pypart/ketefian.py 67 | stompy/model/suntans/__init__.py 68 | stompy/model/suntans/domain.py 69 | stompy/model/suntans/forcing.py 70 | stompy/model/suntans/sunreader.py 71 | stompy/model/suntans/transect.py 72 | stompy/plot/__init__.py 73 | stompy/plot/cmap.py 74 | stompy/plot/mpl15.py 75 | stompy/plot/plot_utils.py 76 | stompy/plot/plot_wkb.py 77 | stompy/plot/cmaps/__init__.py 78 | stompy/spatial/__init__.py 79 | stompy/spatial/algorithms.py 80 | stompy/spatial/field.py 81 | stompy/spatial/gen_spatial_index.py 82 | stompy/spatial/geom_types.py 83 | stompy/spatial/interp_4d.py 84 | stompy/spatial/interp_coverage.py 85 | stompy/spatial/join_features.py 86 | stompy/spatial/kdtree_spatialindex.py 87 | stompy/spatial/linestring_utils.py 88 | stompy/spatial/proj_utils.py 89 | stompy/spatial/qgis_spatialindex.py 90 | stompy/spatial/robust_predicates.py 91 | stompy/spatial/wkb2shp.py 92 | test/test_cmap.py 93 | test/test_dfm_grid.py 94 | test/test_exact_delaunay.py 95 | test/test_field.py 96 | test/test_front.py 97 | test/test_gen_spatial_index.py 98 | test/test_harm_decomp.py 99 | test/test_merge_grids.py 100 | test/test_quad_front.py 101 | test/test_rbr.py 102 | test/test_rdb.py 103 | test/test_transect.py 104 | test/test_trigrid.py 105 | test/test_unstructured_grid.py 106 | test/test_waq_scenario.py 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stompy: spatial tools for ocean modeling in python 2 | 3 | Various python modules related to modeling and oceanographic data analysis. 4 | 5 | ## Prerequisites 6 | 7 | `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: 8 | 9 | ``` 10 | mamba create -n python "gdal<3.9" "numpy<2.0" 11 | mamba activate 12 | ``` 13 | 14 | ## Installation 15 | 16 | 17 | ### Installing with `pip` 18 | 19 | ``` 20 | pip install stompy-ocean 21 | ``` 22 | 23 | ### Installing with `mamba`/`conda` 24 | 25 | Coming soon 26 | 27 | ## Documentation 28 | 29 | See the [Documentation pages](https://stompy.readthedocs.io/en/latest/) for descriptions of the various modules. 30 | 31 | ## Issues 32 | 33 | 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. 34 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /docs/images/composite-shp-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/docs/images/composite-shp-table.png -------------------------------------------------------------------------------- /docs/images/composite-shp-zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/docs/images/composite-shp-zoom.png -------------------------------------------------------------------------------- /docs/images/composite-shp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/docs/images/composite-shp.png -------------------------------------------------------------------------------- /docs/images/dcc-dredged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/docs/images/dcc-dredged.png -------------------------------------------------------------------------------- /docs/images/dcc-original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/docs/images/dcc-original.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. stompy documentation master file, created by 2 | sphinx-quickstart on Mon Jun 19 14:24:20 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | stompy 7 | ====== 8 | 9 | Spatial Tools for Ocean Modeling in Python 10 | 11 | A collection of tools related to oceanographic data processing with an 12 | emphasis on modeling. 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | :caption: Contents: 17 | 18 | stompy 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | -------------------------------------------------------------------------------- /docs/stompy.filters.rst: -------------------------------------------------------------------------------- 1 | stompy\.filters — Flavors of lowpass filters 2 | ======================= 3 | 4 | .. automodule:: stompy.filters 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/stompy.grid.rst: -------------------------------------------------------------------------------- 1 | stompy\.grid — Grid reading, writing and manipulating 2 | ==================== 3 | 4 | Modules for generating, converting, plotting, and editing 5 | unstructured grids as used in hydrodynamic models (e.g. 6 | UnTRIM, SUNTANS, DFlow-FM). 7 | 8 | For generating grids, the only stable and robust code for 9 | this is in `tom.py` (triangular orthogonal mesher) and `paver.py`. 10 | `tom.py` is a command line interface to `paver.py`. A simple 11 | example of calling tom is in `stompy/tests/test_tom.sh`, and 12 | invoking `tom.py -h` will show the other options available. 13 | 14 | For most other grid-related tasks, the best module to use 15 | is `unstructured_grid.py`, as it supports non-triangular meshes 16 | (such as mixed triangles/quads), and is actively developed. 17 | Grid generation methods built on unstructured_grid.py are 18 | in front.py, but these are not stable and generally should not 19 | be used. 20 | 21 | Note that for any significant amount of grid modification, the 22 | CGAL python bindings are essential. A backup pure python 23 | implementation is included, but will be orders of magnitude 24 | slower and likely less robust numerically. Watch for error 25 | messages near the start of using tom.py to see whether there 26 | are issues loading CGAL. 27 | 28 | Submodules 29 | ---------- 30 | 31 | stompy\.grid\.depth\_connectivity module 32 | ---------------------------------------- 33 | 34 | .. automodule:: stompy.grid.depth_connectivity 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | stompy\.grid\.exact\_delaunay module 40 | ------------------------------------ 41 | 42 | .. automodule:: stompy.grid.exact_delaunay 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | stompy\.grid\.front module 48 | -------------------------- 49 | 50 | .. automodule:: stompy.grid.front 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | stompy\.grid\.merge\_ugrid\_subgrids module 56 | ------------------------------------------- 57 | 58 | .. automodule:: stompy.grid.merge_ugrid_subgrids 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | stompy\.grid\.orthogonalize module 64 | ---------------------------------- 65 | 66 | .. automodule:: stompy.grid.orthogonalize 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | stompy\.grid\.orthomaker module 72 | ------------------------------- 73 | 74 | .. automodule:: stompy.grid.orthomaker 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | stompy\.grid\.paver module 80 | -------------------------- 81 | 82 | .. automodule:: stompy.grid.paver 83 | :members: 84 | :undoc-members: 85 | :show-inheritance: 86 | 87 | stompy\.grid\.tom module 88 | ------------------------ 89 | 90 | .. automodule:: stompy.grid.tom 91 | :members: 92 | :undoc-members: 93 | :show-inheritance: 94 | 95 | stompy\.grid\.trigrid module 96 | ---------------------------- 97 | 98 | .. automodule:: stompy.grid.trigrid 99 | :members: 100 | :undoc-members: 101 | :show-inheritance: 102 | 103 | stompy\.grid\.ugrid module 104 | -------------------------- 105 | 106 | .. automodule:: stompy.grid.ugrid 107 | :members: 108 | :undoc-members: 109 | :show-inheritance: 110 | 111 | stompy\.grid\.unstructured\_grid module 112 | --------------------------------------- 113 | 114 | .. automodule:: stompy.grid.unstructured_grid 115 | :members: 116 | :undoc-members: 117 | :show-inheritance: 118 | 119 | 120 | Module contents 121 | --------------- 122 | 123 | .. automodule:: stompy.grid 124 | :members: 125 | :undoc-members: 126 | :show-inheritance: 127 | -------------------------------------------------------------------------------- /docs/stompy.harm_decomp.rst: -------------------------------------------------------------------------------- 1 | stompy\.harm\_decomp — Harmonic decomposition and recomposition 2 | --------------------------- 3 | 4 | .. automodule:: stompy.harm_decomp 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/stompy.io.local.rst: -------------------------------------------------------------------------------- 1 | stompy\.io\.local — Methods for regional and local data sources 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | stompy\.io\.local\.noaa\_coops module 8 | ------------------------------------- 9 | 10 | .. automodule:: stompy.io.local.noaa_coops 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | stompy\.io\.local\.usgs\_sfbay module 16 | ------------------------------------- 17 | 18 | .. automodule:: stompy.io.local.usgs_sfbay 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: stompy.io.local 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/stompy.io.rst: -------------------------------------------------------------------------------- 1 | stompy\.io — Reading and writing of various formats and data sources 2 | ================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | stompy.io.local 10 | 11 | Submodules 12 | ---------- 13 | 14 | stompy\.io\.match\_datasets module 15 | ---------------------------------- 16 | 17 | .. automodule:: stompy.io.match_datasets 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | 22 | stompy\.io\.qnc module 23 | ---------------------- 24 | 25 | .. automodule:: stompy.io.qnc 26 | :members: 27 | :undoc-members: 28 | :show-inheritance: 29 | 30 | stompy\.io\.rbr module 31 | ---------------------- 32 | 33 | .. automodule:: stompy.io.rbr 34 | :members: 35 | :undoc-members: 36 | :show-inheritance: 37 | 38 | stompy\.io\.rdb module 39 | ---------------------- 40 | 41 | .. automodule:: stompy.io.rdb 42 | :members: 43 | :undoc-members: 44 | :show-inheritance: 45 | 46 | stompy\.io\.rdb\_codes module 47 | ----------------------------- 48 | 49 | .. automodule:: stompy.io.rdb_codes 50 | :members: 51 | :undoc-members: 52 | :show-inheritance: 53 | 54 | stompy\.io\.rdb\_datadescriptors module 55 | --------------------------------------- 56 | 57 | .. automodule:: stompy.io.rdb_datadescriptors 58 | :members: 59 | :undoc-members: 60 | :show-inheritance: 61 | 62 | stompy\.io\.rdradcp module 63 | -------------------------- 64 | 65 | .. automodule:: stompy.io.rdradcp 66 | :members: 67 | :undoc-members: 68 | :show-inheritance: 69 | 70 | 71 | Module contents 72 | --------------- 73 | 74 | .. automodule:: stompy.io 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | -------------------------------------------------------------------------------- /docs/stompy.model.pypart.rst: -------------------------------------------------------------------------------- 1 | stompy\.model\.pypart — Python-based particle tracking 2 | ============================= 3 | 4 | Alpha. 5 | 6 | Submodules 7 | ---------- 8 | 9 | stompy\.model\.pypart\.basic module 10 | ----------------------------------- 11 | 12 | .. automodule:: stompy.model.pypart.basic 13 | :members: 14 | :undoc-members: 15 | :show-inheritance: 16 | 17 | stompy\.model\.pypart\.ketefian module 18 | -------------------------------------- 19 | 20 | .. automodule:: stompy.model.pypart.ketefian 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | 26 | Module contents 27 | --------------- 28 | 29 | .. automodule:: stompy.model.pypart 30 | :members: 31 | :undoc-members: 32 | :show-inheritance: 33 | -------------------------------------------------------------------------------- /docs/stompy.model.rst: -------------------------------------------------------------------------------- 1 | stompy\.model — Utilities and interfaces to numerical models 2 | ===================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | stompy.model.delft 10 | stompy.model.pypart 11 | stompy.model.suntans 12 | 13 | Submodules 14 | ---------- 15 | 16 | stompy\.model\.unstructured\_diffuser module 17 | -------------------------------------------- 18 | 19 | .. automodule:: stompy.model.unstructured_diffuser 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | 24 | 25 | Module contents 26 | --------------- 27 | 28 | .. automodule:: stompy.model 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | -------------------------------------------------------------------------------- /docs/stompy.model.suntans.rst: -------------------------------------------------------------------------------- 1 | stompy\.model\.suntans — Tools and interface for SUNTANS hydrodynamics 2 | ============================== 3 | 4 | N.B.: `Matt Rayson's tools `, are likely more general 5 | purpose and more widely used. 6 | 7 | Submodules 8 | ---------- 9 | 10 | stompy\.model\.suntans\.domain module 11 | ------------------------------------- 12 | 13 | .. automodule:: stompy.model.suntans.domain 14 | :members: 15 | :undoc-members: 16 | :show-inheritance: 17 | 18 | stompy\.model\.suntans\.forcing module 19 | -------------------------------------- 20 | 21 | .. automodule:: stompy.model.suntans.forcing 22 | :members: 23 | :undoc-members: 24 | :show-inheritance: 25 | 26 | stompy\.model\.suntans\.sunreader module 27 | ---------------------------------------- 28 | 29 | .. automodule:: stompy.model.suntans.sunreader 30 | :members: 31 | :undoc-members: 32 | :show-inheritance: 33 | 34 | stompy\.model\.suntans\.transect module 35 | --------------------------------------- 36 | 37 | .. automodule:: stompy.model.suntans.transect 38 | :members: 39 | :undoc-members: 40 | :show-inheritance: 41 | 42 | 43 | Module contents 44 | --------------- 45 | 46 | .. automodule:: stompy.model.suntans 47 | :members: 48 | :undoc-members: 49 | :show-inheritance: 50 | -------------------------------------------------------------------------------- /docs/stompy.nanpsd.rst: -------------------------------------------------------------------------------- 1 | stompy\.nanpsd — A nan-aware power spectral density method 2 | --------------------- 3 | 4 | Uses the correlogram to avoid the widespread nan-contamination of 5 | an FFT. 6 | 7 | .. automodule:: stompy.nanpsd 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/stompy.parse_interval.rst: -------------------------------------------------------------------------------- 1 | stompy\.parse\_interval — Flexible ways of parsing time intervals 2 | ------------------------------ 3 | 4 | .. automodule:: stompy.parse_interval 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/stompy.plot.rst: -------------------------------------------------------------------------------- 1 | stompy\.plot — Plotting utilities and enhancements 2 | ==================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | stompy\.plot\.cmap module 8 | ------------------------- 9 | 10 | .. automodule:: stompy.plot.cmap 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | stompy\.plot\.plot\_utils module 16 | -------------------------------- 17 | 18 | .. automodule:: stompy.plot.plot_utils 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | stompy\.plot\.plot\_wkb module 24 | ------------------------------ 25 | 26 | .. automodule:: stompy.plot.plot_wkb 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: stompy.plot 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /docs/stompy.priority_queue.rst: -------------------------------------------------------------------------------- 1 | stompy\.priority\_queue — Python implementation of priority queue data structure 2 | ------------------------------ 3 | 4 | .. automodule:: stompy.priority_queue 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/stompy.rst: -------------------------------------------------------------------------------- 1 | stompy 2 | ====== 3 | 4 | Spatial Tools for Ocean Modeling in Python 5 | 6 | A collection of tools related to oceanographic data processing with an 7 | emphasis on modeling. 8 | 9 | 10 | Subpackages 11 | ----------- 12 | 13 | .. toctree:: 14 | :maxdepth: 1 15 | 16 | stompy.grid 17 | stompy.io 18 | stompy.model 19 | stompy.plot 20 | stompy.spatial 21 | 22 | Submodules 23 | ---------- 24 | 25 | .. toctree:: 26 | :maxdepth: 1 27 | 28 | stompy.filters 29 | stompy.harm_decomp 30 | stompy.nanpsd 31 | stompy.parse_interval 32 | stompy.priority_queue 33 | stompy.scriptable 34 | stompy.tidal_datum 35 | stompy.tidal_consts 36 | stompy.undoer 37 | stompy.utils 38 | stompy.weighted_kde 39 | stompy.xr_utils 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/stompy.scriptable.rst: -------------------------------------------------------------------------------- 1 | stompy\.scriptable — Simple superclass for exposing command-line accessible methods 2 | ------------------------- 3 | 4 | .. automodule:: stompy.scriptable 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/stompy.tidal_datum.rst: -------------------------------------------------------------------------------- 1 | stompy\.tidal\_datum — Methods for extracting tidal datums from timeseries 2 | --------------------------- 3 | 4 | .. automodule:: stompy.tidal_datum 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | -------------------------------------------------------------------------------- /docs/stompy.tide_consts.rst: -------------------------------------------------------------------------------- 1 | stompy\.tide\_consts — Constants related to tidal constituents 2 | --------------------------- 3 | 4 | .. automodule:: stompy.tide_consts 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | -------------------------------------------------------------------------------- /docs/stompy.undoer.rst: -------------------------------------------------------------------------------- 1 | stompy\.undoer — Framework for handling undo history 2 | --------------------- 3 | 4 | Used for grid editing in order to unwind changes to a grid. 5 | 6 | .. automodule:: stompy.undoer 7 | :members: 8 | :undoc-members: 9 | :show-inheritance: 10 | 11 | -------------------------------------------------------------------------------- /docs/stompy.utils.rst: -------------------------------------------------------------------------------- 1 | stompy\.utils — General utilities 2 | -------------------- 3 | 4 | .. automodule:: stompy.utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/stompy.weighted_kde.rst: -------------------------------------------------------------------------------- 1 | stompy\.weighted\_kde — Kernel Density Estimate with weights 2 | ---------------------------- 3 | 4 | .. automodule:: stompy.weighted_kde 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | -------------------------------------------------------------------------------- /docs/stompy.xr_utils.rst: -------------------------------------------------------------------------------- 1 | stompy\.xr\_utils — xarray-related utilities 2 | -------------------------------------------- 3 | 4 | .. automodule:: stompy.xr_utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | -------------------------------------------------------------------------------- /examples/convert_sun_to_dfm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Read a suntans grid in the current directory and write a DFM grid, output_net.nc 5 | """ 6 | 7 | from stompy.grid import unstructured_grid 8 | from stompy.model.delft import dfm_grid 9 | 10 | ug=unstructured_grid.SuntansGrid(".") 11 | 12 | dfm_grid.write_dfm(ug,"output_net.nc") 13 | -------------------------------------------------------------------------------- /examples/curvilinear_grids.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from stompy.grid import front, unstructured_grid 4 | from stompy.plot import plot_wkb 5 | from stompy.spatial import linestring_utils 6 | from stompy import filters 7 | from shapely import geometry 8 | 9 | ## 10 | 11 | # define the domain 12 | 13 | 14 | s=np.linspace(0,6000,200) # along-channel coordinate 15 | amp=700 # amplitude of meanders 16 | lamb=4000 # wave-length of meanders 17 | width=500 # mean channel width 18 | noise_w=50 # amplitude of noise to add to the channel banks 19 | noise_l=1500 # length-scale of noise 20 | 21 | centerline=np.c_[ s, amp*np.cos(2*np.pi*s/lamb)] 22 | pline=geometry.LineString(centerline) 23 | channel=pline.buffer(width/2) 24 | ring=np.array(channel.exterior) 25 | ring_norm=linestring_utils.left_normals(ring) 26 | 27 | noise=(np.random.random(len(ring_norm))-0.5) 28 | winsize=int( noise_l/( channel.exterior.length/len(ring_norm) ) ) 29 | noise[:winsize]=0 # so the ends still match up 30 | noise[-winsize:]=0 31 | noise_lp=filters.lowpass_fir(noise,winsize) 32 | noise_lp *= noise_w/np.sqrt(np.mean(noise_lp**2)) 33 | 34 | # domain boundary including the random noise 35 | ring_noise=ring+noise_lp[:,None]*ring_norm 36 | 37 | # Create the curvilinear section 38 | thalweg=centerline[50:110] 39 | 40 | plt.figure(1).clf() 41 | plt.plot(centerline[:,0], 42 | centerline[:,1], 43 | 'k-',zorder=2) 44 | plt.axis('equal') 45 | 46 | plot_wkb.plot_wkb(channel,zorder=-2) 47 | plt.plot(ring_noise[:,0], 48 | ring_noise[:,1], 49 | 'm-') 50 | 51 | plt.plot(thalweg[:,0],thalweg[:,1],'r--',lw=3) 52 | 53 | ## 54 | 55 | # First, just the curvilinear section: 56 | g=unstructured_grid.UnstructuredGrid(max_sides=4) 57 | 58 | thalweg_resamp=linestring_utils.resample_linearring(thalweg,50,closed_ring=0) 59 | 60 | g.add_rectilinear_on_line(thalweg_resamp, 61 | profile=lambda x,s,perp: np.linspace(-200,200,20), 62 | add_streamwise=False) 63 | g.plot_edges(zorder=5,color='y') 64 | -------------------------------------------------------------------------------- /examples/dfm_dwaq/.gitignore: -------------------------------------------------------------------------------- 1 | local_config.py 2 | dfm_run 3 | -------------------------------------------------------------------------------- /examples/dfm_dwaq/README.md: -------------------------------------------------------------------------------- 1 | This directory shows an example of scripting the creation and execution of a very 2 | simple DFlowFM simulation, and a subsequent scalar simulation using DWAQ. 3 | 4 | The hydrodynamic model is a 500m square grid with a resolution of 10m. The southern 5 | end of the western boundary has a periodic flow. It runs for 48 hours. 6 | 7 | The D-WAQ tracer run includes three tracers: 8 | 9 | - dye1 is initialized as a Gaussian blob in the middle of the domain, and 0 on the flow boundary 10 | - unity is initalized as 1.0, with 0.0 on the flow boundary. 11 | - boundary_dye is initialized as 0.0, with 1.0 on the flow boundary. 12 | 13 | At the end of the simulation the three tracers look like: 14 | 15 | ![sample output](sample_output.png) 16 | 17 | Relevant files: 18 | 19 | `basic_test.py`: The full script 20 | 21 | `local_config.py.in`: Copy this to local_config.py, and edit to set paths to dflowfm, delwaq1, and delwaq2. 22 | 23 | `dflow-template.mdu`: Template values for configuring the DFM run. These can be overwritten in the script, 24 | but for parameters that don't need to change programmatically put them here. 25 | 26 | `dfm_run`: The full DFM output is not in the repository, but some of the key files are here for perusing 27 | without having to run the script. 28 | 29 | `dwaq_run`: The full DWAQ output not in the repository, but some of the key files are here for perusing 30 | without having to run the script. 31 | -------------------------------------------------------------------------------- /examples/dfm_dwaq/local_config.py.in: -------------------------------------------------------------------------------- 1 | import stompy.model.delft.waq_scenario as dwaq 2 | import stompy.model.delft.dflow_model as dfm 3 | 4 | import os 5 | 6 | DELFT_SRC="/home/rusty/src/dfm/1.5.4/lnx64" 7 | DELFT_SHARE=os.path.join(DELFT_SRC,"share","delft3d") 8 | DELFT_LIB=os.path.join(DELFT_SRC,"lib") 9 | 10 | def install(): 11 | os.environ['DELFT_SRC']=DELFT_SRC 12 | os.environ['DELFT_SHARE']=DELFT_SHARE 13 | # not great - really should check what's already in there. 14 | os.environ['LD_LIBRARY_PATH']=DELFT_LIB 15 | 16 | dfm_bin_dir=os.path.join(os.environ['DELFT_SRC'],"bin") 17 | dfm.DFlowModel.dfm_bin_dir=dfm_bin_dir 18 | dfm.DFlowModel.mpi_bin_dir=dfm_bin_dir 19 | 20 | -------------------------------------------------------------------------------- /examples/dfm_dwaq/sample_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/examples/dfm_dwaq/sample_output.png -------------------------------------------------------------------------------- /examples/dwaq_adjust_bc_fluxes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Command-line tool to infer missing boundary fluxes, and update them 4 | in a flow file. 5 | 6 | This applies to non-flow BCs in DFM, i.e. sea boundary conditions and 7 | discharges. 8 | 9 | """ 10 | from __future__ import print_function 11 | 12 | import argparse 13 | import sys,os 14 | import numpy as np 15 | 16 | import stompy.model.delft.waq_scenario as waq 17 | 18 | parser = argparse.ArgumentParser(description='Adjust DFM D-WAQ output to add missing BC fluxes.') 19 | 20 | parser.add_argument('hyd_fn', metavar='dfm_out.hyd', type=str, 21 | help='path to hyd file') 22 | 23 | args = parser.parse_args() 24 | 25 | hyd_fn=args.hyd_fn 26 | 27 | print("Opening hydro from %s"%hyd_fn) 28 | hydro=waq.HydroFiles(hyd_fn) 29 | 30 | print("Adjusting flows, updating %s in place"%hydro.get_path('flows-file')) 31 | 32 | hydro.adjust_boundaries_for_conservation() 33 | 34 | print("Done") 35 | -------------------------------------------------------------------------------- /examples/dwaq_map_to_nc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Command-line tool to convert a binary map output to netcdf. 4 | """ 5 | from __future__ import print_function 6 | 7 | import argparse 8 | import sys,os 9 | import numpy as np 10 | 11 | import stompy.model.delft.io as dio 12 | 13 | parser = argparse.ArgumentParser(description='Convert D-WAQ binary map output to NetCDF.') 14 | 15 | parser.add_argument('map_fn', metavar='somefile.map', type=str, 16 | help='path to map file output') 17 | parser.add_argument('hyd_fn', metavar='other.hyd', type=str, 18 | help='path to hyd file') 19 | parser.add_argument('--totaldepth',default='TotalDepth', 20 | help='output variable to use as total depth. none to disable sigma coordinate') 21 | 22 | args = parser.parse_args() 23 | # DBG args=parser.parse_args(['--totaldepth','none',"wy2011.map","com-wy2011.hyd"]) 24 | 25 | map_fn=args.map_fn 26 | hyd_fn=args.hyd_fn 27 | 28 | 29 | output_fn=map_fn.replace('.map','.nc') 30 | if os.path.exists(output_fn): 31 | print("Output file '%s' exists. Aborting"%output_fn) 32 | sys.exit(1) 33 | 34 | print("Reading map data and grid") 35 | map_ds=dio.read_map(map_fn,hyd_fn) 36 | 37 | if args.totaldepth != 'none': 38 | total_depth=args.totaldepth 39 | 40 | print("Adding minor metadata") 41 | 42 | if total_depth not in map_ds: 43 | print("Fabricating a total-depth variable to allow ugrid-ish output") 44 | map_ds[total_depth]=('time','layer','face'),np.ones( (len(map_ds.time), 45 | len(map_ds.layer), 46 | len(map_ds.face)), '=67.0.0", "wheel"] 5 | build-backend = "setuptools.build_meta" 6 | 7 | [project] 8 | name = "stompy-ocean" 9 | version = "0.0.5" 10 | description = "Various python modules related to modeling and oceanographic data analysis." 11 | readme = {file = "README.md", content-type = "text/markdown"} 12 | authors = [{ name = "Rusty Holleman", email = "rustychris@gmail.com" }] 13 | license = { file = "LICENSE" } 14 | classifiers = [ 15 | "License :: OSI Approved :: MIT License", 16 | "Programming Language :: Python", 17 | "Programming Language :: Python :: 3", 18 | ] 19 | keywords = ["modeling", "oceanography", "grid"] 20 | dependencies = ["cmocean", 21 | "matplotlib", 22 | "netCDF4", 23 | "pandas", 24 | "requests", 25 | "scipy", 26 | "Shapely", 27 | "six", 28 | "xarray", 29 | "seawater", 30 | "gdal<3.9", 31 | "numpy<2.0", 32 | "cgal" 33 | ] 34 | requires-python = ">=3.0" 35 | 36 | [tool.setuptools] 37 | packages=['stompy', 'stompy.grid', 'stompy.io', 'stompy.io.local', 38 | 'stompy.model', 'stompy.model.delft', 'stompy.model.fvcom', 39 | 'stompy.model.pypart', 'stompy.model.suntans', 40 | 'stompy.plot', 'stompy.plot.cmaps', 41 | 'stompy.spatial'] 42 | 43 | [tool.setuptools.package-data] 44 | stompy = ["tide_consts.txt"] 45 | 46 | [project.urls] 47 | Homepage = "https://github.com/rustychris/stompy" 48 | -------------------------------------------------------------------------------- /requirements-rtd.txt: -------------------------------------------------------------------------------- 1 | cmocean 2 | future 3 | # GDAL # this is failing the build 4 | matplotlib 5 | netCDF4 6 | numpy 7 | pandas==0.19.2 8 | requests 9 | scipy 10 | Shapely 11 | six 12 | xarray 13 | seawater 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cmocean 2 | gdal 3 | matplotlib 4 | netCDF4 5 | numpy 6 | pandas 7 | requests 8 | scipy 9 | Shapely 10 | six 11 | xarray 12 | seawater 13 | cgal 14 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='stompy', 5 | version='0.1', 6 | packages=['stompy', 'stompy.grid', 'stompy.io', 'stompy.io.local', 7 | 'stompy.model', 'stompy.model.delft', 'stompy.model.fvcom', 8 | 'stompy.model.pypart', 'stompy.model.suntans', 9 | 'stompy.plot', 'stompy.plot.cmaps', 10 | 'stompy.spatial'], 11 | package_data={'stompy':['tide_consts.txt']}, 12 | license='MIT', 13 | url="https://github.com/rustychris/stompy", 14 | author="Rusty Holleman", 15 | author_email="rustychris@gmail.com", 16 | long_description=open('README.md').read(), 17 | ) 18 | -------------------------------------------------------------------------------- /stompy/.gitignore: -------------------------------------------------------------------------------- 1 | wind 2 | 3 | -------------------------------------------------------------------------------- /stompy/__init__.py: -------------------------------------------------------------------------------- 1 | # nothing to do. 2 | -------------------------------------------------------------------------------- /stompy/grid/__init__.py: -------------------------------------------------------------------------------- 1 | # nothing 2 | -------------------------------------------------------------------------------- /stompy/grid/geom_types.py: -------------------------------------------------------------------------------- 1 | from osgeo import ogr 2 | 3 | # map geometry types to names: 4 | ogr2text = { 5 | ogr.wkbGeometryCollection:'GeometryCollection', 6 | ogr.wkbGeometryCollection25D:'GeometryCollection25D', 7 | ogr.wkbLineString:'LineString', 8 | ogr.wkbLineString25D:'LineString25D', 9 | ogr.wkbLinearRing:'LinearRing', 10 | ogr.wkbMultiLineString:'MultiLineString', 11 | ogr.wkbMultiLineString25D:'MultiLineString25D', 12 | ogr.wkbMultiPoint:'MultiPoint', 13 | ogr.wkbMultiPoint25D:'MultiPoint25D', 14 | ogr.wkbMultiPolygon:'MultiPolygon', 15 | ogr.wkbMultiPolygon25D:'MultiPolygon25D', 16 | ogr.wkbPoint:'Point', 17 | ogr.wkbPoint25D:'Point25D', 18 | ogr.wkbPolygon:'Polygon', 19 | ogr.wkbPolygon25D:'Polygon25D', 20 | ogr.wkbUnknown:'Unknown', 21 | } 22 | 23 | text2ogr = dict( [ [ogr2text[k],k] for k in ogr2text.keys()] ) 24 | -------------------------------------------------------------------------------- /stompy/grid/gmsh_scale_helper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handle scale information for a gmsh process. 3 | Expects a single command line argument giving the path to a 4 | pickled Field instance. 5 | 6 | This probably doesn't work with python2 (gmsh docs provide some 7 | additional options needed for python2) 8 | """ 9 | from stompy.spatial import field 10 | import pickle 11 | import struct 12 | import sys 13 | import math 14 | 15 | pkl_file=sys.argv[1] 16 | with open(pkl_file,'rb') as fp: 17 | scale=pickle.load(fp) 18 | 19 | while True: 20 | xyz = struct.unpack("ddd", sys.stdin.buffer.read(24)) 21 | if math.isnan(xyz[0]): 22 | break 23 | f = scale(xyz[:2]) 24 | sys.stdout.buffer.write(struct.pack("d",f)) 25 | sys.stdout.flush() 26 | -------------------------------------------------------------------------------- /stompy/io/__init__.py: -------------------------------------------------------------------------------- 1 | # nothing 2 | -------------------------------------------------------------------------------- /stompy/io/local/.gitignore: -------------------------------------------------------------------------------- 1 | cache 2 | -------------------------------------------------------------------------------- /stompy/io/local/__init__.py: -------------------------------------------------------------------------------- 1 | # Various classes for datasources of local or regional use. 2 | -------------------------------------------------------------------------------- /stompy/io/local/cdip_mop.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple caching layer on THREDDS access to CDIP MOP 3 | wave data. Useful for US West Coast wave climate data, 4 | hourly resolution of many wave parameters. 5 | """ 6 | 7 | import os 8 | import numpy as np 9 | from .common import periods 10 | import logging 11 | log=logging.getLogger('cdip_mop') 12 | 13 | import xarray as xr 14 | 15 | def hindcast_dataset(station,start_date,end_date,cache_dir=None, 16 | variables=['waveHs'],clip='True', 17 | days_per_request='M'): 18 | """ 19 | station: string, such as 'SM141' (which Pescadero State Beach, San Mateo county) 20 | start_date,end_date: np.datetime64 21 | clip: 22 | False: keep duration of cached blocks 23 | True: clip python-style, exclusive of end date 24 | 'inclusive': clip, but include end date 25 | 26 | variables: which variables to fetch. pass None for all variables. 27 | """ 28 | url="http://thredds.cdip.ucsd.edu/thredds/dodsC/cdip/model/MOP_alongshore/%s_hindcast.nc"%station 29 | 30 | ds=None 31 | t=None 32 | 33 | chunks=[] 34 | 35 | for interval_start,interval_end in periods(start_date,end_date,days_per_request): 36 | base_fn="%s_%s_%s_%s.nc"%(station, 37 | "-".join(["%s"%p for p in variables]), 38 | interval_start.strftime('%Y%m%d'),interval_end.strftime('%Y%m%d')) 39 | 40 | if cache_dir is not None: 41 | cache_fn=os.path.join(cache_dir,base_fn) 42 | else: 43 | cache_fn=None 44 | 45 | if (cache_fn is not None) and os.path.exists(cache_fn): 46 | chunk_ds=xr.open_dataset(cache_fn) 47 | chunk_ds.load() 48 | chunks.append(chunk_ds.copy()) 49 | chunk_ds.close() 50 | else: 51 | # Fetch it 52 | if ds is None: # open on demand 53 | log.info("Fetching cdip/mop metadta") 54 | ds=xr.open_dataset(url) 55 | t=ds.waveTime.values 56 | 57 | cmip_start,cmip_stop=np.searchsorted(t, 58 | [np.datetime64(interval_start), 59 | np.datetime64(interval_end) ]) 60 | 61 | if variables is not None: 62 | ds_sel=ds 63 | else: 64 | ds_sel=ds[variables] 65 | 66 | ds_sel=ds_sel[variables].isel(waveTime=slice(cmip_start,cmip_stop+1)) 67 | ds_sel.load() 68 | chunks.append(ds_sel) 69 | if cache_fn is not None: 70 | ds_sel.to_netcdf(cache_fn) 71 | if len(chunks)==0: 72 | return None 73 | elif len(chunks)==1: 74 | ds=chunks[0] 75 | else: 76 | # eliminate any time overlaps: 77 | clipped=[chunks[0]] 78 | for chunk in chunks[1:]: 79 | sel=chunk.waveTime.values>clipped[-1].waveTime.values[-1] 80 | clipped.append( chunk.isel(waveTime=sel) ) 81 | ds=xr.concat(clipped,dim='waveTime') 82 | 83 | if not clip: 84 | pass 85 | else: 86 | if clip=='inclusive': 87 | sel=(ds.waveTime.values>=start_date)&(ds.waveTime.values<=end_date) 88 | else: 89 | sel=(ds.waveTime.values>=start_date)&(ds.waveTime.values', 11 | '', 12 | ' ', 13 | f' ', 14 | ' \n'])) 15 | # PointData tag can include Scalars="Ve" Vectors="Velocity", etc. but I think this 16 | # is just for selecting an "active" field. 17 | 18 | for vari in df.columns: 19 | if vari in ['x','y','z']: 20 | continue 21 | fp.write(f' \n') 22 | fp.write(" ".join( ("%.4f"%x for x in df[vari].values) ) ) 23 | fp.write('\n \n') 24 | 25 | fp.write("\n".join([ 26 | ' ', 27 | ' ' # was separate open/close tags 28 | ])) 29 | 30 | fp.write( ' \n') 31 | fp.write( ' \n') 32 | fp.write( " ".join( ("%.3f %.3f %.3f"%(p[0],p[1],p[2]) for p in df[ ['x','y','z'] ].values) ) ) 33 | 34 | fp.write('\n\n \n') 35 | 36 | fp.write(' \n') 37 | fp.write('\n') 38 | fp.write( " ".join( ("%d"%x for x in range(len(df))) ) ) 39 | fp.write('\n\n') 40 | 41 | fp.write('') 42 | fp.write( " ".join( ("%d"%(x+1) for x in range(len(df))) ) ) 43 | fp.write('\n') 44 | 45 | fp.write('') 46 | fp.write( " ".join( ("1" for x in range(len(df))) ) ) 47 | fp.write('\n') 48 | 49 | fp.write("\n".join([ 50 | ' ', 51 | ' ', 52 | ' ', 53 | '\n'] )) 54 | -------------------------------------------------------------------------------- /stompy/io/wocss/.gitignore: -------------------------------------------------------------------------------- 1 | windsuv.dat 2 | windsuv.out 3 | windsuv.query 4 | windsuv.stations 5 | outfyl 6 | GSwoxB1 7 | 8 | -------------------------------------------------------------------------------- /stompy/io/wocss/Makefile: -------------------------------------------------------------------------------- 1 | all: GSwoxB1 2 | 3 | FC=gfortran 4 | 5 | GSwoxB1: GSwoxB1.f 6 | $(FC) -o $@ $< 7 | -------------------------------------------------------------------------------- /stompy/io/wocss/NOTES: -------------------------------------------------------------------------------- 1 | How available are the necessary data? 2 | 3 | In winds.dat: 4 | 5 | surface wind inputs: 10 stations, fairly broadly distributed. 6 | 7 | then a sounding wind speed ( utm 569000 4173000 ) 8 | a sounding at the same location, labeled JU230512.OAK, sounds a lot like 9 | oakland airport? second sounding has temp, Td, and pressure 10 | And the wind data is repeated... 11 | 12 | - http://www.esrl.noaa.gov/ might provide the sounding data 13 | 14 | ds=xr.open_dataset('raob_soundings25653.cdf',decode_times=0) 15 | 16 | - nice! - it has twice daily sondes. just picked a random period in spring 2013, 17 | and got 150 profiles. 18 | 19 | - plotting the wind vectors on top of the terrain, it looks pretty reasonable. 20 | 21 | Questions before this could be put into place for our runs: 22 | - how much wind data can we get, starting with WY2013? 23 | - assume that the sonde profiles can be interpolated in time without too much 24 | trouble. 25 | - would have to downsample the gridded wind for input to suntans. probably 26 | okay. going to be a big dataset. 27 | - aim for 1h winds? 3h? using the sample setup as an example, one realization 28 | of the output wind field is 130kB. So a year at 3h is 194 MB. No sweat. 29 | Possible that 1km is too coarse for a place like Carquinez, but do we 30 | really need perfect wind there? 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /stompy/io/wocss/plots.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import xarray as xr 4 | 5 | ## 6 | 7 | station_xy=np.loadtxt('windsuv.stations') 8 | 9 | # this has 999 in most places, and a handful of signed values 10 | query=np.loadtxt('windsuv.query') # 246,108 11 | u_query=query[:nrows ] 12 | v_query=query[nrows:] 13 | 14 | # this has a nice smooth field interpolated between stations 15 | output=np.loadtxt('windsuv.out') # 246,108 16 | u_output=output[:nrows] 17 | v_output=output[nrows:] 18 | 19 | # has similar pattern as query, but different values. 20 | dat=np.loadtxt('windsuv.dat') 21 | u_dat=dat[:nrows] 22 | v_dat=dat[nrows:] 23 | 24 | # basic DEM 25 | terrain=np.loadtxt('terrain.dat') 26 | 27 | ## 28 | plt.figure(1).clf() 29 | fig,ax=plt.subplots(num=1) 30 | 31 | ax.plot(station_xy[:,0], 32 | station_xy[:,1],'ko') 33 | 34 | # not sure that came out right 35 | nrows=query.shape[0]//2 36 | 37 | # mostly 999, but with seemingly independent, signed values, in the 38 | # range -100, 176. dm/s ? 39 | # clumping of signs supports this being wind vectors 40 | # ax.imshow(u_query[::-1,:],cmap='jet',origin='lower') 41 | 42 | # ax.imshow(u_dat[::-1,:],cmap='jet',origin='lower') 43 | # ax.imshow(v_output[::-1,:],cmap='jet',origin='lower') 44 | 45 | # ax.imshow( (u_dat-u_query)[::-1,:], cmap='jet',origin='lower') 46 | 47 | 48 | ax.imshow(terrain[::-1,:],cmap='jet',origin='lower') 49 | 50 | ## 51 | 52 | ds=xr.open_dataset('raob_soundings25653.cdf',decode_times=0) 53 | 54 | ## 55 | 56 | 57 | # quiver plot: 58 | x=np.arange(u_output.shape[1]) 59 | y=np.arange(u_output.shape[0])[::-1] 60 | 61 | X,Y=np.meshgrid(x,y) 62 | 63 | plt.figure(1).clf() 64 | fig,ax=plt.subplots(num=1) 65 | #ax.imshow(v_output[::-1,:],origin='lower') 66 | ax.imshow(terrain[::-1,:],origin='lower',interpolation='nearest') 67 | 68 | slc=slice(None,None,5) 69 | ax.quiver(X[slc,slc],Y[slc,slc], 70 | u_output[slc,slc],v_output[slc,slc]) 71 | -------------------------------------------------------------------------------- /stompy/io/wocss/raob_soundings25653.cdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/stompy/io/wocss/raob_soundings25653.cdf -------------------------------------------------------------------------------- /stompy/io/wocss/rundat.dat: -------------------------------------------------------------------------------- 1 | **************Sn Francisco Bay area stuff******************************* 2 | 1 * NSKIP ascii option writes every nskip grid pts in x & y. 3 | 10 * NLVL:number of flow surfaces used for calculations 4 | 0.0,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.8,1.0 * SIGMA(NLVL):sigma values of flow levels 5 | 7 * NFLAT: # of horiz. sfcs for interpol. (includes anemom. lvl) 6 | 10.,50.,100.,200.,400.,800.,1000. * zchooz: heights (m, msl) of horiz sfcs 7 | 200,1,1 * NUMNWS,NUMTMP,numdop: total # sta., # upper wind & t-sondes 8 | 0.515,0.3048 * SPDCNV,HTCNV conv factors input spds to m/s and heights to m 9 | 123,108 * MCRS,NCRS: # rows/cols 10 | 1.0 * DSCRS:dx -- grid spacing (km) 11 | 524.5,4134.0 * UTMAPX,UTMAPY:utms of reference point 12 | 1,1 * kgridx,kgridy, indices of reference point 13 | 1500. * AVTHK -- ht (m) of top sfc over low point 14 | 1.0 * SLFAC slope fact 1st guess top sfc (0=flat,1=terr follow) 15 | 0.2 * CMPRES max compress of space bet sfcs smaller=less rise 16 | 0.001 * DPOTMIN min. theta lapse rate (deg/m) -- low stab lim 17 | 0.05,10.0 * ZZERO,Z10 sfc roughness length (m) & anemometer ht. (m) 18 | 0.1 * D2MIN: min. dist for interp. wts in grid units 19 | 2.0 * DTWT: distance weight power -- wt=1/(Dist**DTWT) 20 | 20 * NITer, iteration limit in subroutine bal5. 21 | 0.2 * ADJMAX: max adj fact near obs 0=no adj & 1=reg adj 22 | 65,12 * lowix & lowiy -- ix & iy indices of a low pt. for t-sonde 1 23 | 41,50 * ditto -- each sonde requires specifying 5 more low pts. 24 | 3,33 * ditto -- there will be 5*numtmp pts 25 | 32,99 * ditto 26 | 42,50 * ditto 27 | 1 * NEND number of cases before stopping. 28 | -------------------------------------------------------------------------------- /stompy/io/wocss/winds.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/stompy/io/wocss/winds.dat -------------------------------------------------------------------------------- /stompy/model/__init__.py: -------------------------------------------------------------------------------- 1 | # nothing to do. 2 | -------------------------------------------------------------------------------- /stompy/model/delft/.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | 3 | -------------------------------------------------------------------------------- /stompy/model/delft/CODE_GUIDE.txt: -------------------------------------------------------------------------------- 1 | Just some notes on coding style decisions and rationale (or complaints/quandaries) 2 | 3 | Use tuples for class default values 4 | -- 5 | When it's necessary for a class, such as Scenario, to have a default value expressed 6 | as a list, use a tuple rather than a list. This reduces errors in which method 7 | code modifies the classes value. In particular, 8 | class A(object): 9 | x=(1,2,3) 10 | a1=A() 11 | a1.x += (4,) 12 | a2=A() 13 | a2.x != a1.x 14 | is safe, but if x were a list, then a2.x would get the modified value from a1. 15 | 16 | 17 | Constants 18 | -- 19 | Make constants available as attributes of the relevant class. This makes 20 | objects self-contained and 'batteries-included'. Otherwise you have to 21 | import the defining module just to use the object. 22 | -------------------------------------------------------------------------------- /stompy/model/delft/__init__.py: -------------------------------------------------------------------------------- 1 | pass 2 | -------------------------------------------------------------------------------- /stompy/model/delft/data/trim-tut_fti_waq.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/stompy/model/delft/data/trim-tut_fti_waq.dat -------------------------------------------------------------------------------- /stompy/model/delft/data/trim-tut_fti_waq.def: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/stompy/model/delft/data/trim-tut_fti_waq.def -------------------------------------------------------------------------------- /stompy/model/delft/data/tut_fti_waq-bal.his: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/stompy/model/delft/data/tut_fti_waq-bal.his -------------------------------------------------------------------------------- /stompy/model/delft/data/tut_fti_waq.hda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/stompy/model/delft/data/tut_fti_waq.hda -------------------------------------------------------------------------------- /stompy/model/delft/data/tut_fti_waq.hdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/stompy/model/delft/data/tut_fti_waq.hdf -------------------------------------------------------------------------------- /stompy/model/delft/dflow_grid.py: -------------------------------------------------------------------------------- 1 | # see how involved a NEFIS reader in native python/numpy would be 2 | import os 3 | import sys 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import memoize 7 | 8 | import matplotlib.tri as tri 9 | 10 | class DflowGrid2D(object): 11 | def __init__(self,xcor,ycor,xz,yz,active): 12 | self.xcor=xcor 13 | self.ycor=ycor 14 | self.xz=xz 15 | self.yz=yz 16 | self.active=active 17 | comp=(active==1) # computational cells 18 | self.node_active=comp.copy() 19 | self.node_active[:-1,:-1] = comp[1:,1:] | comp[:-1,1:] | comp[1:,:-1] | comp[:-1,:-1] 20 | 21 | @staticmethod 22 | def from_nef_map(nef): 23 | xcor=nef['map-const'].getelt('XCOR',[0]) 24 | ycor=nef['map-const'].getelt('YCOR',[0]) 25 | xz=nef['map-const'].getelt('XZ',[0]) 26 | yz=nef['map-const'].getelt('YZ',[0]) 27 | active=nef['map-const'].getelt('KCS',[0]) 28 | return DflowGrid2D(xcor,ycor,xz,yz,active) 29 | 30 | def wireframe(self): 31 | # this method double-draws interior lines. 32 | coll=self.pcolor_face(0*self.xcor) 33 | coll.set_array(None) 34 | coll.set_facecolors('none') 35 | return coll 36 | 37 | def pcolor_face(self,v): 38 | vmsk=np.ma.masked_where(self.active!=1,v) 39 | return plt.pcolor( self.xcor[:-1,:-1], 40 | self.ycor[:-1,:-1], 41 | vmsk[1:-1,1:-1], 42 | edgecolors='k',lw=0.2) 43 | 44 | def pcolormesh_node(self,v): 45 | vmsk=np.ma.masked_where(~self.node_active,v) 46 | return plt.pcolormesh( self.xcor[:-1,:-1], 47 | self.ycor[:-1,:-1], 48 | vmsk[:-1,:-1], 49 | edgecolors='k',lw=0.2, 50 | shading='gouraud') 51 | 52 | @memoize.memoize() 53 | def to_triangulation(self): 54 | # map 2d node index to 1d index - not yet compressed. 55 | idx1d=np.arange(np.prod(self.xcor.shape)).reshape(self.xcor.shape) 56 | 57 | tris=[] 58 | comp=(self.active==1)[1:-1,1:-1] 59 | for row,col in zip(*np.nonzero(comp)): 60 | # may not be CCW 61 | tris.append( [ idx1d[row,col], 62 | idx1d[row,col+1], 63 | idx1d[row+1,col+1] ] ) 64 | tris.append( [ idx1d[row+1,col+1], 65 | idx1d[row+1,col], 66 | idx1d[row,col] ] ) 67 | 68 | used=np.unique(tris) 69 | x=self.xcor.ravel()[used] 70 | y=self.ycor.ravel()[used] 71 | remap=np.zeros(np.prod(self.xcor.shape)) 72 | remap[used] = np.arange(len(used)) 73 | orig_tris=tris 74 | tris=remap[tris] # these now index 75 | 76 | T=tri.Triangulation(x,y,tris) 77 | idx_map=used 78 | 79 | return T,idx_map 80 | def pcolor_node(self,v,**kws): 81 | my_kws=dict(shading='gouraud') 82 | my_kws.update(kws) 83 | T,idx_map = self.to_triangulation() 84 | return plt.tripcolor(T,v.ravel()[idx_map],**my_kws) 85 | def contourf_node(self,v,*args,**kws): 86 | T,idx_map = self.to_triangulation() 87 | return plt.tricontourf(T,v.ravel()[idx_map],*args,**kws) 88 | 89 | 90 | -------------------------------------------------------------------------------- /stompy/model/delft/map_merge.py: -------------------------------------------------------------------------------- 1 | """ 2 | Merge DFM map files from multiprocessor to single domain. 3 | 4 | In theory dfmoutput mapmerge infiles... 5 | does this, but experience has shown it is not reliable. 6 | 7 | """ 8 | 9 | 10 | class MapMerge(object): 11 | def __init__(self,infiles,outfile,global_grid): 12 | self.global_grid=global_grid 13 | self.outfile=outfile 14 | self.infiles=infiles 15 | def write_merged(self): 16 | self.write_grid() 17 | self.write_data() 18 | def write_grid(self): 19 | # here - could decide on dialect, naming. 20 | ds=self.global_grid.write_to_xarray() 21 | ds.to_netcdf(self.outfile) 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /stompy/model/delft/nefis_nc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Converting between nefis and netcdf 3 | In a separate module to separate dependencies 4 | 5 | Relies on the qnc wrapper of netcdf4 6 | """ 7 | 8 | from collections import defaultdict 9 | from ...io import qnc 10 | 11 | def nefis_to_nc(nef,squeeze_unl=True,squeeze_element=True, 12 | short_if_unique=True,to_lower=True,unl_name='time', 13 | element_map={},nc_kwargs={},nc=None): 14 | """ 15 | nef: an open Nefis object 16 | squeeze_unl: unit length unlimited dimensions in groups are dropped 17 | groups can't be dimensionless, so parameter values are often in 18 | a unit-length group. 19 | short_if_unique: element names which appear in only one group get 20 | just the element name. others are group_element 21 | to_lower: make names lower case 22 | squeeze_element: unit dimensions in elements are also removed 23 | unl_name: if there is a unique unlimited dimension length (ignoring 24 | unit lengths if squeeze_unl is set) - use this name for it. 25 | element_map: map original element names to new names. this matches 26 | against element names before to_lower (but after strip), and the results 27 | will *not* be subject to to_lower. 28 | nc_kwargs: dict of argument to pass to qnc.empty 29 | nc: altenatively, an already open QDataset 30 | """ 31 | if nc is None: 32 | nc=qnc.empty(**nc_kwargs) 33 | # required for writing to disk 34 | nc._set_string_mode('fixed') 35 | 36 | # string handling is a little funky - 37 | # still working out whether it should be varlen or fixed. 38 | # fixed makes the string length a new dimension, just annoying 39 | # to get strings back from that. 40 | # varlen ends up having an object dtype, which may not write out 41 | # well with netcdf. 42 | 43 | # check for unique element names 44 | name_count=defaultdict(lambda: 0) 45 | for group in nef.groups(): 46 | for elt_name in group.cell.element_names: 47 | name_count[elt_name]+=1 48 | 49 | # check for unique unlimited dimension: 50 | n_unl=0 51 | for group in nef.groups(): 52 | if 0 in group.shape and (group.unl_length()>1 or not squeeze_unl): 53 | n_unl+=1 54 | 55 | for group in nef.groups(): 56 | # print group.name 57 | 58 | g_shape=group.shape 59 | grp_slices=[slice(None)]*len(g_shape) 60 | grp_dim_names=[None]*len(g_shape) 61 | 62 | if 0 in g_shape: # has an unlimitied dimension 63 | idx=list(g_shape).index(0) 64 | if group.unl_length()==1 and squeeze_unl: # which will be squeezed 65 | grp_slices[idx]=0 66 | elif n_unl==1 and unl_name: # which will be named 67 | grp_dim_names[idx]=unl_name 68 | 69 | for elt_name in group.cell.element_names: 70 | # print elt_name 71 | 72 | if name_count[elt_name]==1 and short_if_unique: 73 | vname=elt_name 74 | else: 75 | vname=group.name + "_" + elt_name 76 | 77 | if vname in element_map: 78 | vname=element_map[vname] 79 | elif to_lower: 80 | vname=vname.lower() 81 | 82 | value=group.getelt(elt_name) 83 | # apply slices 84 | value=value[tuple(grp_slices)] 85 | 86 | if squeeze_element: 87 | # slices specific to this element 88 | val_slices=[slice(None)]*len(value.shape) 89 | # iterate over just the element portion of the shape 90 | for idx in range(len(g_shape),len(val_slices)): 91 | if value.shape[idx]==1: 92 | val_slices[idx]=0 93 | value=value[val_slices] 94 | 95 | # mimics qnc naming. 96 | names=[qnc.anon_dim_name(size=l) for l in value.shape] 97 | for idx,name in enumerate(grp_dim_names): 98 | if name: 99 | names[idx]=name 100 | names.append(Ellipsis) 101 | 102 | nc[vname][names] = value 103 | setattr(nc.variables[vname],'group_name',group.name) 104 | return nc 105 | 106 | -------------------------------------------------------------------------------- /stompy/model/delft/splice_waq.py: -------------------------------------------------------------------------------- 1 | # invoke with python -m delft.splice_waq 2 | # (assuming that delft is on PYTHONPATH 3 | # use waq_scenario machinery to stitch a bunch of subdomains into 4 | # a single subdomain. 5 | # sort of a repurposing of the DWaqAggregator. 6 | 7 | import os 8 | import logging 9 | import numpy as np 10 | 11 | logger = logging.getLogger() 12 | 13 | from ..suntans import sunreader 14 | from . import waq_scenario 15 | from . import sundwaq 16 | from ... import scriptable 17 | 18 | class SpliceScenario(waq_scenario.Scenario): 19 | pass 20 | 21 | class SunSplicer(scriptable.Scriptable): 22 | def cmd_merge(self,sun_dir,agg_shp="agg-cells.shp",name='global'): 23 | """ sun_dir [agg_shp] [name] 24 | sun_dir: path to directory containing suntans.dat 25 | agg_shp: path, absolute or relative to sun_dir, to shapefile definining 26 | aggregation. Defaults to creating a non-aggregating shapefile. 27 | name: the name under the dwaq directory for the output. 28 | 29 | Combines dwaq-formatted output from multiple processors into a single 30 | domain. Calls suntans_post and make_agg_shp as needed. 31 | """ 32 | sun_dir=os.path.abspath(sun_dir) 33 | 34 | self.cmd_suntans_post(sun_dir) 35 | sun=sunreader.SunReader(sun_dir) 36 | 37 | run_prefix="sun" 38 | dwaq_path=os.path.join(sun.datadir,'dwaq') 39 | 40 | agg_shp=os.path.join(sun_dir,agg_shp) 41 | self.cmd_make_agg_shp(sun_dir,agg_shp) 42 | 43 | agg=waq_scenario.HydroMultiAggregator(agg_shp=agg_shp, 44 | run_prefix="sun", 45 | path=os.path.join(sun_dir,'dwaq')) 46 | 47 | scen=SpliceScenario(hydro=agg) 48 | scen.base_path=os.path.join(dwaq_path,"global") 49 | scen.name="spliced" 50 | 51 | scen.write_hydro() 52 | 53 | def cmd_make_agg_shp(self,sun_dir,agg_shp="grid-cells.shp"): 54 | """ sun_dir [agg_shp] 55 | If agg_shp does not exist, write a shapefile which retains all global 56 | cells (i.e. no aggregation) 57 | """ 58 | # agg_shp is relative to sun_dir, unless it is an absolute 59 | # path 60 | 61 | agg_shp=os.path.join(sun_dir,agg_shp) # make it absolute 62 | 63 | if not os.path.exists(agg_shp): 64 | sun=sunreader.SunReader(sun_dir) 65 | g=sun.grid() 66 | fields=np.zeros(g.Ncells(),dtype=[('name',object)]) 67 | fields['name'] =["%d"%c for c in range(g.Ncells())] 68 | g.write_cells_shp(agg_shp,overwrite=True,fields=fields) 69 | 70 | def cmd_suntans_post(self,sun_dir): 71 | """ sun_dir 72 | sun_dir: path to directory containing suntans.dat 73 | Write flowgeom.nc for each subdomain of dwaq output under the given 74 | directory. 75 | """ 76 | sun=sunreader.SunReader(sun_dir) 77 | sundwaq.postprocess(sun=sun,force=False) 78 | 79 | 80 | if __name__ == '__main__': 81 | splicer=SunSplicer() 82 | splicer.main() 83 | -------------------------------------------------------------------------------- /stompy/model/delft/test_agg.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests related to aggregating WAQ hydro 3 | on hold, as the current aggregation code uses netcdf net files to 4 | figure out the partitions, which aren't available for the FTI 5 | tutorial case. 6 | """ 7 | from __future__ import print_function 8 | 9 | import os 10 | import shutil 11 | 12 | import nose 13 | import numpy as np 14 | from . import waq_scenario 15 | 16 | 17 | PC=waq_scenario.ParameterConstant 18 | Sub=waq_scenario.Substance 19 | IC=waq_scenario.Initial 20 | 21 | 22 | class Testbox(waq_scenario.Scenario): 23 | base_path=os.path.join(os.path.dirname(__file__),'testbox') 24 | name="bloombox02-test" 25 | 26 | integration_option="15 NODISP-AT-BOUND LOWER-ORDER-AT-BOUND DISP-AT-NOFLOW" 27 | 28 | def cmd_default(self): 29 | self.cmd_write_runid() 30 | self.cmd_write_hydro() 31 | 32 | def test_agg(): 33 | dwaq_path="data/waq_hyd" # /tut_fti_waq.hyd 34 | hydro=waq_scenario.DwaqAggregator("box_defs.shp", 35 | "r17",dwaq_path,nprocs=None) 36 | 37 | scen=TextBox(hydro=hydro) 38 | 39 | if os.path.exists(scen.base_path): 40 | print("base path:",scen.base_path) 41 | shutil.rmtree(scen.base_path) 42 | 43 | scen.cmd_default() 44 | 45 | shutil.rmtree(scen.base_path) 46 | 47 | 48 | if __name__=='__main__': 49 | nose.main() 50 | -------------------------------------------------------------------------------- /stompy/model/delft/test_dflow_grid.py: -------------------------------------------------------------------------------- 1 | import nose 2 | import nefis 3 | import numpy as np 4 | import dflow_grid as dg 5 | 6 | def load_data(): 7 | nef=nefis.Nefis('data/trim-tut_fti_waq.dat', 8 | 'data/trim-tut_fti_waq.def') 9 | g=dg.DflowGrid2D.from_nef_map(nef) 10 | 11 | u1=nef['map-series'].getelt('U1',[10]) 12 | rho=nef['map-series'].getelt('RHO',[10]) 13 | dp0=nef['map-const'].getelt('DP0',[0]) 14 | dps0=nef['map-const'].getelt('DPS0',[0]) 15 | 16 | nef.close() 17 | return g,u1,rho,dp0,dps0 18 | 19 | def test_create(): 20 | g,u1,rho,dp0,dps0=load_data() 21 | assert(g) 22 | 23 | # Plotting 24 | def test_wireframe(): 25 | g,u1,rho,dp0,dps0 = load_data() 26 | g.wireframe() 27 | 28 | def test_pcolor_face(): 29 | g,u1,rho,dp0,dps0 = load_data() 30 | g.pcolor_face(rho[1]) 31 | 32 | def test_contourf_node(): 33 | g,u1,rho,dp0,dps0 = load_data() 34 | g.contourf_node(dp0) 35 | 36 | def test_pcolor_node(): 37 | g,u1,rho,dp0,dps0 = load_data() 38 | g.pcolor_node(dp0) 39 | 40 | if __name__ == '__main__': 41 | nose.main() 42 | -------------------------------------------------------------------------------- /stompy/model/delft/test_waq_process.py: -------------------------------------------------------------------------------- 1 | import nose 2 | 3 | from delft import waq_scenario 4 | from delft import waq_process 5 | 6 | def test_sub_lookup(): 7 | scen=waq_scenario.Scenario(None) 8 | pdb=waq_process.ProcessDB(scenario=scen) 9 | sub=pdb.substance_by_id('Green') 10 | 11 | assert(sub.item_nm=='Algae (non-Diatoms)') 12 | 13 | 14 | if __name__ == '__main__': 15 | nose.main() 16 | -------------------------------------------------------------------------------- /stompy/model/fvcom/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | INCOMPLETE 3 | Minimal start to tools for FVCOM runs 4 | """ 5 | -------------------------------------------------------------------------------- /stompy/model/fvcom/fvcom.py: -------------------------------------------------------------------------------- 1 | import netCDF4 2 | import memoize as memo 3 | import numpy as np 4 | import trigrid 5 | import pytz 6 | 7 | class FvcomNC(object): 8 | tz='US/Eastern' 9 | tzvalue='-0500' 10 | 11 | def __init__(self,nc_file,**kwargs): 12 | self.nc_file = nc_file 13 | self.nc=netCDF4.Dataset(self.nc_file) 14 | self.__dict__.update(kwargs) 15 | 16 | # pickle protocol interface - since Dataset cannot be pickled. 17 | def __getstate__(self): 18 | d = dict(self.__dict__) 19 | del d['nc'] 20 | return d 21 | def __setstate__(self,d): 22 | self.__dict__.update(d) 23 | self.nc=netCDF4.Dataset(self.nc_file) 24 | 25 | @property 26 | @memo.memoize() 27 | def time(self): 28 | nc_t_var=self.nc.variables['time'] 29 | units,origin = nc_t_var.units.split(" since ") 30 | if origin[-1] in '0123456789': 31 | origin = origin + self.tzvalue 32 | tzero = np.datetime64(origin,'s') 33 | 34 | days=self.nc.variables['Itime'][:] * np.timedelta64(1,'D') 35 | msecs=self.nc.variables['Itime2'][:]*np.timedelta64(1,'ms') 36 | t=tzero+days+msecs 37 | return t 38 | 39 | @property 40 | @memo.memoize() 41 | def points(self): 42 | return np.array( [self.nc.variables['x'][:], 43 | self.nc.variables['y'][:]], dtype='f8' ).T 44 | @property 45 | @memo.memoize() 46 | def centers(self): 47 | return np.array( [self.nc.variables['xc'][:], 48 | self.nc.variables['yc'][:]] ).T 49 | @property 50 | @memo.memoize() 51 | def trigrid(self): 52 | cells=self.nc.variables['nv'][:].T - 1 53 | points=self.points.copy() 54 | g=trigrid.TriGrid(points=points,cells=cells) 55 | areas=g.areas() 56 | sel=np.nonzero(areas<0)[0] 57 | cells[sel,:] = cells[sel,::-1] 58 | 59 | # make a clean one with the new cells 60 | g=trigrid.TriGrid(points=points,cells=cells) 61 | g.make_edges_from_cells() 62 | return g 63 | 64 | # convenience for getting cell centered mean quantities 65 | @property 66 | @memo.memoize() 67 | def hc(self): 68 | h=self.nc.variables['h'][:] 69 | return h[self.trigrid.cells].mean(axis=1) 70 | 71 | @memo.memoize(lru=2) 72 | def zetac(self,tidx): 73 | zeta=self.nc.variables['zeta'][tidx] 74 | return zeta[self.trigrid.cells].mean(axis=1) 75 | 76 | @memo.memoize(lru=2) 77 | def uvw(self,tidx,c=slice(None),sigma_layer=slice(None)): 78 | u=self.nc.variables['u'][tidx,sigma_layer,c] 79 | v=self.nc.variables['v'][tidx,sigma_layer,c] 80 | w=self.nc.variables['ww'][tidx,sigma_layer,c] 81 | 82 | return np.concatenate( (u[...,None],v[...,None],w[...,None]), axis=-1 ) 83 | 84 | @memo.memoize(lru=2) 85 | def to_sigma_layer(self,tidx,c,X): 86 | """ 0-based sigma layer index 87 | """ 88 | z=X[2] 89 | h=self.hc[c] 90 | zeta=self.zetac(tidx)[c] 91 | 92 | # [-1,0] 93 | sigma=(z-(-h))/(zeta-(-h)) - 1 94 | siglevels=self.nc.variables['siglev'][:,self.trigrid.cells[c]].mean(axis=1) 95 | layer=np.searchsorted(-siglevels,-sigma).clip(0,len(siglevels)-2) 96 | return layer 97 | -------------------------------------------------------------------------------- /stompy/model/openfoam/cli.py: -------------------------------------------------------------------------------- 1 | from . import depth_average 2 | from ... import utils 3 | import os 4 | 5 | def main(argv=None): 6 | import argparse 7 | 8 | parser = argparse.ArgumentParser(description='Postprocess OpenFOAM results, esp. depth-averaging') 9 | 10 | parser.add_argument("-c", "--case", help="Path to case, default is working directory", 11 | default=".") 12 | parser.add_argument("-U", "--velocity", help="Enable depth-averaged velocity output", 13 | action="store_true") 14 | parser.add_argument("-s","--stage", help="Enable stage (water surface elevation) output", 15 | action="store_true") 16 | parser.add_argument("-t","--time", help="Select output times as comma-separated time names, or 'all'", 17 | default="all") 18 | parser.add_argument("-r","--res", help="Output raster cell size (positive), or count in x (negative)", 19 | default=20.0, type=float) 20 | parser.add_argument("-f","--force", help="Force overwrite existing files",action='store_true') 21 | 22 | args = parser.parse_args(argv) 23 | 24 | pf = depth_average.PostFoam(sim_dir=args.case) 25 | 26 | if args.time=='all': 27 | times = pf.available_times(omit_zero=True) 28 | else: 29 | times = args.time.split(",") 30 | print(f"{len(times)} time step(s) to process") 31 | 32 | raster_info = pf.set_raster_parameters(dx=args.res) 33 | 34 | print(f"Output rasters will be {raster_info['ny']}x{raster_info['nx']}") 35 | output_path=os.path.join(args.case,"postProcessing",'python') 36 | if not os.path.exists(output_path): 37 | os.makedirs(output_path) 38 | print(f"Writing output to {output_path}") 39 | 40 | for t in utils.progress(times,msg="Time steps: %s"): 41 | if args.velocity: 42 | fn=os.path.join(output_path,f"U_{t}.tif") 43 | if args.force or not os.path.exists(fn): 44 | fld_U=pf.to_raster('U',t) 45 | # should put the velocity components into bands. 46 | fld_U.write_gdal(fn,overwrite=True) 47 | if args.stage: 48 | fn=os.path.join(output_path,f"stage_{t}.tif") 49 | if args.force or not os.path.exists(fn): 50 | fld_stage=pf.to_raster('wse',t) 51 | fld_stage.write_gdal(fn,overwrite=True) 52 | 53 | if __name__ == '__main__': 54 | main() 55 | -------------------------------------------------------------------------------- /stompy/model/otps/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /stompy/model/otps/__init__.py: -------------------------------------------------------------------------------- 1 | from .otps_model import * 2 | -------------------------------------------------------------------------------- /stompy/model/pypart/__init__.py: -------------------------------------------------------------------------------- 1 | # nothing to do 2 | -------------------------------------------------------------------------------- /stompy/model/slurm_mixin.py: -------------------------------------------------------------------------------- 1 | """ 2 | First cut at refactoring slurm interaction 3 | """ 4 | 5 | # For farm -- assumes that for any mpi work we're already in a job. 6 | class SlurmMixin: 7 | @classmethod 8 | def slurm_jobid(cls): 9 | return os.environ.get('SLURM_JOBID',None) 10 | 11 | @classmethod 12 | def assert_slurm(cls): 13 | # Could use srun outside of an existing job to synchronously 14 | # schedule and run. But then we'd have to get the partition and task 15 | # details down to here. 16 | assert cls.slurm_jobid() is not None,"mpi tasks need to be run within a job!" 17 | 18 | @classmethod 19 | def slurm_num_procs(cls): 20 | return int(os.environ['SLURM_NTASKS']) 21 | 22 | @classmethod 23 | def slurm_pack_options(cls,n): 24 | """ 25 | Return options to pass to srun to invoke an mpi task 26 | with n cpus. 27 | """ 28 | # is there a single component that works? 29 | n_het_tasks=0 # running count of tasks across components 30 | packs=[] # list of components needed to get to n 31 | 32 | # Scan up to 10 components: 33 | for group in range(10): 34 | group=str(group) 35 | group_size=int(os.environ.get( f'SLURM_NTASKS_PACK_GROUP_{group}', 0)) 36 | if group_size==0: break 37 | if group_size>=n: 38 | packs=[group] 39 | break 40 | if n_het_tasks0: 51 | print(f"Scanned PACK_GROUPS and found {n_het_tasks} < {n}. No can do.", 52 | flush=True) 53 | raise Exception("Insufficient cpus for MPI request") 54 | else: 55 | # Not a pack job. 56 | n_tasks=int(os.environ.get('SLURM_NTASKS',0)) 57 | if n_tasks==n: 58 | print(f"Homogeneous job, and n==NTASKS") 59 | return [] 60 | elif n_tasks SLURM ntasks {n_tasks}") 62 | else: 63 | options=['-n',str(n)] 64 | print(f"Homogeneous oversized job. Add {' '.join(options)}", 65 | flush=True) 66 | return options 67 | 68 | def run_mpi(self,sun_args): 69 | # This needs to be better abstracted, not suntans specific. 70 | self.assert_slurm() 71 | 72 | sun="sun" 73 | if self.sun_bin_dir is not None: 74 | sun=os.path.join(self.sun_bin_dir,sun) 75 | 76 | pack_opts=self.slurm_pack_options(self.num_procs) 77 | cmd=["srun"] 78 | cmd+=pack_opts 79 | cmd+=[sun] + sun_args 80 | print("About to invoke MPI cmd:") 81 | print(cmd,flush=True) 82 | subprocess.call(cmd) 83 | 84 | -------------------------------------------------------------------------------- /stompy/model/suntans/__init__.py: -------------------------------------------------------------------------------- 1 | # make it importable 2 | -------------------------------------------------------------------------------- /stompy/plot/__init__.py: -------------------------------------------------------------------------------- 1 | # nothing 2 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/Blue_Cyan_Yellow.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/nd/basic/Blue_Cyan_Yellow.cpt 2 | # autogenerated GMT palette "Basic_Blue_Cyan_Yellow" 3 | # cptutils version 1.44, Tue Mar 13 13:16:00 2012 4 | # COLOR_MODEL = RGB 5 | 0 0 0 255 25 0 128 255 6 | 25 0 128 255 50 0 255 255 7 | 50 0 255 255 75 128 255 128 8 | 75 128 255 128 100 255 255 0 9 | B 0 0 0 10 | F 255 255 255 11 | N 255 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/DEM_screen.cpt: -------------------------------------------------------------------------------- 1 | # cpt file created by: Thomas Dewez for elevations 2 | # 3 | # COLOR_MODEL = RGB 4 | 0 0 132 53 100 51 204 0 5 | 100 51 204 0 200 244 240 113 6 | 200 244 240 113 400 244 189 69 7 | 400 244 189 69 600 153 100 43 8 | 600 153 100 43 800 255 255 255 9 | B 255 255 255 10 | F 0 132 53 11 | N 0 132 53 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/GMT_cyclic.cpt: -------------------------------------------------------------------------------- 1 | # $Id: GMT_cyclic.cpt,v 1.1 2007/10/01 15:36:21 remko Exp $ 2 | # 3 | # Cyclic spectrum, spans 360 degrees of hue 4 | # Designed by Remko Scharroo, Altimetrics LLC 5 | # COLOR_MODEL = +HSV 6 | 0 0 1 1 180 180 1 1 7 | 180 180 1 1 360 360 1 1 8 | B 0 0 1 9 | F 0 0 0 10 | N 0 0 0.75 11 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/GMT_drywet.cpt: -------------------------------------------------------------------------------- 1 | # $Id: GMT_drywet.cpt,v 1.1 2002/05/23 22:59:15 pwessel Exp $ 2 | # 3 | # Dry to Wet colorbar 4 | # Created by Ed Maurer, U Washington 5 | 0 134 97 42 2 238 199 100 6 | 2 238 199 100 4 180 238 135 7 | 4 180 238 135 6 50 238 235 8 | 6 50 238 235 8 12 120 238 9 | 8 12 120 238 10 38 1 183 10 | 10 38 1 183 12 8 51 113 11 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/Primary_04.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/nd/rich/Primary_04.cpt 2 | # autogenerated GMT palette "Rich Primary 04" 3 | # cptutils version 1.44, Tue Mar 13 13:15:15 2012 4 | # COLOR_MODEL = RGB 5 | 0 217 217 0 25 217 108 0 6 | 25 217 108 0 50 217 0 0 7 | 50 217 0 0 75 108 28 70 8 | 75 108 28 70 100 0 56 140 9 | B 0 0 0 10 | F 255 255 255 11 | N 255 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/RdYlGn.ggr: -------------------------------------------------------------------------------- 1 | GIMP Gradient 2 | Name: RdYlGn 3 | 8 4 | 0.000000 0.062500 0.125000 0.647059 0.000000 0.149020 1.000000 0.843137 0.188235 0.152941 1.000000 0 0 5 | 0.125000 0.187500 0.250000 0.843137 0.188235 0.152941 1.000000 0.956863 0.427451 0.262745 1.000000 0 0 6 | 0.250000 0.312500 0.375000 0.956863 0.427451 0.262745 1.000000 0.992157 0.682353 0.380392 1.000000 0 0 7 | 0.375000 0.437500 0.500000 0.992157 0.682353 0.380392 1.000000 0.996078 0.878431 0.545098 1.000000 0 0 8 | 0.500000 0.562500 0.625000 0.850980 0.937255 0.545098 1.000000 0.650980 0.850980 0.415686 1.000000 0 0 9 | 0.625000 0.687500 0.750000 0.650980 0.850980 0.415686 1.000000 0.400000 0.741176 0.388235 1.000000 0 0 10 | 0.750000 0.812500 0.875000 0.400000 0.741176 0.388235 1.000000 0.101961 0.596078 0.313725 1.000000 0 0 11 | 0.875000 0.937500 1.000000 0.101961 0.596078 0.313725 1.000000 0.000000 0.407843 0.215686 1.000000 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/Spectral.ggr: -------------------------------------------------------------------------------- 1 | GIMP Gradient 2 | Name: Spectral 3 | 8 4 | 0.000000 0.062500 0.125000 0.619608 0.003922 0.258824 1.000000 0.835294 0.243137 0.309804 1.000000 0 0 5 | 0.125000 0.187500 0.250000 0.835294 0.243137 0.309804 1.000000 0.956863 0.427451 0.262745 1.000000 0 0 6 | 0.250000 0.312500 0.375000 0.956863 0.427451 0.262745 1.000000 0.992157 0.682353 0.380392 1.000000 0 0 7 | 0.375000 0.437500 0.500000 0.992157 0.682353 0.380392 1.000000 0.996078 0.878431 0.545098 1.000000 0 0 8 | 0.500000 0.562500 0.625000 0.901961 0.960784 0.596078 1.000000 0.670588 0.866667 0.643137 1.000000 0 0 9 | 0.625000 0.687500 0.750000 0.670588 0.866667 0.643137 1.000000 0.400000 0.760784 0.647059 1.000000 0 0 10 | 0.750000 0.812500 0.875000 0.400000 0.760784 0.647059 1.000000 0.196078 0.533333 0.741176 1.000000 0 0 11 | 0.875000 0.937500 1.000000 0.196078 0.533333 0.741176 1.000000 0.368627 0.309804 0.635294 1.000000 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/Split_Complementary_01a.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/nd/turanj/Split_Complementary_01a.cpt 2 | # autogenerated GMT palette "Turanj Split Complementary 01a" 3 | # cptutils version 1.44, Tue Mar 13 13:15:19 2012 4 | # COLOR_MODEL = RGB 5 | 0 0 25 255 25 128 63 128 6 | 25 128 63 128 50 255 102 0 7 | 50 255 102 0 75 128 178 76 8 | 75 128 178 76 100 0 255 153 9 | B 0 0 0 10 | F 255 255 255 11 | N 255 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/Split_Complementary_07.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/nd/vermillion/Split_Complementary_07.cpt 2 | # autogenerated GMT palette "Vermillion Split Complementary 07" 3 | # cptutils version 1.44, Tue Mar 13 13:16:06 2012 4 | # COLOR_MODEL = RGB 5 | 0 217 22 43 25 108 62 149 6 | 25 108 62 149 50 0 102 255 7 | 50 0 102 255 75 0 147 137 8 | 75 0 147 137 100 0 191 19 9 | B 0 0 0 10 | F 255 255 255 11 | N 255 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/StepSeq25.cpt: -------------------------------------------------------------------------------- 1 | # ../cpt/ncar/StepSeq25.cpt 2 | # autogenerated GMT palette "StepSeq25.txt" 3 | # cptutils version 1.41, Fri Jan 20 21:02:01 2012 4 | # COLOR_MODEL = RGB 5 | 0.000000e+00 153 15 15 1.000000e+00 153 15 15 6 | 1.000000e+00 178 44 44 2.000000e+00 178 44 44 7 | 2.000000e+00 204 81 81 3.000000e+00 204 81 81 8 | 3.000000e+00 229 126 126 4.000000e+00 229 126 126 9 | 4.000000e+00 255 178 178 5.000000e+00 255 178 178 10 | 5.000000e+00 153 84 15 6.000000e+00 153 84 15 11 | 6.000000e+00 178 111 44 7.000000e+00 178 111 44 12 | 7.000000e+00 204 142 81 8.000000e+00 204 142 81 13 | 8.000000e+00 229 177 126 9.000000e+00 229 177 126 14 | 9.000000e+00 255 216 178 1.000000e+01 255 216 178 15 | 1.000000e+01 107 153 15 1.100000e+01 107 153 15 16 | 1.100000e+01 133 178 44 1.200000e+01 133 178 44 17 | 1.200000e+01 163 204 81 1.300000e+01 163 204 81 18 | 1.300000e+01 195 229 126 1.400000e+01 195 229 126 19 | 1.400000e+01 229 255 178 1.500000e+01 229 255 178 20 | 1.500000e+01 15 107 153 1.600000e+01 15 107 153 21 | 1.600000e+01 44 133 178 1.700000e+01 44 133 178 22 | 1.700000e+01 81 163 204 1.800000e+01 81 163 204 23 | 1.800000e+01 126 195 229 1.900000e+01 126 195 229 24 | 1.900000e+01 178 229 255 2.000000e+01 178 229 255 25 | 2.000000e+01 38 15 153 2.100000e+01 38 15 153 26 | 2.100000e+01 66 44 178 2.200000e+01 66 44 178 27 | 2.200000e+01 101 81 204 2.300000e+01 101 81 204 28 | 2.300000e+01 143 126 229 2.400000e+01 143 126 229 29 | 2.400000e+01 191 178 255 2.500000e+01 191 178 255 30 | B 0 0 0 31 | F 255 255 255 32 | N 255 0 0 33 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/Tertiary_01b.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/nd/terra/Tertiary_01b.cpt 2 | # autogenerated GMT palette "Terra Tertiary 01b" 3 | # cptutils version 1.44, Tue Mar 13 13:15:33 2012 4 | # COLOR_MODEL = RGB 5 | 0 0 13 127 25 96 17 82 6 | 25 96 17 82 50 191 21 38 7 | 50 191 21 38 75 222 138 19 8 | 75 222 138 19 100 252 255 0 9 | B 0 0 0 10 | F 255 255 255 11 | N 255 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/YlOrRd_09.ggr: -------------------------------------------------------------------------------- 1 | GIMP Gradient 2 | Name: YlOrRd_09 3 | 9 4 | 0.000000 0.055550 0.111100 1.000000 1.000000 0.800000 1.000000 1.000000 1.000000 0.800000 1.000000 0 0 5 | 0.111100 0.166650 0.222200 1.000000 0.929412 0.627451 1.000000 1.000000 0.929412 0.627451 1.000000 0 0 6 | 0.222200 0.277750 0.333300 0.996078 0.850980 0.462745 1.000000 0.996078 0.850980 0.462745 1.000000 0 0 7 | 0.333300 0.388850 0.444400 0.996078 0.698039 0.298039 1.000000 0.996078 0.698039 0.298039 1.000000 0 0 8 | 0.444400 0.500000 0.555600 0.992157 0.552941 0.235294 1.000000 0.992157 0.552941 0.235294 1.000000 0 0 9 | 0.555600 0.611150 0.666700 0.988235 0.305882 0.164706 1.000000 0.988235 0.305882 0.164706 1.000000 0 0 10 | 0.666700 0.722250 0.777800 0.890196 0.101961 0.109804 1.000000 0.890196 0.101961 0.109804 1.000000 0 0 11 | 0.777800 0.833350 0.888900 0.741176 0.000000 0.149020 1.000000 0.741176 0.000000 0.149020 1.000000 0 0 12 | 0.888900 0.944450 1.000000 0.501961 0.000000 0.149020 1.000000 0.501961 0.000000 0.149020 1.000000 0 0 13 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/__init__.py: -------------------------------------------------------------------------------- 1 | # really a data directoy, but setup complains if there isn't an init file. 2 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/aspectcolr.cpt: -------------------------------------------------------------------------------- 1 | # GMT colour palette table (cpt) 2 | # grass2cpt ouput 3 | # COLOR_MODEL = RGB 4 | 0.000000e+00 255 255 255 1.000000e+00 255 255 0 5 | 1.000000e+00 255 255 0 9.000000e+01 0 255 0 6 | 9.000000e+01 0 255 0 1.800000e+02 0 255 255 7 | 1.800000e+02 0 255 255 2.700000e+02 255 0 0 8 | 2.700000e+02 255 0 0 3.600000e+02 255 255 0 9 | N 128 128 128 10 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/brightsong2.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/pj/1/brightsong2.cpt 2 | # autogenerated GMT palette "pj-brightsong2" 3 | # cptutils version 1.44, Tue Mar 13 13:17:05 2012 4 | # COLOR_MODEL = RGB 5 | 0 74 228 103 29 255 255 64 6 | 29 255 255 64 100 255 63 153 7 | B 0 0 0 8 | F 255 255 255 9 | N 255 0 0 10 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/butterflyfairy.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/rc/butterflyfairy.cpt 2 | # autogenerated GMT palette "~1butterflyfairy" 3 | # cptutils version 1.44, Tue Mar 13 13:17:36 2012 4 | # COLOR_MODEL = RGB 5 | 0 167 77 38 25 215 167 105 6 | 25 215 167 105 50 133 203 206 7 | 50 133 203 206 75 19 88 138 8 | 75 19 88 138 100 1 6 9 9 | B 0 0 0 10 | F 255 255 255 11 | N 255 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/caleblack.cpt: -------------------------------------------------------------------------------- 1 | # taken from visit's caleblack 2 | # 3 | # 4 | # COLOR_MODEL = RGB 5 | 0 0 0 0 25 0 0 255 6 | 25 0 0 255 50 0 255 255 7 | 50 0 255 255 75 0 255 0 8 | 75 0 255 0 100 255 255 0 9 | 100 255 255 0 125 255 0 0 10 | 125 255 0 0 150 255 0 255 11 | B 0 0 0 12 | F 255 255 255 13 | N 255 0 0 14 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/cbcSpectral.cpt: -------------------------------------------------------------------------------- 1 | # cbcont/div/cbcSpectral.cpt 2 | # autogenerated GMT palette "Spectral_11" 3 | # cptutils version 1.36, Sun Apr 18 18:51:48 2010 4 | # COLOR_MODEL = RGB 5 | -5.500000e+00 158 1 66 -5.000000e+00 158 1 66 6 | -5.000000e+00 158 1 66 -4.500000e+00 186 32 72 7 | -4.500000e+00 186 32 72 -4.000000e+00 213 62 79 8 | -4.000000e+00 213 62 79 -3.500000e+00 228 86 73 9 | -3.500000e+00 228 86 73 -3.000000e+00 244 109 67 10 | -3.000000e+00 244 109 67 -2.500000e+00 249 142 82 11 | -2.500000e+00 249 142 82 -2.000000e+00 253 174 97 12 | -2.000000e+00 253 174 97 -1.500000e+00 254 199 118 13 | -1.500000e+00 254 199 118 -1.000000e+00 254 224 139 14 | -1.000000e+00 254 224 139 -5.000000e-01 254 240 165 15 | -5.000000e-01 254 240 165 0.000000e+00 255 255 191 16 | 0.000000e+00 255 255 191 5.000000e-01 242 250 172 17 | 5.000000e-01 242 250 172 1.000000e+00 230 245 152 18 | 1.000000e+00 230 245 152 1.500000e+00 200 233 158 19 | 1.500000e+00 200 233 158 2.000000e+00 171 221 164 20 | 2.000000e+00 171 221 164 2.500000e+00 136 208 164 21 | 2.500000e+00 136 208 164 3.000000e+00 102 194 165 22 | 3.000000e+00 102 194 165 3.500000e+00 76 165 177 23 | 3.500000e+00 76 165 177 4.000000e+00 50 136 189 24 | 4.000000e+00 50 136 189 4.500000e+00 72 108 176 25 | 4.500000e+00 72 108 176 5.000000e+00 94 79 162 26 | 5.000000e+00 94 79 162 5.500000e+00 94 79 162 27 | B 0 0 0 28 | F 0 0 0 29 | N 0 0 0 30 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/colormaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/stompy/plot/cmaps/colormaps.png -------------------------------------------------------------------------------- /stompy/plot/cmaps/diverging.cpt: -------------------------------------------------------------------------------- 1 | # COLOR_MODEL = RGB 2 | 0 204 255 255 45 146 194 255 3 | 35 146 194 255 50 0 0 0 4 | 50 0 0 0 65 239 207 136 5 | 65 239 207 136 100 255 255 186 6 | B 0 0 0 7 | F 255 255 255 8 | N 255 0 255 9 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/dkbluered.cpt: -------------------------------------------------------------------------------- 1 | # autogenerated GMT palette "" 2 | # cptutils version 1.31, Mon Feb 2 00:31:48 2009 3 | # COLOR_MODEL = RGB 4 | 0.000000e+00 7 0 51 1.000000e+00 17 0 79 5 | 1.000000e+00 17 0 79 2.000000e+00 25 0 107 6 | 2.000000e+00 25 0 107 3.000000e+00 28 0 135 7 | 3.000000e+00 28 0 135 4.000000e+00 25 0 163 8 | 4.000000e+00 25 0 163 5.000000e+00 22 0 191 9 | 5.000000e+00 22 0 191 6.000000e+00 15 0 219 10 | 6.000000e+00 15 0 219 7.000000e+00 2 0 247 11 | 7.000000e+00 2 0 247 8.000000e+00 20 33 255 12 | 8.000000e+00 20 33 255 9.000000e+00 56 76 255 13 | 9.000000e+00 56 76 255 1.000000e+01 89 117 255 14 | 1.000000e+01 89 117 255 1.100000e+01 122 153 255 15 | 1.100000e+01 122 153 255 1.200000e+01 155 186 255 16 | 1.200000e+01 155 186 255 1.300000e+01 191 211 255 17 | 1.300000e+01 191 211 255 1.400000e+01 224 237 255 18 | 1.400000e+01 224 237 255 1.500000e+01 255 255 255 19 | 1.500000e+01 255 255 255 1.600000e+01 255 237 224 20 | 1.600000e+01 255 237 224 1.700000e+01 255 211 191 21 | 1.700000e+01 255 211 191 1.800000e+01 255 186 155 22 | 1.800000e+01 255 186 155 1.900000e+01 255 153 122 23 | 1.900000e+01 255 153 122 2.000000e+01 255 117 89 24 | 2.000000e+01 255 117 89 2.100000e+01 255 76 56 25 | 2.100000e+01 255 76 56 2.200000e+01 255 33 20 26 | 2.200000e+01 255 33 20 2.300000e+01 247 0 2 27 | 2.300000e+01 247 0 2 2.400000e+01 219 0 15 28 | 2.400000e+01 219 0 15 2.500000e+01 191 0 22 29 | 2.500000e+01 191 0 22 2.600000e+01 163 0 25 30 | 2.600000e+01 163 0 25 2.700000e+01 135 0 28 31 | 2.700000e+01 135 0 28 2.800000e+01 107 0 25 32 | 2.800000e+01 107 0 25 2.900000e+01 79 0 17 33 | 2.900000e+01 79 0 17 3.000000e+01 53 0 7 34 | B 0 0 0 35 | F 255 255 255 36 | N 255 0 0 37 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/es_platinum_06.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/es/platinum/es_platinum_06.cpt 2 | # autogenerated GMT palette "ES Platinum 06" 3 | # cptutils version 1.44, Tue Mar 13 13:18:16 2012 4 | # COLOR_MODEL = RGB 5 | 0 30 35 39 100 240 242 244 6 | B 0 0 0 7 | F 255 255 255 8 | N 255 0 0 9 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/f-30-31-32.cpt: -------------------------------------------------------------------------------- 1 | # f-30-31-32.cpt 2 | # autogenerated GMT palette "formula-30-31-32.rgb" 3 | # cptutils version 1.44, Tue Mar 13 13:00:30 2012 4 | # COLOR_MODEL = RGB 5 | 0 0 0 0 1 0 0 16 6 | 1 0 0 16 2 0 0 32 7 | 2 0 0 32 3 0 0 49 8 | 3 0 0 49 4 0 0 65 9 | 4 0 0 65 5 0 0 81 10 | 5 0 0 81 6 0 0 97 11 | 6 0 0 97 7 0 0 113 12 | 7 0 0 113 8 0 0 130 13 | 8 0 0 130 9 0 0 146 14 | 9 0 0 146 10 0 0 162 15 | 10 0 0 162 11 0 0 178 16 | 11 0 0 178 12 0 0 194 17 | 12 0 0 194 13 0 0 210 18 | 13 0 0 210 14 0 0 227 19 | 14 0 0 227 15 0 0 243 20 | 15 0 0 243 16 3 0 255 21 | 16 3 0 255 17 16 0 255 22 | 17 16 0 255 18 28 0 255 23 | 18 28 0 255 19 41 0 255 24 | 19 41 0 255 20 54 0 255 25 | 20 54 0 255 21 66 0 255 26 | 21 66 0 255 22 79 0 255 27 | 22 79 0 255 23 92 0 255 28 | 23 92 0 255 24 104 0 255 29 | 24 104 0 255 25 117 0 255 30 | 25 117 0 255 26 130 0 255 31 | 26 130 0 255 27 142 4 251 32 | 27 142 4 251 28 155 12 243 33 | 28 155 12 243 29 168 21 234 34 | 29 168 21 234 30 180 29 226 35 | 30 180 29 226 31 193 37 218 36 | 31 193 37 218 32 206 45 210 37 | 32 206 45 210 33 218 53 202 38 | 33 218 53 202 34 231 61 194 39 | 34 231 61 194 35 243 69 186 40 | 35 243 69 186 36 255 77 178 41 | 36 255 77 178 37 255 85 170 42 | 37 255 85 170 38 255 93 162 43 | 38 255 93 162 39 255 102 153 44 | 39 255 102 153 40 255 110 145 45 | 40 255 110 145 41 255 118 137 46 | 41 255 118 137 42 255 126 129 47 | 42 255 126 129 43 255 134 121 48 | 43 255 134 121 44 255 142 113 49 | 44 255 142 113 45 255 150 105 50 | 45 255 150 105 46 255 158 97 51 | 46 255 158 97 47 255 166 89 52 | 47 255 166 89 48 255 174 81 53 | 48 255 174 81 49 255 182 73 54 | 49 255 182 73 50 255 191 64 55 | 50 255 191 64 51 255 199 56 56 | 51 255 199 56 52 255 207 48 57 | 52 255 207 48 53 255 215 40 58 | 53 255 215 40 54 255 223 32 59 | 54 255 223 32 55 255 231 24 60 | 55 255 231 24 56 255 239 16 61 | 56 255 239 16 57 255 247 8 62 | 57 255 247 8 58 255 255 2 63 | 58 255 255 2 59 255 255 53 64 | 59 255 255 53 60 255 255 103 65 | 60 255 255 103 61 255 255 154 66 | 61 255 255 154 62 255 255 204 67 | 62 255 255 204 63 255 255 255 68 | B 0 0 0 69 | F 255 255 255 70 | N 255 0 0 71 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/haxby.cpt: -------------------------------------------------------------------------------- 1 | # GMT colour palette table (cpt) 2 | # grass2cpt ouput 3 | # COLOR_MODEL = RGB 4 | 0.000000e+00 37 57 175 1.000000e+01 40 127 251 5 | 1.000000e+01 40 127 251 2.000000e+01 50 190 255 6 | 2.000000e+01 50 190 255 3.000000e+01 106 235 255 7 | 3.000000e+01 106 235 255 4.000000e+01 138 236 174 8 | 4.000000e+01 138 236 174 5.000000e+01 205 255 162 9 | 5.000000e+01 205 255 162 6.000000e+01 240 236 121 10 | 6.000000e+01 240 236 121 7.000000e+01 255 189 87 11 | 7.000000e+01 255 189 87 8.000000e+01 255 161 68 12 | 8.000000e+01 255 161 68 9.000000e+01 255 186 133 13 | 9.000000e+01 255 186 133 1.000000e+02 255 255 255 14 | N 128 128 128 15 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/hot_and_cold.cpt: -------------------------------------------------------------------------------- 1 | # taken from visit's hot_and_cold 2 | # 3 | # 4 | # COLOR_MODEL = RGB 5 | 0 0 255 255 45 0 0 255 6 | 45 0 0 255 50 64 0 64 7 | 50 64 0 64 55 255 0 0 8 | 55 255 0 0 100 255 255 0 9 | B 0 0 0 10 | F 255 255 255 11 | N 255 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/hot_desaturated.cpt: -------------------------------------------------------------------------------- 1 | # taken from visit's hot_desaturated 2 | # 3 | # 4 | # COLOR_MODEL = RGB 5 | 0 71 71 219 25 0 0 91 6 | 25 0 0 91 50 0 255 255 7 | 50 0 255 255 75 0 127 0 8 | 75 0 127 0 100 255 255 0 9 | 100 255 255 0 125 255 96 0 10 | 125 255 96 0 150 107 0 0 11 | 150 107 0 0 175 224 76 76 12 | B 0 0 0 13 | F 255 255 255 14 | N 255 0 0 15 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/iris1.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/pj/3/iris1.cpt 2 | # autogenerated GMT palette "pj3-iris1" 3 | # cptutils version 1.44, Tue Mar 13 13:17:01 2012 4 | # COLOR_MODEL = RGB 5 | 0.00 255 255 255 16.99 114 24 140 6 | 16.99 114 24 140 30.00 76 57 131 7 | 30.00 76 57 131 52.00 17 162 2 8 | 52.00 17 162 2 77.00 65 195 65 9 | 77.00 65 195 65 95.00 58 116 68 10 | 95.00 58 116 68 100.00 58 116 68 11 | B 0 0 0 12 | F 255 255 255 13 | N 255 0 0 14 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/nonlinear_gray01.ggr: -------------------------------------------------------------------------------- 1 | GIMP Gradient 2 | Name: nonlinear_gray01 3 | 4 4 | 0.000000 0.057487 0.110294 0.000000 0.000000 0.000000 1.000000 0.250000 0.250000 0.250000 1.000000 0 0 0 0 5 | 0.110294 0.308824 0.493984 0.250000 0.250000 0.250000 1.000000 0.500000 0.500000 0.500000 1.000000 0 0 0 0 6 | 0.493984 0.625000 0.864973 0.500000 0.500000 0.500000 1.000000 0.750000 0.750000 0.750000 1.000000 0 0 0 0 7 | 0.864973 0.949198 1.000000 0.750000 0.750000 0.750000 1.000000 1.000000 1.000000 1.000000 1.000000 0 0 0 0 8 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/nrl_sirkes.cpt: -------------------------------------------------------------------------------- 1 | # ../cpt/ncar/nrl_sirkes.cpt 2 | # autogenerated GMT palette "nrl_sirkes.txt" 3 | # cptutils version 1.41, Fri Jan 20 21:02:01 2012 4 | # COLOR_MODEL = RGB 5 | 0.000000e+00 0 97 128 1.000000e+00 0 97 128 6 | 1.000000e+00 0 128 161 2.000000e+00 0 128 161 7 | 2.000000e+00 0 161 191 3.000000e+00 0 161 191 8 | 3.000000e+00 0 191 224 4.000000e+00 0 191 224 9 | 4.000000e+00 0 224 255 5.000000e+00 0 224 255 10 | 5.000000e+00 0 255 255 6.000000e+00 0 255 255 11 | 6.000000e+00 51 252 252 7.000000e+00 51 252 252 12 | 7.000000e+00 102 252 252 8.000000e+00 102 252 252 13 | 8.000000e+00 153 252 252 9.000000e+00 153 252 252 14 | 9.000000e+00 204 252 252 1.000000e+01 204 252 252 15 | 1.000000e+01 255 255 255 1.100000e+01 255 255 255 16 | 1.100000e+01 252 252 0 1.200000e+01 252 252 0 17 | 1.200000e+01 252 224 0 1.300000e+01 252 224 0 18 | 1.300000e+01 252 191 0 1.400000e+01 252 191 0 19 | 1.400000e+01 252 161 0 1.500000e+01 252 161 0 20 | 1.500000e+01 252 128 0 1.600000e+01 252 128 0 21 | 1.600000e+01 252 97 0 1.700000e+01 252 97 0 22 | 1.700000e+01 252 64 0 1.800000e+01 252 64 0 23 | 1.800000e+01 252 33 0 1.900000e+01 252 33 0 24 | 1.900000e+01 191 0 0 2.000000e+01 191 0 0 25 | 2.000000e+01 128 0 0 2.100000e+01 128 0 0 26 | B 0 0 0 27 | F 255 255 255 28 | N 255 0 0 29 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/nrl_sirkes_nowhite.cpt: -------------------------------------------------------------------------------- 1 | # ../cpt/ncar/nrl_sirkes_nowhite.cpt 2 | # autogenerated GMT palette "nrl_sirkes_nowhite.txt" 3 | # cptutils version 1.41, Fri Jan 20 21:02:02 2012 4 | # COLOR_MODEL = RGB 5 | 0.000000e+00 0 97 128 1.000000e+00 0 97 128 6 | 1.000000e+00 0 128 161 2.000000e+00 0 128 161 7 | 2.000000e+00 0 161 191 3.000000e+00 0 161 191 8 | 3.000000e+00 0 191 224 4.000000e+00 0 191 224 9 | 4.000000e+00 0 224 255 5.000000e+00 0 224 255 10 | 5.000000e+00 0 255 255 6.000000e+00 0 255 255 11 | 6.000000e+00 51 252 252 7.000000e+00 51 252 252 12 | 7.000000e+00 102 252 252 8.000000e+00 102 252 252 13 | 8.000000e+00 153 252 252 9.000000e+00 153 252 252 14 | 9.000000e+00 252 252 0 1.000000e+01 252 252 0 15 | 1.000000e+01 252 224 0 1.100000e+01 252 224 0 16 | 1.100000e+01 252 191 0 1.200000e+01 252 191 0 17 | 1.200000e+01 252 161 0 1.300000e+01 252 161 0 18 | 1.300000e+01 252 128 0 1.400000e+01 252 128 0 19 | 1.400000e+01 252 97 0 1.500000e+01 252 97 0 20 | 1.500000e+01 252 64 0 1.600000e+01 252 64 0 21 | 1.600000e+01 252 33 0 1.700000e+01 252 33 0 22 | 1.700000e+01 191 0 0 1.800000e+01 191 0 0 23 | 1.800000e+01 128 0 0 1.900000e+01 128 0 0 24 | B 0 0 0 25 | F 255 255 255 26 | N 255 0 0 27 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/parrot.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/rc/parrot.cpt 2 | # autogenerated GMT palette "~1parrot" 3 | # cptutils version 1.44, Tue Mar 13 13:17:36 2012 4 | # COLOR_MODEL = RGB 5 | 0.00 195 0 33 45.00 231 211 85 6 | 45.00 231 211 85 55.00 231 211 85 7 | 55.00 231 211 85 85.01 0 29 112 8 | 85.01 0 29 112 100.00 0 29 112 9 | B 0 0 0 10 | F 255 255 255 11 | N 255 0 0 12 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/pm3d14.cpt: -------------------------------------------------------------------------------- 1 | # pm3d14.cpt 2 | # autogenerated GMT palette "pm3d14.rgb" 3 | # cptutils version 1.44, Tue Mar 13 12:59:53 2012 4 | # COLOR_MODEL = RGB 5 | 0 255 255 255 1 223 255 230 6 | 1 223 255 230 2 210 255 204 7 | 2 210 255 204 3 199 255 180 8 | 3 199 255 180 4 191 255 156 9 | 4 191 255 156 5 183 255 133 10 | 5 183 255 133 6 176 255 111 11 | 6 176 255 111 7 170 255 91 12 | 7 170 255 91 8 164 254 72 13 | 8 164 254 72 9 159 254 56 14 | 9 159 254 56 10 153 254 41 15 | 10 153 254 41 11 148 254 28 16 | 11 148 254 28 12 144 253 18 17 | 12 144 253 18 13 139 253 10 18 | 13 139 253 10 14 135 252 4 19 | 14 135 252 4 15 131 252 1 20 | 15 131 252 1 16 126 251 0 21 | 16 126 251 0 17 123 250 2 22 | 17 123 250 2 18 119 249 6 23 | 18 119 249 6 19 115 248 13 24 | 19 115 248 13 20 111 247 23 25 | 20 111 247 23 21 108 246 34 26 | 21 108 246 34 22 104 244 48 27 | 22 104 244 48 23 101 243 64 28 | 23 101 243 64 24 98 241 82 29 | 24 98 241 82 25 94 239 101 30 | 25 94 239 101 26 91 237 122 31 | 26 91 237 122 27 88 235 144 32 | 27 88 235 144 28 85 233 168 33 | 28 85 233 168 29 82 230 192 34 | 29 82 230 192 30 79 227 217 35 | 30 79 227 217 31 76 225 242 36 | 31 76 225 242 32 73 222 255 37 | 32 73 222 255 33 70 218 255 38 | 33 70 218 255 34 68 215 255 39 | 34 68 215 255 35 65 211 255 40 | 35 65 211 255 36 62 207 255 41 | 36 62 207 255 37 60 203 255 42 | 37 60 203 255 38 57 199 255 43 | 38 57 199 255 39 54 195 255 44 | 39 54 195 255 40 52 190 255 45 | 40 52 190 255 41 49 185 255 46 | 41 49 185 255 42 47 179 255 47 | 42 47 179 255 43 44 174 255 48 | 43 44 174 255 44 42 168 255 49 | 44 42 168 255 45 39 162 255 50 | 45 39 162 255 46 37 156 255 51 | 46 37 156 255 47 35 149 255 52 | 47 35 149 255 48 32 142 255 53 | 48 32 142 255 49 30 135 255 54 | 49 30 135 255 50 28 128 255 55 | 50 28 128 255 51 26 120 255 56 | 51 26 120 255 52 23 112 255 57 | 52 23 112 255 53 21 103 255 58 | 53 21 103 255 54 19 94 255 59 | 54 19 94 255 55 17 85 255 60 | 55 17 85 255 56 15 76 255 61 | 56 15 76 255 57 12 66 255 62 | 57 12 66 255 58 10 56 255 63 | 58 10 56 255 59 8 46 255 64 | 59 8 46 255 60 6 35 255 65 | 60 6 35 255 61 4 24 255 66 | 61 4 24 255 62 2 12 255 67 | 62 2 12 255 63 0 0 255 68 | B 0 0 0 69 | F 255 255 255 70 | N 255 0 0 71 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/poison.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/neota/elem/poison.cpt 2 | # autogenerated GMT palette "ELEM poison" 3 | # cptutils version 1.44, Tue Mar 13 13:16:19 2012 4 | # COLOR_MODEL = RGB 5 | 0.00 0 0 0 6.73 32 11 11 6 | 6.73 32 11 11 13.45 64 21 21 7 | 13.45 64 21 21 21.64 66 44 84 8 | 21.64 66 44 84 29.82 67 67 147 9 | 29.82 67 67 147 41.81 79 111 151 10 | 41.81 79 111 151 53.80 91 155 155 11 | 53.80 91 155 155 59.65 112 172 144 12 | 59.65 112 172 144 65.50 134 189 134 13 | 65.50 134 189 134 72.51 169 197 152 14 | 72.51 169 197 152 79.53 204 204 170 15 | 79.53 204 204 170 89.77 230 230 212 16 | 89.77 230 230 212 100.00 255 255 255 17 | B 0 0 0 18 | F 255 255 255 19 | N 255 0 0 20 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/saga-01.cpt: -------------------------------------------------------------------------------- 1 | # saga-01.cpt 2 | # autogenerated GMT palette "saga-01.txt" 3 | # cptutils version 1.44, Thu Mar 8 23:40:39 2012 4 | # COLOR_MODEL = RGB 5 | 0 0 1 128 1 0 7 167 6 | 1 0 7 167 2 0 25 202 7 | 2 0 25 202 3 0 53 230 8 | 3 0 53 230 4 0 88 248 9 | 4 0 88 248 5 1 127 255 10 | 5 1 127 255 6 7 167 248 11 | 6 7 167 248 7 25 202 230 12 | 7 25 202 230 8 53 230 202 13 | 8 53 230 202 9 88 248 167 14 | 9 88 248 167 10 128 255 128 15 | 10 128 255 128 11 167 248 88 16 | 11 167 248 88 12 202 230 53 17 | 12 202 230 53 13 230 202 25 18 | 13 230 202 25 14 248 167 7 19 | 14 248 167 7 15 255 128 1 20 | 15 255 128 1 16 248 88 0 21 | 16 248 88 0 17 230 53 0 22 | 17 230 53 0 18 202 25 0 23 | 18 202 25 0 19 167 7 0 24 | B 0 0 0 25 | F 255 255 255 26 | N 255 0 0 27 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/saga-02.cpt: -------------------------------------------------------------------------------- 1 | # saga-02.cpt 2 | # autogenerated GMT palette "saga-02.txt" 3 | # cptutils version 1.44, Thu Mar 8 23:40:39 2012 4 | # COLOR_MODEL = RGB 5 | 0 61 63 255 1 55 70 255 6 | 1 55 70 255 2 42 83 255 7 | 2 42 83 255 3 27 98 255 8 | 3 27 98 255 4 13 112 255 9 | 4 13 112 255 5 0 126 253 10 | 5 0 126 253 6 6 150 223 11 | 6 6 150 223 7 20 168 191 12 | 7 20 168 191 8 41 180 158 13 | 8 41 180 158 9 66 187 126 14 | 9 66 187 126 10 95 190 95 15 | 10 95 190 95 11 126 187 66 16 | 11 126 187 66 12 158 180 41 17 | 12 158 180 41 13 191 168 20 18 | 13 191 168 20 14 223 150 6 19 | 14 223 150 6 15 253 127 0 20 | 15 253 127 0 16 255 112 13 21 | 16 255 112 13 17 255 98 27 22 | 17 255 98 27 18 255 83 42 23 | 18 255 83 42 19 255 70 55 24 | B 0 0 0 25 | F 255 255 255 26 | N 255 0 0 27 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/saga-13.cpt: -------------------------------------------------------------------------------- 1 | # saga-13.cpt 2 | # autogenerated GMT palette "saga-13.txt" 3 | # cptutils version 1.44, Thu Mar 8 23:40:39 2012 4 | # COLOR_MODEL = RGB 5 | 0 0 255 0 1 38 242 0 6 | 1 38 242 0 2 76 229 0 7 | 2 76 229 0 3 114 216 0 8 | 3 114 216 0 4 152 203 0 9 | 4 152 203 0 5 191 191 0 10 | 5 191 191 0 6 203 178 0 11 | 6 203 178 0 7 216 165 0 12 | 7 216 165 0 8 229 152 0 13 | 8 229 152 0 9 242 139 0 14 | 9 242 139 0 10 255 127 0 15 | 10 255 127 0 11 248 114 0 16 | 11 248 114 0 12 242 101 0 17 | 12 242 101 0 13 235 88 0 18 | 13 235 88 0 14 229 75 0 19 | 14 229 75 0 15 223 63 0 20 | 15 223 63 0 16 191 50 0 21 | 16 191 50 0 17 159 37 0 22 | 17 159 37 0 18 127 25 0 23 | 18 127 25 0 19 95 12 0 24 | B 0 0 0 25 | F 255 255 255 26 | N 255 0 0 27 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/saga-16.cpt: -------------------------------------------------------------------------------- 1 | # saga-16.cpt 2 | # autogenerated GMT palette "saga-16.txt" 3 | # cptutils version 1.44, Thu Mar 8 23:40:39 2012 4 | # COLOR_MODEL = RGB 5 | 0 0 25 152 1 0 50 178 6 | 1 0 50 178 2 0 76 203 7 | 2 0 76 203 3 0 101 229 8 | 3 0 101 229 4 0 127 255 9 | 4 0 127 255 5 40 141 244 10 | 5 40 141 244 6 80 156 233 11 | 6 80 156 233 7 120 170 222 12 | 7 120 170 222 8 160 185 211 13 | 8 160 185 211 9 200 200 200 14 | 9 200 200 200 10 211 185 160 15 | 10 211 185 160 11 222 170 120 16 | 11 222 170 120 12 233 156 80 17 | 12 233 156 80 13 244 141 40 18 | 13 244 141 40 14 255 127 0 19 | 14 255 127 0 15 229 101 0 20 | 15 229 101 0 16 203 76 0 21 | 16 203 76 0 17 178 50 0 22 | 17 178 50 0 18 152 25 0 23 | 18 152 25 0 19 127 0 0 24 | B 0 0 0 25 | F 255 255 255 26 | N 255 0 0 27 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/salinity_bgy1.ggr: -------------------------------------------------------------------------------- 1 | GIMP Gradient 2 | Name: salinity_bgy1 3 | 5 4 | 0.000000 0.028617 0.052464 0.952941 0.916626 0.900623 1.000000 0.843137 0.827967 0.413303 1.000000 0 0 0 0 5 | 0.052464 0.134340 0.262321 0.843137 0.827967 0.413303 1.000000 0.559091 0.764706 0.200923 1.000000 0 0 0 0 6 | 0.262321 0.441176 0.593800 0.559091 0.764706 0.200923 1.000000 0.521569 0.796078 0.807843 1.000000 0 0 0 0 7 | 0.593800 0.767091 0.945151 0.521569 0.796078 0.807843 1.000000 0.074510 0.345098 0.541176 1.000000 0 0 0 0 8 | 0.945151 0.973768 1.000000 0.074510 0.345098 0.541176 1.000000 0.000000 0.000000 0.000000 1.000000 0 0 0 0 9 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/salinity_bgy2.ggr: -------------------------------------------------------------------------------- 1 | GIMP Gradient 2 | Name: salinity_bgy2 3 | 5 4 | 0.000000 0.030749 0.063503 0.952941 0.916626 0.900623 1.000000 0.876863 0.767744 0.399647 1.000000 0 0 0 0 5 | 0.063503 0.195856 0.322193 0.876863 0.767744 0.399647 1.000000 0.559091 0.764706 0.200923 1.000000 0 0 0 0 6 | 0.322193 0.441176 0.593800 0.559091 0.764706 0.200923 1.000000 0.521569 0.796078 0.807843 1.000000 0 0 0 0 7 | 0.593800 0.767091 0.911096 0.521569 0.796078 0.807843 1.000000 0.074510 0.345098 0.541176 1.000000 0 0 0 0 8 | 0.911096 0.958556 1.000000 0.074510 0.345098 0.541176 1.000000 0.000000 0.000000 0.000000 1.000000 0 0 0 0 9 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/salinity_bgy3.ggr: -------------------------------------------------------------------------------- 1 | GIMP Gradient 2 | Name: salinity_bgy3 3 | 5 4 | 0.000000 0.030749 0.063503 0.952941 0.916626 0.756863 1.000000 0.876863 0.767744 0.399647 1.000000 0 0 0 0 5 | 0.063503 0.195856 0.322193 0.876863 0.767744 0.399647 1.000000 0.559091 0.764706 0.200923 1.000000 0 0 0 0 6 | 0.322193 0.441176 0.593800 0.559091 0.764706 0.200923 1.000000 0.521569 0.796078 0.807843 1.000000 0 0 0 0 7 | 0.593800 0.767091 0.948798 0.521569 0.796078 0.807843 1.000000 0.074510 0.345098 0.541176 1.000000 0 0 0 0 8 | 0.948798 0.973877 1.000000 0.074510 0.345098 0.541176 1.000000 0.024783 0.114783 0.180000 1.000000 0 0 0 0 9 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/scoutie.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/rc/scoutie.cpt 2 | # autogenerated GMT palette "~1scouty" 3 | # cptutils version 1.44, Tue Mar 13 13:17:37 2012 4 | # COLOR_MODEL = RGB 5 | 0.00 255 204 0 50.00 0 226 90 6 | 50.00 0 226 90 85.01 26 0 121 7 | 85.01 26 0 121 100.00 26 0 121 8 | B 0 0 0 9 | F 255 255 255 10 | N 255 0 0 11 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/slyphide.cpt: -------------------------------------------------------------------------------- 1 | # cpt-city/pj/4/slyphide.cpt 2 | # autogenerated GMT palette "pj4-slyphide" 3 | # cptutils version 1.44, Tue Mar 13 13:16:58 2012 4 | # COLOR_MODEL = RGB 5 | 0.00 243 240 193 20.00 190 222 159 6 | 20.00 190 222 159 45.00 123 163 93 7 | 45.00 123 163 93 73.00 116 114 159 8 | 73.00 116 114 159 92.99 76 31 112 9 | 92.99 76 31 112 100.00 76 31 112 10 | B 0 0 0 11 | F 255 255 255 12 | N 255 0 0 13 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/srtGreys05.cpt: -------------------------------------------------------------------------------- 1 | # GMT palette srtGreys05.cpt 2 | # 3 | # This product includes color specifications and designs 4 | # developed by Cynthia Brewer (http://colorbrewer.org/). 5 | # 6 | # Converted to the cpt format by J.J.Green 7 | # Serrated sequential palette with 6 colours 8 | # 9 | # COLOR_MODEL = RGB 10 | 0.00 217 217 217 1.00 247 247 247 11 | 1.00 189 189 189 2.00 217 217 217 12 | 2.00 150 150 150 3.00 189 189 189 13 | 3.00 099 099 099 4.00 150 150 150 14 | 4.00 037 037 037 5.00 099 099 099 15 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/temperature.cpt: -------------------------------------------------------------------------------- 1 | # extracted from GRID-Arendal 2 | # http://www.grida.no/graphicslib/detail/mean-surface-temperature_c7e4 3 | # 4 | # COLOR_MODEL = RGB 5 | 17.00 30 92 179 17.33 30 92 179 6 | 17.33 23 111 193 17.67 23 111 193 7 | 17.67 11 142 216 18.00 11 142 216 8 | 18.00 4 161 230 18.33 4 161 230 9 | 18.33 25 181 241 18.67 25 181 241 10 | 18.67 51 188 207 19.00 51 188 207 11 | 19.00 102 204 206 19.33 102 204 206 12 | 19.33 153 219 184 19.67 153 219 184 13 | 19.67 192 229 136 20.00 192 229 136 14 | 20.00 204 230 75 20.33 204 230 75 15 | 20.33 243 240 29 20.67 243 240 29 16 | 20.67 254 222 39 21.00 254 222 39 17 | 21.00 252 199 7 21.33 252 199 7 18 | 21.33 248 157 14 21.67 248 157 14 19 | 21.67 245 114 21 22.00 245 114 21 20 | 22.00 241 71 28 22.33 241 71 28 21 | 22.33 219 30 38 22.67 219 30 38 22 | 22.67 164 38 44 23.00 164 38 44 23 | B 0 0 0 24 | F 255 255 255 25 | N 255 0 0 26 | -------------------------------------------------------------------------------- /stompy/plot/cmaps/wave.cpt: -------------------------------------------------------------------------------- 1 | # GMT colour palette table (cpt) 2 | # grass2cpt ouput 3 | # COLOR_MODEL = RGB 4 | 0.000000e+00 255 85 85 1.667000e+01 170 170 0 5 | 1.667000e+01 170 170 0 3.333000e+01 85 255 85 6 | 3.333000e+01 85 255 85 5.000000e+01 0 170 170 7 | 5.000000e+01 0 170 170 6.667000e+01 85 85 255 8 | 6.667000e+01 85 85 255 8.333000e+01 170 0 170 9 | 8.333000e+01 170 0 170 1.000000e+02 255 85 85 10 | N 128 128 128 11 | -------------------------------------------------------------------------------- /stompy/plot/ggr.py: -------------------------------------------------------------------------------- 1 | """ Read Gimp .ggr gradient files. 2 | Ned Batchelder, http://nedbatchelder.com 3 | This code is in the public domain. 4 | """ 5 | 6 | __version__ = '1.0.20070915' 7 | 8 | import colorsys, math 9 | 10 | class GimpGradient: 11 | """ Read and interpret a Gimp .ggr gradient file. 12 | """ 13 | def __init__(self, f=None): 14 | if f: 15 | self.read(f) 16 | 17 | class _segment: 18 | pass 19 | 20 | def read(self, f): 21 | """ Read a .ggr file from f (either an open file or a file path). 22 | """ 23 | try: 24 | f=open(f) 25 | except TypeError: 26 | pass # probably f is already a file 27 | 28 | if f.readline().strip() != "GIMP Gradient": 29 | raise Exception("Not a GIMP gradient file") 30 | line = f.readline().strip() 31 | if not line.startswith("Name: "): 32 | raise Exception("Not a GIMP gradient file") 33 | self.name = line.split(": ", 1)[1] 34 | nsegs = int(f.readline().strip()) 35 | self.segs = [] 36 | for i in range(nsegs): 37 | line = f.readline().strip() 38 | seg = self._segment() 39 | (seg.l, seg.m, seg.r, 40 | seg.rl, seg.gl, seg.bl, _, 41 | seg.rr, seg.gr, seg.br, _, 42 | seg.fn, seg.space) = list(map(float, line.split()))[:13] 43 | self.segs.append(seg) 44 | 45 | def color(self, x): 46 | """ Get the color for the point x in the range [0..1). 47 | The color is returned as an rgb triple, with all values in the range 48 | [0..1). 49 | """ 50 | # Find the segment. 51 | for seg in self.segs: 52 | if seg.l <= x <= seg.r: 53 | break 54 | else: 55 | # No segment applies! Return black I guess. 56 | return (0,0,0) 57 | 58 | # Normalize the segment geometry. 59 | mid = (seg.m - seg.l)/(seg.r - seg.l) 60 | pos = (x - seg.l)/(seg.r - seg.l) 61 | 62 | # Assume linear (most common, and needed by most others). 63 | if pos <= mid: 64 | f = pos/mid/2 65 | else: 66 | f = (pos - mid)/(1 - mid)/2 + 0.5 67 | 68 | # Find the correct interpolation factor. 69 | if seg.fn == 1: # Curved 70 | f = math.pow(pos, math.log(0.5) / math.log(mid)); 71 | elif seg.fn == 2: # Sinusoidal 72 | f = (math.sin((-math.pi/2) + math.pi*f) + 1)/2 73 | elif seg.fn == 3: # Spherical increasing 74 | f -= 1 75 | f = math.sqrt(1 - f*f) 76 | elif seg.fn == 4: # Spherical decreasing 77 | f = 1 - math.sqrt(1 - f*f); 78 | 79 | # Interpolate the colors 80 | if seg.space == 0: 81 | c = ( 82 | seg.rl + (seg.rr-seg.rl) * f, 83 | seg.gl + (seg.gr-seg.gl) * f, 84 | seg.bl + (seg.br-seg.bl) * f 85 | ) 86 | elif seg.space in (1,2): 87 | hl, sl, vl = colorsys.rgb_to_hsv(seg.rl, seg.gl, seg.bl) 88 | hr, sr, vr = colorsys.rgb_to_hsv(seg.rr, seg.gr, seg.br) 89 | 90 | if seg.space == 1 and hr < hl: 91 | hr += 1 92 | elif seg.space == 2 and hr > hl: 93 | hr -= 1 94 | 95 | c = colorsys.hsv_to_rgb( 96 | (hl + (hr-hl) * f) % 1.0, 97 | sl + (sr-sl) * f, 98 | vl + (vr-vl) * f 99 | ) 100 | return c 101 | -------------------------------------------------------------------------------- /stompy/plot/mkanim.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Oct 6 12:20:10 2022 4 | 5 | @author: rusty 6 | """ 7 | 8 | import subprocess, os 9 | 10 | out='cimarron_v1.mp4' 11 | 12 | def mkanim(out,frame_path,fps=16,ffmpeg='ffmpeg',overwrite=True): 13 | """ 14 | out: path to output mp4 file 15 | frame_path: pattern for image paths, including %04d or similar for 16 | frame number. 17 | """ 18 | if os.path.exists(out): 19 | if overwrite: 20 | os.unlink(out) 21 | else: 22 | raise Exception(f"{out} exists") 23 | 24 | res=subprocess.run(['ffmpeg', 25 | '-framerate',str(fps), 26 | '-i',frame_path, 27 | # this deals with odd dimensions 28 | '-vf',"crop=trunc(iw/2)*2:trunc(ih/2)*2", 29 | '-c:v','libx264', 30 | '-preset','slow', 31 | '-profile:v','high', 32 | '-level:v','4.0', 33 | '-pix_fmt','yuv420p', 34 | '-crf','20','-r',str(fps), 35 | out], capture_output=True) 36 | print(res.stderr.decode()) 37 | -------------------------------------------------------------------------------- /stompy/plot/plot_wkb.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from matplotlib.collections import PatchCollection,LineCollection 3 | from matplotlib.path import Path 4 | from matplotlib.patches import PathPatch 5 | import matplotlib.pyplot as plt 6 | import numpy as np 7 | from .. import utils 8 | 9 | def plot_linestring(linestring,**kwargs): 10 | ax=kwargs.pop('ax',plt.gca()) 11 | c = np.array(linestring.coords) 12 | return ax.plot( c[:,0],c[:,1],**kwargs)[0] 13 | 14 | def plot_multilinestring(mls,**kwargs): 15 | ax=kwargs.pop('ax',plt.gca()) 16 | if mls.geom_type == 'MultiLineString': 17 | segs = [np.array(ls.coords) for ls in mls.geoms] 18 | coll = LineCollection(segs,**kwargs) 19 | ax.add_collection(coll) 20 | return coll 21 | else: 22 | return plot_linestring(mls,**kwargs) 23 | 24 | ######## 25 | # New, non-hacked way to plot polygons with holes 26 | # From: http://sgillies.net/blog/1013/painting-punctured-polygons-with-matplotlib/ 27 | 28 | def ring_coding(ob): 29 | # The codes will be all "LINETO" commands, except for "MOVETO"s at the 30 | # beginning of each subpath 31 | n = len(ob.coords) 32 | codes = np.ones(n, dtype=Path.code_type) * Path.LINETO 33 | codes[0] = Path.MOVETO 34 | # unsure of difference between CLOSEPOLY and leaving as is. 35 | # codes[-1] = Path.CLOSEPOLY # doesn't seem to make a difference 36 | return codes 37 | 38 | def pathify(polygon): 39 | # Convert coordinates to path vertices. Objects produced by Shapely's 40 | # analytic methods have the proper coordinate order, no need to sort. 41 | 42 | # 20170707: matplotlib pickier about ordering of internal rings, may have 43 | # reverse interiors. 44 | # 20170719: shapely doesn't guarantee one order or the other 45 | def ensure_orientation(a,ccw=True): 46 | """ 47 | take an array-like [N,2] set of points defining a polygon, 48 | return an array which is ordered ccw (or cw is ccw=False) 49 | """ 50 | a=np.asarray(a) # pre-shapely 2 51 | area=utils.signed_area(a) 52 | if ccw == (area<0): 53 | a=a[::-1] 54 | return a 55 | 56 | vertices = np.concatenate( 57 | [ ensure_orientation(polygon.exterior.coords,ccw=True)] 58 | + [ ensure_orientation(r.coords,ccw=False) for r in polygon.interiors]) 59 | codes = np.concatenate( 60 | [ring_coding(polygon.exterior)] 61 | + [ring_coding(r) for r in polygon.interiors]) 62 | return Path(vertices, codes) 63 | 64 | def poly_to_patch(polygon,**kwargs): 65 | return PathPatch(pathify(polygon), **kwargs) 66 | 67 | def multipoly_to_patches(multipoly,*args,**kwargs): 68 | patches = [poly_to_patch(p) for p in multipoly.geoms] 69 | return PatchCollection(patches,*args,**kwargs) 70 | 71 | def plot_polygon(p,*args,**kwargs): 72 | if 'holes' in kwargs: 73 | print("dropping obsolete holes keyword argument") 74 | del kwargs['holes'] 75 | 76 | ax = kwargs.pop('ax',plt.gca()) 77 | 78 | patch = poly_to_patch(p,*args,**kwargs) 79 | ax.add_patch(patch) 80 | return patch 81 | 82 | def plot_multipolygon(mp,*args,**kwargs): 83 | if 'holes' in kwargs: 84 | print("dropping obsolete holes keyword argument") 85 | del kwargs['holes'] 86 | ax = kwargs.pop('ax',plt.gca()) 87 | coll = multipoly_to_patches(mp,*args,**kwargs) 88 | ax.add_collection( coll ) 89 | return coll 90 | 91 | 92 | def plot_wkb(g,*args,**kwargs): 93 | if g.geom_type == 'MultiPolygon': 94 | return plot_multipolygon(g,*args,**kwargs) 95 | elif g.geom_type=='Polygon': 96 | return plot_polygon(g,*args,**kwargs) 97 | elif g.geom_type == 'MultiLineString': 98 | return plot_multilinestring(g,*args,**kwargs) 99 | elif g.geom_type =='LineString': 100 | return plot_linestring(g,*args,**kwargs) 101 | else: 102 | raise Exception("no match to type") 103 | 104 | -------------------------------------------------------------------------------- /stompy/priority_queue.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | # Priority dictionary using binary heaps 4 | # David Eppstein, UC Irvine, 8 Mar 2002 5 | 6 | class priorityDictionary(dict): 7 | def __init__(self): 8 | '''Initialize priorityDictionary by creating binary heap 9 | of pairs (value,key). Note that changing or removing a dict entry will 10 | not remove the old pair from the heap until it is found by smallest() or 11 | until the heap is rebuilt.''' 12 | self.__heap = [] 13 | dict.__init__(self) 14 | 15 | def smallest(self): 16 | '''Find smallest item after removing deleted items from heap.''' 17 | if len(self) == 0: 18 | raise IndexError( "smallest of empty priorityDictionary") 19 | heap = self.__heap 20 | while heap[0][1] not in self or self[heap[0][1]] != heap[0][0]: 21 | lastItem = heap.pop() 22 | insertionPoint = 0 23 | while 1: 24 | smallChild = 2*insertionPoint+1 25 | if smallChild+1 < len(heap) and \ 26 | heap[smallChild] > heap[smallChild+1]: 27 | smallChild += 1 28 | if smallChild >= len(heap) or lastItem <= heap[smallChild]: 29 | heap[insertionPoint] = lastItem 30 | break 31 | heap[insertionPoint] = heap[smallChild] 32 | insertionPoint = smallChild 33 | return heap[0][1] 34 | 35 | def __iter__(self): 36 | '''Create destructive sorted iterator of priorityDictionary.''' 37 | def iterfn(): 38 | while len(self) > 0: 39 | x = self.smallest() 40 | yield x 41 | del self[x] 42 | return iterfn() 43 | 44 | def __setitem__(self,key,val): 45 | '''Change value stored in dictionary and add corresponding 46 | pair to heap. Rebuilds the heap if the number of deleted items grows 47 | too large, to avoid memory leakage.''' 48 | dict.__setitem__(self,key,val) 49 | heap = self.__heap 50 | if len(heap) > 2 * len(self): 51 | self.__heap = [(v,k) for k,v in self.items()] 52 | self.__heap.sort() # builtin sort likely faster than O(n) heapify 53 | else: 54 | newPair = (val,key) 55 | insertionPoint = len(heap) 56 | heap.append(None) 57 | while insertionPoint > 0 and \ 58 | newPair < heap[(insertionPoint-1)//2]: 59 | heap[insertionPoint] = heap[(insertionPoint-1)//2] 60 | insertionPoint = (insertionPoint-1)//2 61 | heap[insertionPoint] = newPair 62 | 63 | def setdefault(self,key,val): 64 | '''Reimplement setdefault to call our customized __setitem__.''' 65 | if key not in self: 66 | self[key] = val 67 | return self[key] 68 | -------------------------------------------------------------------------------- /stompy/scriptable.py: -------------------------------------------------------------------------------- 1 | """ 2 | provide command line access to methods on classes 3 | """ 4 | from __future__ import print_function 5 | 6 | import sys 7 | import getopt 8 | 9 | class Unbuffered(object): 10 | def __init__(self, stream): 11 | self.stream = stream 12 | def write(self, data): 13 | self.stream.write(data) 14 | self.stream.flush() 15 | def __getattr__(self, attr): 16 | return getattr(self.stream, attr) 17 | 18 | class Scriptable(object): 19 | def invoke_command(self,cmd,args): 20 | meth_name = "cmd_" + cmd 21 | 22 | try: 23 | f = getattr(self,meth_name) 24 | except AttributeError: 25 | print("Command '%s' is not recognized"%cmd) 26 | self.cmd_help() 27 | sys.exit(1) 28 | f(*args) 29 | 30 | def cmd_help(self): 31 | """ 32 | List available commands 33 | """ 34 | print("Available commands") 35 | for k in dir(self): 36 | if k.startswith('cmd_'): 37 | v=getattr(self,k) 38 | doc=(v.__doc__ or "undocumented").strip() 39 | # 9 seems to work for the usual indentation (8 spaces) 40 | # plus the 9 to line up with '%15s: ' below 41 | doc=doc.replace("\n","\n"+" "*9) 42 | print("%15s: %s"%(k.replace('cmd_',''),doc)) 43 | 44 | cli_options="h" # can be overridden in subclasses 45 | def cli_usage(self): 46 | print("python {} [command]".format(sys.argv[0]) ) 47 | print(" Command is one of:" ) 48 | for att in dir(self): 49 | if att.startswith('cmd_'): 50 | print(" "+att[4:]) 51 | print("Use help for more info") 52 | def cli_handle_option(self,opt,val): 53 | if opt == '-h': 54 | self.cli_usage() 55 | sys.exit(0) 56 | else: 57 | print("Unhandled option '%s'"%opt) 58 | self.cli_usage() 59 | seys.exit(1) 60 | 61 | def main(self,args=None): 62 | """ parse command line and start making things 63 | """ 64 | if args is None: 65 | args = sys.argv[1:] 66 | 67 | try: 68 | opts,rest = getopt.getopt(args, self.cli_options) 69 | except getopt.GetoptError: 70 | self.cli_usage() 71 | sys.exit(1) 72 | 73 | for opt,val in opts: 74 | self.cli_handle_option(opt,val) 75 | 76 | if len(rest) > 0: 77 | cmd = rest[0] 78 | rest = rest[1:] 79 | else: 80 | # default action: 81 | cmd = 'default' 82 | 83 | # try making all output unbuffered: 84 | sys.stdout = Unbuffered(sys.stdout) 85 | 86 | # DependencyGraph.check_timestamps = self.check_timestamps 87 | self.invoke_command(cmd,rest) 88 | -------------------------------------------------------------------------------- /stompy/spatial/__init__.py: -------------------------------------------------------------------------------- 1 | # nothing 2 | -------------------------------------------------------------------------------- /stompy/spatial/algorithms.py: -------------------------------------------------------------------------------- 1 | import shapely.ops 2 | from shapely import geometry 3 | 4 | def cut_polygon(poly,line): 5 | # slice the exterior separately from interior, then recombine 6 | ext_sliced=poly.exterior.union( line ) 7 | ext_poly=geometry.Polygon(poly.exterior) 8 | int_sliced=[ p.union(line) 9 | for p in poly.interiors ] 10 | 11 | ext_parts, _dangles,_cuts,_invalids = shapely.ops.polygonize_full( ext_sliced ) 12 | ext_parts=list(ext_parts) # so we can update entries 13 | 14 | # It's possible to introduce some new area here - places where the cut line 15 | # goes outside the exterior but forms a loop with the exterior. 16 | 17 | ext_parts=[p_ext 18 | for p_ext in ext_parts 19 | if p_ext.intersection(ext_poly).area / p_ext.area > 0.99 ] 20 | 21 | for p in int_sliced: 22 | int_parts, _dangles,_cuts,_invalids = shapely.ops.polygonize_full( p ) 23 | # remove from an ext_part if there's overlap 24 | for p_int in int_parts: 25 | for i_ext, p_ext in enumerate(ext_parts): 26 | if p_ext.intersects(p_int): 27 | ext_parts[i_ext] = p_ext.difference(p_int) 28 | 29 | return ext_parts 30 | -------------------------------------------------------------------------------- /stompy/spatial/geom_types.py: -------------------------------------------------------------------------------- 1 | from osgeo import ogr 2 | 3 | # map geometry types to names: 4 | ogr2text = { 5 | ogr.wkbGeometryCollection:'GeometryCollection', 6 | ogr.wkbGeometryCollection25D:'GeometryCollection25D', 7 | ogr.wkbLineString:'LineString', 8 | ogr.wkbLineString25D:'LineString25D', 9 | ogr.wkbLinearRing:'LinearRing', 10 | ogr.wkbMultiLineString:'MultiLineString', 11 | ogr.wkbMultiLineString25D:'MultiLineString25D', 12 | ogr.wkbMultiPoint:'MultiPoint', 13 | ogr.wkbMultiPoint25D:'MultiPoint25D', 14 | ogr.wkbMultiPolygon:'MultiPolygon', 15 | ogr.wkbMultiPolygon25D:'MultiPolygon25D', 16 | ogr.wkbPoint:'Point', 17 | ogr.wkbPoint25D:'Point25D', 18 | ogr.wkbPolygon:'Polygon', 19 | ogr.wkbPolygon25D:'Polygon25D', 20 | ogr.wkbUnknown:'Unknown', 21 | } 22 | 23 | text2ogr = dict( [ [ogr2text[k],k] for k in ogr2text.keys()] ) 24 | -------------------------------------------------------------------------------- /stompy/spatial/interp_nn.py: -------------------------------------------------------------------------------- 1 | """ 2 | Nearest neighbor interpolation using scipy voronoi 3 | 4 | Note that this is not fast enough for interpolation of raster, but instead meant 5 | for a relatively small number of target points, where the same weights can 6 | be re-used many times. 7 | """ 8 | import numpy as np 9 | from .. import memoize, utils 10 | from scipy.spatial import Voronoi 11 | 12 | def signed_area_py(points): 13 | # typical voronoi regions don't have that many vertices and 14 | # using straight pythong is ~3x faster. 15 | N=points.shape[0] 16 | area=0.0 17 | for i in range(N): 18 | ip1 = (i+1)%N 19 | area += points[i,0]*points[ip1,1] - points[ip1,0]*points[i,1] 20 | return 0.5*area 21 | 22 | def nn_weights(src_xy, 23 | dst_xy, 24 | center=None, 25 | pad=None): 26 | """ 27 | src_xy: [N,2] locations of source data points 28 | dst_xy: [2] location of point to extract 29 | center: specify a center for the dummy points, defaults to mean 30 | pd: specify scale for dummy points, default to 100x the larger span. 31 | """ 32 | if src_xy.shape[0]==0: 33 | return np.zeros(0) 34 | if src_xy.shape[0]==1: 35 | return np.r_[1.0] 36 | 37 | # anything else it should work, even if goofy. 38 | 39 | if center is None: 40 | center=src_xy.mean(axis=0) 41 | if pad is None: 42 | pad = 100*(src_xy.max(axis=0) - src_xy.min(axis=0)).max() 43 | 44 | assert pad>0,"Degenerate points?" 45 | 46 | dummies = np.array( [[center[0]-pad,center[1]-pad], 47 | [center[0]+pad,center[1]-pad], 48 | [center[0]-pad,center[1]+pad], 49 | [center[0]+pad,center[1]+pad]] ) 50 | 51 | if 1: 52 | vor1 = Voronoi(np.concatenate([src_xy,dummies])) 53 | vor2 = Voronoi(np.concatenate([src_xy,dummies,[dst_xy]])) 54 | 55 | # how does one deal with the ridge vertices? 56 | # cheat and put some dummy vertices far away, and ignore 57 | # area stolen from them. 58 | 59 | weights=[] 60 | for i in range(src_xy.shape[0]): 61 | reg1=vor1.regions[ vor1.point_region[i] ] 62 | reg2=vor2.regions[ vor2.point_region[i] ] 63 | verts1=vor1.vertices[reg1] 64 | verts2=vor2.vertices[reg2] 65 | #area1=np.abs(utils.signed_area(verts1)) 66 | #area2=np.abs(utils.signed_area(verts2)) 67 | area1=np.abs(signed_area_py(verts1)) 68 | area2=np.abs(signed_area_py(verts2)) 69 | if area1 cp.frame: 45 | self.pop_op() 46 | finally: 47 | self.state='recording' 48 | 49 | def backstep(self): 50 | if self.state!='recording': 51 | raise Exception("backstep: state is not recording") 52 | self.state='reverting' 53 | try: 54 | self.pop_op() 55 | finally: 56 | self.state='recording' 57 | 58 | def commit(self): 59 | assert self.state != 'reverting' 60 | self.op_stack = None 61 | self.op_stack_serial += 1 62 | self.state='inactive' 63 | 64 | def push_op(self,meth,*data,**kwdata): 65 | self.abs_serial=self.abs_serial+1 66 | if self.state!='recording': 67 | return 68 | 69 | if self.op_stack is not None: 70 | self.op_stack.append( (meth,data,kwdata) ) 71 | 72 | def pop_op(self): 73 | assert self.state=='reverting' 74 | 75 | self.abs_serial=self.abs_serial+1 76 | 77 | f = self.op_stack.pop() 78 | self.log.debug("popping: %s"%( str(f) ) ) 79 | meth = f[0] 80 | args = f[1] 81 | kwargs = f[2] 82 | 83 | meth(*args,**kwargs) 84 | 85 | def __getstate__(self): 86 | try: 87 | d=super(OpHistory,self).__getstate__() 88 | except AttributeError: 89 | d = dict(self.__dict__) 90 | 91 | d['op_stack']=None 92 | d['state']='inactive' 93 | 94 | return d 95 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | *.png 2 | cache 3 | test_cache 4 | ptm_out 5 | run-dfm_to_ptm 6 | cells.dat 7 | edges.dat 8 | final.pav 9 | final.pdf 10 | points.dat 11 | smoothed-shoreline.* 12 | tom.log 13 | 14 | uk_tides 15 | dwaq_aggregation.* 16 | output* 17 | run-dfm* 18 | -------------------------------------------------------------------------------- /test/data/.gitignore: -------------------------------------------------------------------------------- 1 | *.tar.Z 2 | DATA 3 | -------------------------------------------------------------------------------- /test/data/Ex1.g01.hdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/Ex1.g01.hdf -------------------------------------------------------------------------------- /test/data/dem_sources.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/dem_sources.dbf -------------------------------------------------------------------------------- /test/data/dem_sources.prj: -------------------------------------------------------------------------------- 1 | PROJCS["NAD_1983_UTM_Zone_10N",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-123],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] -------------------------------------------------------------------------------- /test/data/dem_sources.qpj: -------------------------------------------------------------------------------- 1 | PROJCS["NAD83 / UTM zone 10N",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-123],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","26910"]] 2 | -------------------------------------------------------------------------------- /test/data/dem_sources.shp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/dem_sources.shp -------------------------------------------------------------------------------- /test/data/dem_sources.shx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/dem_sources.shx -------------------------------------------------------------------------------- /test/data/dumbarton.dbf: -------------------------------------------------------------------------------- 1 | _A!FIDN  0.000 -------------------------------------------------------------------------------- /test/data/dumbarton.prj: -------------------------------------------------------------------------------- 1 | PROJCS["NAD_1983_UTM_Zone_10N",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-123],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] -------------------------------------------------------------------------------- /test/data/dumbarton.shp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/dumbarton.shp -------------------------------------------------------------------------------- /test/data/dumbarton.shx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/dumbarton.shx -------------------------------------------------------------------------------- /test/data/lsb_combined_v14_net.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/lsb_combined_v14_net.nc -------------------------------------------------------------------------------- /test/data/scale-lines.dbf: -------------------------------------------------------------------------------- 1 | _AWscaleN 6000. 1000. 200.0 100.0 200.0 100.0 200.0 200.0 35.00 35.00 35.00 35.00 35.00 25.00 35.00 25.00 35.00 18.00 35.00 40.00 40.00 50.00 50.00 90.00 100.0 -------------------------------------------------------------------------------- /test/data/scale-lines.prj: -------------------------------------------------------------------------------- 1 | PROJCS["NAD_1983_UTM_Zone_10N",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-123],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] -------------------------------------------------------------------------------- /test/data/scale-lines.qpj: -------------------------------------------------------------------------------- 1 | PROJCS["UTM Zone 10, Northern Hemisphere",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9108"]],AUTHORITY["EPSG","4269"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-123],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] 2 | -------------------------------------------------------------------------------- /test/data/scale-lines.shp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/scale-lines.shp -------------------------------------------------------------------------------- /test/data/scale-lines.shx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/scale-lines.shx -------------------------------------------------------------------------------- /test/data/scale.dbf: -------------------------------------------------------------------------------- 1 | _$AWscaleN 50.000000000000000 50.000000000000000 50.000000000000000 100.000000000000000 50.000000000000000 1500.000000000000000 500.000000000000000 500.000000000000000 100.000000000000000 200.000000000000000 200.000000000000000 100.000000000000000 500.000000000000000 100.000000000000000 100.000000000000000 100.000000000000000 200.000000000000000 200.000000000000000 100.000000000000000 50.000000000000000 35.000000000000000 35.000000000000000 35.000000000000000 35.000000000000000 35.000000000000000 35.000000000000000 50.000000000000000 100.000000000000000 50.000000000000000 75.000000000000000 50.000000000000000 16.000000000000000 24.000000000000000 24.000000000000000 26.000000000000000 12.000000000000000 -------------------------------------------------------------------------------- /test/data/scale.prj: -------------------------------------------------------------------------------- 1 | PROJCS["NAD_1983_UTM_Zone_10N",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-123],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] -------------------------------------------------------------------------------- /test/data/scale.qpj: -------------------------------------------------------------------------------- 1 | PROJCS["UTM Zone 10, Northern Hemisphere",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9108"]],AUTHORITY["EPSG","4269"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-123],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] 2 | -------------------------------------------------------------------------------- /test/data/scale.shp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/scale.shp -------------------------------------------------------------------------------- /test/data/scale.shx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/scale.shx -------------------------------------------------------------------------------- /test/data/temp-transect.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustychris/stompy/5d86c88d0b1dff442d2294e322b651c6fb7e3f1f/test/data/temp-transect.nc -------------------------------------------------------------------------------- /test/dev_crash.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import logging 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | import pdb 7 | 8 | from scipy import optimize as opt 9 | 10 | from stompy.spatial import field 11 | from stompy import utils 12 | 13 | from stompy.grid import (unstructured_grid, exact_delaunay, front) 14 | 15 | import logging 16 | logging.basicConfig(level=logging.INFO) 17 | 18 | from stompy.spatial.linestring_utils import upsample_linearring,resample_linearring 19 | from stompy.spatial import field,constrained_delaunay,wkb2shp 20 | 21 | ## Curve - 22 | 23 | def hex_curve(): 24 | hexagon = np.array( [[0,11], 25 | [10,0], 26 | [30,0], 27 | [40,9], 28 | [30,20], 29 | [10,20]] ) 30 | return front.Curve(hexagon) 31 | 32 | 33 | def test_basic_setup(): 34 | boundary=hex_curve() 35 | af=front.AdvancingTriangles() 36 | scale=field.ConstantField(3) 37 | 38 | af.add_curve(boundary) 39 | af.set_edge_scale(scale) 40 | 41 | # create boundary edges based on scale and curves: 42 | af.initialize_boundaries() 43 | 44 | return af 45 | 46 | # when resample nodes on a sliding boundary, want to calculate the available 47 | # span, and if it's small, start distributing the nodes evenly. 48 | # where small is defined by local_scale * max_span_factor 49 | 50 | def test_resample(): 51 | af=test_basic_setup() 52 | a=0 53 | b=af.grid.node_to_nodes(a)[0] 54 | he=af.grid.nodes_to_halfedge(a,b) 55 | anchor=he.node_rev() 56 | n=he.node_fwd() 57 | n2=he.rev().node_rev() 58 | # Fails here, in grid.modify_node 59 | af.resample(n=n,anchor=anchor,scale=25,direction=1) 60 | af.resample(n=n2,anchor=anchor,scale=25,direction=-1) 61 | 62 | test_resample() 63 | 64 | 65 | # during modify_node(n=9) 66 | # 9 comes in as node b in call to line_is_free 67 | # vertex handle gives it as 22.5,0.0, which is the new location 68 | # lw from line_walk is bad. 69 | # after_add_node() just inserts the new point into the DT. 70 | # - could be related to premature garbage collection of points? 71 | # nope. 72 | # - related to init_face? has to be there for proper functioning 73 | # - or failure to remove the original vertex before creating the new one? 74 | # no, that seems to be taken care of. 75 | 76 | # does a line free call work before modifying the node? 77 | # nope. So maybe something else in the early part of before_modify_node 78 | # invalidates the state? 79 | # it's the second time through the loop that fails? 80 | # 10--9 crashes, even when it's the first in the loop 81 | # even if we could drop init_face, it segfaults without it. 82 | # segfaults when performing the line walk on a deep copy of DT. 83 | # the test it is attempting is along an existing finite edge. 84 | # happens whether the edge is constrained or not. 85 | 86 | # Possible next steps: 87 | # 1. could remove the node, insert in the new spot, maybe do a locate first? 88 | # and for any nodes which are now DT neighbors clearly we can skip the 89 | # line_is_free. 90 | # 2. hand-write the line_is_free stuff, ala live_dt. 91 | # 3. Abstract out the line_is_free stuff in live_dt, and both that and this 92 | # can use it. 93 | -------------------------------------------------------------------------------- /test/dev_front.py: -------------------------------------------------------------------------------- 1 | reload(unstructured_grid) 2 | reload(exact_delaunay) 3 | reload(front) 4 | 5 | import test_front 6 | reload(test_front) 7 | from test_front import * 8 | 9 | ## 10 | rings=sine_sine_rings() 11 | density = field.ConstantField(25.0) 12 | 13 | af=front.AdvancingTriangles() 14 | af.set_edge_scale(density) 15 | 16 | af.add_curve(rings[0],interior=False) 17 | for ring in rings[1:]: 18 | af.add_curve(ring,interior=True) 19 | af.initialize_boundaries() 20 | 21 | # gets pretty far, then corrupts some shoreline 22 | # while getting an intersecting constraint error 23 | af.loop(283) 24 | 25 | # 26 | 27 | ## 28 | af.loop(1) 29 | zoom=(3685.3576744887459, 3766.6617074119663, -106.27412460553033, -45.230532144628569) 30 | zoom=(2391.5283204797729, 2467.0778002411375, -175.48232649105583, -118.75928967022534) 31 | zoom=(617.5288764629438, 851.27615881930399, 122.37679637174006, 241.99027866141051) 32 | af.plot_summary(label_nodes=False) 33 | 34 | site=af.choose_site() 35 | site.plot() 36 | 37 | plt.axis(zoom) 38 | 39 | ## 40 | 41 | # this is what fails. 42 | af.resample_neighbors(site) 43 | -------------------------------------------------------------------------------- /test/dev_grayscott.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | Reaction Diffusion : Gray-Scott model 5 | 6 | References: 7 | ---------- 8 | Complex Patterns in a Simple System 9 | John E. Pearson, Science 261, 5118, 189-192, 1993. 10 | 11 | Encode movie 12 | ------------ 13 | 14 | ffmpeg -r 30 -i "tmp-%03d.png" -c:v libx264 -crf 23 -pix_fmt yuv420p bacteria.mp4 15 | ''' 16 | 17 | # https://www.youtube.com/watch?v=xL1kjpBqRNQ 18 | 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | 22 | 23 | # Parameters from http://www.aliensaint.com/uo/java/rd/ 24 | # ----------------------------------------------------- 25 | n = 200 26 | Du, Dv, F, k = 0.16, 0.08, 0.035, 0.065 # Bacteria 1 27 | # Du, Dv, F, k = 0.14, 0.06, 0.035, 0.065 # Bacteria 2 28 | Du=np.linspace(0.20,0.12,n)[None,:] 29 | Dv=np.linspace(0.10,0.05,n)[None,:] 30 | 31 | # Du, Dv, F, k = 0.16, 0.08, 0.060, 0.062 # Coral 32 | # Du, Dv, F, k = 0.19, 0.05, 0.060, 0.062 # Fingerprint 33 | # Du, Dv, F, k = 0.10, 0.10, 0.018, 0.050 # Spirals 34 | # Du, Dv, F, k = 0.12, 0.08, 0.020, 0.050 # Spirals Dense 35 | # Du, Dv, F, k = 0.10, 0.16, 0.020, 0.050 # Spirals Fast 36 | # Du, Dv, F, k = 0.16, 0.08, 0.020, 0.055 # Unstable 37 | # Du, Dv, F, k = 0.16, 0.08, 0.050, 0.065 # Worms 1 38 | # Du, Dv, F, k = 0.16, 0.08, 0.054, 0.063 # Worms 2 39 | # Du, Dv, F, k = 0.16, 0.08, 0.035, 0.060 # Zebrafish 40 | 41 | 42 | Z = np.zeros((n+2,n+2), [('U', np.double), ('V', np.double)]) 43 | U,V = Z['U'], Z['V'] 44 | u,v = U[1:-1,1:-1], V[1:-1,1:-1] 45 | 46 | r = 20 47 | u[...] = 1.0 48 | U[n/2-r:n/2+r,n/2-r:n/2+r] = 0.50 49 | V[n/2-r:n/2+r,n/2-r:n/2+r] = 0.25 50 | u += 0.05*np.random.random((n,n)) 51 | v += 0.05*np.random.random((n,n)) 52 | 53 | 54 | plt.ion() 55 | 56 | size = np.array(Z.shape) 57 | dpi = 72.0 58 | figsize= size[1]/float(dpi),size[0]/float(dpi) 59 | fig = plt.figure(figsize=figsize, dpi=dpi, facecolor="white",num=3) 60 | fig.add_axes([0.0, 0.0, 1.0, 1.0], frameon=False) 61 | im = plt.imshow(V, interpolation='bicubic', cmap=plt.cm.gray_r) 62 | plt.xticks([]), plt.yticks([]) 63 | 64 | ## 65 | for i in xrange(10000): 66 | Lu = ( U[0:-2,1:-1] + 67 | U[1:-1,0:-2] - 4*U[1:-1,1:-1] + U[1:-1,2:] + 68 | U[2: ,1:-1] ) 69 | Lv = ( V[0:-2,1:-1] + 70 | V[1:-1,0:-2] - 4*V[1:-1,1:-1] + V[1:-1,2:] + 71 | V[2: ,1:-1] ) 72 | 73 | uvv = u*v*v 74 | u += (Du*Lu - uvv + F *(1-u)) 75 | v += (Dv*Lv + uvv - (F+k)*v ) 76 | 77 | if i % 50 == 0: 78 | im.set_data(V) 79 | im.set_clim(vmin=V.min(), vmax=V.max()) 80 | plt.draw() 81 | plt.pause(0.01) 82 | 83 | # plt.ioff() 84 | # plt.savefig("../figures/zebra.png",dpi=dpi) 85 | # plt.savefig("../figures/bacteria.png",dpi=dpi) 86 | # plt.savefig("../figures/fingerprint.png",dpi=dpi) 87 | # plt.show() 88 | -------------------------------------------------------------------------------- /test/dev_mpi_ugrid.py: -------------------------------------------------------------------------------- 1 | """ 2 | Use dask.Array to lazily combine subdomain outputs 3 | """ 4 | import os,glob 5 | import copy 6 | import numpy as np 7 | import xarray as xr 8 | from stompy import utils 9 | from stompy.grid import unstructured_grid 10 | import matplotlib.pyplot as plt 11 | 12 | 13 | ## 14 | 15 | out_dir="/home/rusty/src/csc/dflowfm/runs/v03regroup_20190115/DFM_OUTPUT_flowfm" 16 | map_files=glob.glob(os.path.join(out_dir,'*0???_map.nc')) 17 | 18 | ## 19 | 20 | from stompy.grid import multi_ugrid 21 | 22 | # Slow, all the time is in add_grid(). 23 | mu=multi_ugrid.MultiUgrid(map_files,cleanup_dfm=True) 24 | 25 | ucmag=mu['mesh2d_ucmag'].isel(time=-1).values 26 | 27 | plt.figure(1).clf() 28 | 29 | ccoll=mu.grid.plot_cells(values=ucmag,cmap='jet') 30 | 31 | plt.axis('tight') 32 | plt.axis('equal') 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/dev_sms.py: -------------------------------------------------------------------------------- 1 | from stompy.grid import unstructured_grid 2 | import six 3 | 4 | six.moves.reload_module(unstructured_grid) 5 | 6 | # thanks to Jacob Zhao for sample input 7 | g=unstructured_grid.UnstructuredGrid.read_sms("0509.grd") 8 | 9 | ## 10 | 11 | # appears to bring in cells, edges and edge marks okay. 12 | plt.figure(1).clf() 13 | g.plot_edges(values=g.edges['mark']) 14 | g.plot_cells(alpha=0.5) 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/test_cgal_line_walk.py: -------------------------------------------------------------------------------- 1 | from CGAL.CGAL_Triangulation_2 import Constrained_Delaunay_triangulation_2 2 | from CGAL.CGAL_Kernel import Point_2 3 | 4 | from stompy.grid import cgal_line_walk 5 | 6 | import numpy as np 7 | 8 | def test_cgal_link_walk(): 9 | DT=Constrained_Delaunay_triangulation_2() 10 | 11 | xys=np.array( [ [0,0], 12 | [1,0], 13 | [0,1], 14 | [1,2] ],'f8' ) 15 | # in some versions, Point_2 is picky that it gets doubles, 16 | # not ints. 17 | pnts=[Point_2(xy[0],xy[1]) 18 | for xy in xys] 19 | 20 | vhs=[DT.insert(p) for p in pnts] 21 | 22 | DT.insert_constraint(vhs[0],vhs[2]) 23 | 24 | ## 25 | 26 | res0=cgal_line_walk.line_walk(DT,vhs[0],vhs[1]) 27 | 28 | assert not DT.is_constrained(res0[0][1]) 29 | res1=cgal_line_walk.line_walk(DT,vhs[0],vhs[2]) 30 | 31 | assert DT.is_constrained(res1[0][1]) 32 | 33 | assert len(cgal_line_walk.line_conflicts(DT,p1=[5,5],p2=[5,6]))==0 34 | 35 | assert len(cgal_line_walk.line_conflicts(DT,p1=[0.5,-0.5],p2=[0.5,0.5]))==0 36 | 37 | assert len(cgal_line_walk.line_conflicts(DT,p1=[-0.5,0.5],p2=[0.5,0.5]))>0 38 | 39 | res3=cgal_line_walk.line_conflicts(DT,p1=[0,-1],p2=[2,1]) 40 | assert len(res3)>0 41 | assert res3[0][0]=='v' 42 | 43 | 44 | -------------------------------------------------------------------------------- /test/test_cgal_triangulation.py: -------------------------------------------------------------------------------- 1 | from CGAL.CGAL_Triangulation_2 import Constrained_Delaunay_triangulation_2 2 | from CGAL.CGAL_Kernel import Point_2 3 | 4 | def test_basic(): 5 | def create(): 6 | DT = Constrained_Delaunay_triangulation_2() 7 | 8 | pnts=[Point_2(0.0, 0.0), 9 | Point_2(1.0, 0.0), 10 | Point_2(1.0, 1.0)] 11 | 12 | vhs=[ DT.insert(pnt) for pnt in pnts ] 13 | 14 | DT.insert_constraint(vhs[0],vhs[1]) 15 | DT.insert_constraint(vhs[1],vhs[2]) 16 | 17 | DT.remove_incident_constraints(vhs[1]) 18 | 19 | return DT 20 | 21 | DTs=[create() 22 | for _ in range(100)] 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/test_cimis.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os 3 | import shutil 4 | import six 5 | import numpy as np 6 | from stompy.io.local import cimis 7 | 8 | # os.environ['CIMIS_KEY']='FILL_THIS_IN' 9 | 10 | def test_cimis(): 11 | """ 12 | Fetch CIMIS data for station 171. This requires a network connection 13 | and defining CIMIS_KEY (freely available application key) in the environment 14 | or above in this file. 15 | """ 16 | # Start anew 17 | print("Get pristine cache dir") 18 | cache_dir='test_cache' 19 | os.path.exists(cache_dir) and shutil.rmtree(cache_dir) 20 | os.mkdir(cache_dir) 21 | 22 | print("Fetch first time") 23 | # 2/5/2001 is start of record for union city 24 | period=[np.datetime64('2016-01-01'), 25 | np.datetime64('2016-03-01')] 26 | df=cimis.cimis_fetch_to_xr(171,period[0],period[1],station_meta=True, 27 | days_per_request='10D',cache_dir=cache_dir) 28 | print("Fetch second time") 29 | df2=cimis.cimis_fetch_to_xr(171,period[0],period[1],station_meta=True, 30 | days_per_request='10D',cache_dir=cache_dir) 31 | print("Done") 32 | return df2 33 | 34 | -------------------------------------------------------------------------------- /test/test_cmap.py: -------------------------------------------------------------------------------- 1 | from stompy.plot import cmap 2 | 3 | def test_list_gradients(): 4 | assert len(cmap.list_gradients()) 5 | 6 | def test_load_gradient(): 7 | assert cmap.load_gradient('hot_desaturated.cpt') is not None 8 | -------------------------------------------------------------------------------- /test/test_coamps.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import six 3 | from stompy.io.local import coamps 4 | six.moves.reload_module(coamps) 5 | 6 | 7 | bounds=[340000, 610000, 3980000, 4294000] 8 | 9 | ds=coamps.coamps_dataset( bounds=bounds, 10 | start=np.datetime64("2018-01-01 00:00:00"), 11 | stop= np.datetime64("2018-02-01 00:00:00"), 12 | cache_dir="/home/rusty/src/sfb_ocean/wind/cache", 13 | fields=['wnd_utru','wnd_vtru'], 14 | fetch=False) 15 | 16 | # that call first does 17 | # def fetch_coamps_wind(start,stop, cache_dir, **kw): 18 | # to do the downloading. 19 | 20 | # then iterates over def coamps_files(start,stop,cache_dir,fields=['wnd_utru','wnd_vtru','pres_msl']): 21 | # to figure out what to compile 22 | ## 23 | six.moves.reload_module(coamps) 24 | files=coamps.coamps_files(start=np.datetime64("2018-01-01 00:00:00"), 25 | stop= np.datetime64("2018-02-01 00:00:00"), 26 | cache_dir="/home/rusty/src/sfb_ocean/wind/cache", 27 | fields=['wnd_utru','wnd_vtru']) 28 | 29 | files=list(files) 30 | -------------------------------------------------------------------------------- /test/test_constrained_delaunay.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from stompy.spatial.constrained_delaunay import ConstrainedXYZField 4 | 5 | data_dir=os.path.join(os.path.dirname(__file__),'data') 6 | 7 | def test_basic(): 8 | basedir = data_dir 9 | 10 | s = ConstrainedXYZField.read_shps([os.path.join(basedir,'scale.shp'), 11 | os.path.join(basedir,'scale-lines.shp')], 12 | value_field='scale') 13 | 14 | sg = s.to_grid(nx=500,ny=500) 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/test_dfm.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test setup for DFM model run configuration 3 | """ 4 | 5 | import subprocess 6 | import six 7 | import os 8 | import xarray as xr 9 | from stompy.grid import unstructured_grid 10 | from stompy import utils 11 | import numpy as np 12 | import stompy.model.delft.dflow_model as dfm 13 | import stompy.model.hydro_model as hm 14 | 15 | ## 16 | 17 | # machine-specific paths -- 18 | # for tests, assume that PATH and LD_LIBRARY_PATH are set appropriately. 19 | 20 | def base_model(run_dir='run-dfm-test00'): 21 | g=unstructured_grid.UnstructuredGrid(max_sides=4) 22 | 23 | ret=g.add_rectilinear([0,0],[500,500],50,50) 24 | # sloping N-S from -3 at y=0 to +2 at y=500 25 | g.add_node_field('node_z_bed',-3 + g.nodes['x'][:,1]/100) 26 | 27 | model=dfm.DFlowModel() 28 | model.load_template('dflow-template.mdu') 29 | model.set_grid(g) 30 | 31 | model.num_procs=0 32 | 33 | model.set_run_dir(run_dir, mode='pristine') 34 | model.run_start=np.datetime64("2018-01-01 00:00") 35 | model.run_stop =np.datetime64("2018-01-03 00:00") 36 | dt=np.timedelta64(300,'s') 37 | t=np.arange(model.run_start-20*dt, 38 | model.run_stop +20*dt, 39 | dt) 40 | # 4h period 41 | periodic_Q=5*np.sin((t-t[0])/np.timedelta64(1,'s') * 2*np.pi/(4*3600.)) 42 | Q=xr.DataArray(periodic_Q,dims=['time'],coords={'time':t}) 43 | inflow=hm.FlowBC(name='inflow', 44 | geom=np.array([ [0,0],[0,500]]), 45 | flow=Q) 46 | 47 | stage=hm.HarmonicStageBC(name='stage', 48 | msl=1.0,S2=(0.5,0), 49 | geom=np.array([ [500,0],[500,500]])) 50 | 51 | source=hm.SourceSinkBC(name='source', flow=20.0, 52 | geom=np.array([100,100])) 53 | 54 | model.add_bcs([inflow,stage,source]) 55 | 56 | model.projection='EPSG:26910' 57 | model.mdu['geometry','WaterLevIni']=0.0 58 | model.mdu['output','WaqInterval']=1800 59 | model.mdu['output','MapInterval']=1800 60 | model.write() 61 | 62 | return model 63 | 64 | ## 65 | def test_bcs(): 66 | # Get a well-behaved small hydro run: 67 | model=base_model() 68 | model.partition() 69 | model.run_model() 70 | 71 | -------------------------------------------------------------------------------- /test/test_dfm_grid.py: -------------------------------------------------------------------------------- 1 | from stompy.model.delft import dfm_grid 2 | import matplotlib.pyplot as plt 3 | import xarray as xr 4 | 5 | ## 6 | 7 | # g=dfm_grid.DFMGrid("/home/rusty/models/grids/mick_alviso_v4_net.nc/mick_alviso_v4_net.nc") 8 | nc_fn="/home/rusty/models/delft/dfm/alviso/20151214/Alviso_input/alviso2012_net.nc" 9 | g=dfm_grid.DFMGrid(nc_fn) 10 | ds=xr.open_dataset(nc_fn) 11 | 12 | ## 13 | 14 | fig=plt.figure(1) 15 | fig.clf() 16 | ax=fig.add_subplot(1,1,1) 17 | 18 | g.plot_edges(ax=ax,color='k') 19 | -------------------------------------------------------------------------------- /test/test_dfm_to_ptm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 16 05:21:30 2022 4 | 5 | @author: rusty 6 | """ 7 | import six 8 | from stompy.model.delft import dfm_to_ptm 9 | six.moves.reload_module(dfm_to_ptm) 10 | 11 | # Testing 12 | #mdu_path="../flowfm.mdu" 13 | # This one is giving some bizarre fluxes. 14 | mdu_path="S:/Data/Hydro/UCD_CSC/DFM/runs/test_run2_dwaq_20160701/flowfm.mdu" 15 | converter=dfm_to_ptm.DFlowToPTMHydro(mdu_path,'test_hydro.nc',time_slice=slice(0,10), 16 | grd_fn='test_sub.grd',overwrite=True) 17 | 18 | #%% 19 | 20 | # 105255 .. 105296 sequential, inclusive 21 | converter.unmapped_faces # =np.nonzero(~valid)[0] 22 | # mostly negative, all are substantial 23 | converter.unmapped_fluxes # =Qs[~valid] 24 | 25 | # These are indexes into the horizontal exchanges of flow. 26 | # should be able to get those to links (probably 1:1, since it's a 1D run) 27 | hyd=converter.hyd 28 | # sgn is +1 for all of these 29 | unmapped_links=hyd.exch_to_2d_link[converter.unmapped_faces]['link'] 30 | 31 | unmapped_elts=hyd.links[unmapped_links,1] 32 | 33 | g=hyd.grid() 34 | #%% 35 | import matplotlib.pyplot as plt 36 | fig,ax=plt.subplots(num=1,clear=1) 37 | ax.set_adjustable('datalim') 38 | g.plot_edges(lw=0.5,color='k',alpha=0.5) 39 | def labeler(i,r): 40 | return str(converter.unmapped_fluxes[i==unmapped_elts]) 41 | g.plot_cells(mask=unmapped_elts,labeler=labeler) 42 | 43 | # The locations still look like DCD 44 | # but the fluxes at these points are large. 45 | # is it possible that I'm pulling the wrong faces of these cells? 46 | # like it's getting mixed up between the src/sink exchange and a real 47 | # exchange, maybe 1-off? 48 | # also, check the actual DCD inputs. maybe they are off. 49 | -------------------------------------------------------------------------------- /test/test_exact_delaunay2.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import numpy as np 4 | 5 | from stompy.grid import exact_delaunay, unstructured_grid 6 | 7 | Triangulation=exact_delaunay.Triangulation 8 | from stompy.spatial import robust_predicates 9 | 10 | def test_gen_intersected_elements(): 11 | dt = Triangulation() 12 | pnts = [ [0,0], 13 | [5,0], 14 | [10,0], 15 | [5,5] ] 16 | 17 | nA=dt.add_node( x=pnts[0] ) # This tests insert into empty 18 | dt.add_node( x=pnts[1] ) # adjacent_vertex 19 | dt.add_node( x=pnts[2] ) # adjacent_vertex 20 | dt.add_node( x=pnts[3] ) # adjacent_edge 21 | 22 | dt.add_node( x=[3,0] ) # colinear 23 | 24 | dt.add_node( x=[6,2] ) # into cell interior 25 | nB=dt.add_node( x=[12,4] ) # collinear cell interior 26 | 27 | nodes=list(dt.valid_node_iter()) 28 | 29 | for iA,nA in enumerate(nodes): 30 | for nB in nodes[iA+1:]: 31 | print("test_gen_intersected_elements: %s to %s"%(dt.nodes['x'][nA], 32 | dt.nodes['x'][nB])) 33 | fwd=list(dt.gen_intersected_elements(nA=nA,nB=nB)) 34 | rev=list(dt.gen_intersected_elements(nA=nB,nB=nA)) 35 | assert len(fwd) == len(rev) 36 | 37 | def test_gen_int_elts_dim1(): 38 | dt = Triangulation() 39 | pnts = [ [0,0], 40 | [5,0], 41 | [10,0] ] 42 | for pnt in pnts: 43 | dt.add_node( x=pnt ) 44 | 45 | assert len(list(dt.gen_intersected_elements(0,1)))==3 46 | assert len(list(dt.gen_intersected_elements(0,2)))==5 47 | assert len(list(dt.gen_intersected_elements(1,2)))==3 48 | 49 | # and with some points 50 | assert len(list(dt.gen_intersected_elements(pA=[-1,-1], 51 | pB=[-1,1])))==0 52 | elts=list(dt.gen_intersected_elements(pA=[0,-1],pB=[0,1])) 53 | assert len(elts)==1 54 | assert elts[0][0]=='node' 55 | 56 | elts=list(dt.gen_intersected_elements(pA=[0,-1],pB=[1,1])) 57 | assert len(elts)==1 58 | assert elts[0][0]=='edge' 59 | 60 | def test_gen_int_elts_dim0(): 61 | dt = Triangulation() 62 | 63 | assert len(list(dt.gen_intersected_elements(pA=[-1,0],pB=[1,0])))==0 64 | 65 | dt.add_node(x=[0,0]) 66 | 67 | assert len(list(dt.gen_intersected_elements(pA=[-1,0],pB=[1,0])))==1 68 | assert len(list(dt.gen_intersected_elements(pA=[-1,0],pB=[1,1])))==0 69 | -------------------------------------------------------------------------------- /test/test_field.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | 4 | from stompy.spatial import field 5 | 6 | 7 | datadir=os.path.join( os.path.dirname(__file__), 'data') 8 | 9 | #depth_bin_file = '/home/rusty/classes/research/spatialdata/us/ca/suntans/bathymetry/compiled2/final.bin' 10 | 11 | def test_xyz(): 12 | depth_bin_file = os.path.join(datadir,'depth.xyz') 13 | 14 | f = field.XYZText(fname=depth_bin_file) 15 | f.build_index() 16 | 17 | center = np.array([ 563379.6 , 4196117. ]) 18 | 19 | elev = f.inv_dist_interp(center, 20 | min_n_closest=8, 21 | min_radius=3900.0) 22 | 23 | 24 | 25 | ## 26 | 27 | def test_lin_interp(): 28 | X=np.array([[0.,0.],[10.,0.],[10.,10.],[0.,10.]]) 29 | F=np.array([1.,2.,3.,4.]) 30 | 31 | f = field.XYZField(X=X,F=F) 32 | 33 | elev = f.interpolate( [2,3] ) 34 | 35 | out=f.interpolate(X) 36 | assert np.allclose(out,F) 37 | 38 | 39 | -------------------------------------------------------------------------------- /test/test_filters.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from stompy import filters 3 | 4 | def test_modes(): 5 | t_h=np.arange(0,24*20) 6 | 7 | x= 2.0+ ( 1.0 *np.cos( t_h/12.4 * 2*np.pi ) + 8 | 0.3 *np.cos( t_h/12. * 2*np.pi ) + 9 | 0.1 *np.cos( t_h/24.8 * 2*np.pi ) + 10 | 0.1 *np.cos( t_h/24. * 2*np.pi ) ) 11 | x_lp=filters.lowpass_godin(x,t_h/24.) 12 | x_lp_nan=filters.lowpass_godin(x,t_h/24.,ends='nan') 13 | 14 | if 0: 15 | import matplotlib.pyplot as plt # just for dev 16 | plt.figure(10).clf() 17 | plt.plot(t_h,x) 18 | plt.plot(t_h,x_lp) 19 | plt.plot(t_h,x_lp_nan) 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/test_front_speed.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import logging 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | import pdb 7 | 8 | from scipy import optimize as opt 9 | 10 | from stompy.spatial import field 11 | from stompy import utils 12 | 13 | from stompy.grid import (unstructured_grid, exact_delaunay, front) 14 | 15 | import logging 16 | logging.basicConfig(level=logging.INFO) 17 | 18 | from stompy.spatial.linestring_utils import upsample_linearring,resample_linearring 19 | from stompy.spatial import field,constrained_delaunay,wkb2shp 20 | 21 | ## Curve - 22 | 23 | def hex_curve(): 24 | hexagon = np.array( [[0,11], 25 | [10,0], 26 | [30,0], 27 | [40,9], 28 | [30,20], 29 | [10,20]] ) 30 | return front.Curve(hexagon) 31 | 32 | def test_basic_setup(): 33 | boundary=hex_curve() 34 | af=front.AdvancingTriangles() 35 | scale=field.ConstantField(3) 36 | 37 | af.add_curve(boundary) 38 | af.set_edge_scale(scale) 39 | 40 | # create boundary edges based on scale and curves: 41 | af.initialize_boundaries() 42 | 43 | if 0: 44 | plt.clf() 45 | g=af.grid 46 | g.plot_edges() 47 | g.plot_nodes() 48 | 49 | # 50 | coll=g.plot_halfedges(values=g.edges['cells']) 51 | coll.set_lw(0) 52 | coll.set_cmap('winter') 53 | 54 | return af 55 | 56 | 57 | def test_no_lookahead(): 58 | af=test_basic_setup() 59 | af.log.setLevel(logging.INFO) 60 | af.cdt.post_check=False 61 | 62 | af.current=af.root=front.DTChooseSite(af) 63 | 64 | def cb(): 65 | af.plot_summary(label_nodes=False) 66 | try: 67 | af.current.site.plot() 68 | except: # AttributeError: 69 | pass 70 | 71 | while 1: 72 | if not af.current.children: 73 | break # we're done? 74 | 75 | for child_i in range(len(af.current.children)): 76 | if af.current.try_child(child_i): 77 | # Accept the first child which returns true 78 | break 79 | else: 80 | assert False # none of the children worked out 81 | return af 82 | ## 83 | # 172 cells, with CGAL, takes 8.7s ? so 20 cells/second. 84 | 85 | test_no_lookahead() 86 | 87 | # 10s in prun 88 | # removed some log.debug statements 89 | # 8.3s 90 | # 5.8s in optimize, 1.1s in line_walk 91 | 92 | ## 93 | -------------------------------------------------------------------------------- /test/test_gen_spatial_index.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import nose 3 | 4 | from stompy.spatial import gen_spatial_index 5 | 6 | reload(gen_spatial_index) 7 | 8 | def helper(implementation): 9 | x=np.linspace(0,1,51) 10 | y=np.linspace(0,1,51) 11 | X,Y=np.meshgrid(x,y) 12 | 13 | pnts=np.c_[ X.ravel(), X.ravel(), Y.ravel(), Y.ravel() ] 14 | 15 | klass=gen_spatial_index.point_index_class_factory(implementation=implementation) 16 | tuples=zip( np.arange(len(pnts)), pnts, [None]*len(pnts) ) 17 | 18 | index=klass(tuples,interleaved=False) 19 | 20 | # avoid ambiguous answer - 0.015 better than 0.01 21 | target=np.array([0.015,0.015,0.02,0.02]) 22 | 23 | hits=index.nearest( target, 5) 24 | hits=list(hits) # in case it's a generator 25 | 26 | brute=np.argmin(utils.dist(pnts[:,[0,2]]-target[[0,2]])) 27 | 28 | assert hits[0]==brute 29 | 30 | def test_rtree(): 31 | helper('rtree') 32 | 33 | def test_best(): 34 | helper('best') 35 | 36 | def test_kdtree(): 37 | helper('kdtree') 38 | 39 | def test_qgis(): 40 | # likely to fail if not run from within qgis. 41 | helper('qgis') 42 | 43 | ## 44 | 45 | if __name__=='__main__': 46 | nose.main() 47 | 48 | -------------------------------------------------------------------------------- /test/test_harm_decomp.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | 4 | from stompy import harm_decomp 5 | import numpy as np 6 | 7 | def test_basic(): 8 | # A sample problem: 9 | omegas = np.array([1.0,0.0]) 10 | 11 | # the constructed data: 12 | amps = np.array([1,5.0]) 13 | phis = np.array([1,0]) 14 | 15 | t = np.linspace(0,10*np.pi,125) 16 | h = amps[0]*np.cos(omegas[0]*t - phis[0]) + amps[1]*np.cos(omegas[1]*t - phis[1]) 17 | 18 | comps = harm_decomp.decompose(t,h,omegas) 19 | recon=harm_decomp.recompose(t,comps,omegas) 20 | 21 | assert np.allclose( recon, h) 22 | 23 | print("Components: ",comps) 24 | 25 | -------------------------------------------------------------------------------- /test/test_lag.py: -------------------------------------------------------------------------------- 1 | from stompy.utils import * 2 | import xarray as xr 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | 6 | 7 | ## 8 | 9 | def test_basic(): 10 | # Test timeseries: 11 | t_h=np.arange(0,24*30,0.1) # 6 minute time steps in units of hours 12 | # And as np datetimes: 13 | t=np.datetime64("2000-01-01 00:00") + (3600*t_h)*np.timedelta64(1,'s') 14 | 15 | mod=xr.Dataset() 16 | mod['time']=('time',),t 17 | mod['x']=( ('time',), 18 | np.cos( 2*np.pi *t_h /12.4) + 0.2*np.cos( 2*np.pi *t_h /12.0) ) 19 | 20 | obs=xr.Dataset() 21 | # give it a time shift: 22 | obs['time']=('time',),t+np.timedelta64(45,'m') 23 | obs['x']=( ('time',), 24 | (np.cos( 2*np.pi *t_h /12.4) + 0.2*np.cos( 2*np.pi *t_h /12.0) ) ) 25 | # Add some noise to everybody 26 | obs['x'].values += 0.1*(np.random.random(t_h.shape) - 0.5) 27 | mod['x'].values += 0.1*(np.random.random(t_h.shape) - 0.5) 28 | 29 | # Make it shorter and coarser in time 30 | obs = obs.isel(time=slice(20,-100,3)) 31 | 32 | lag=utils.find_lag_xr(mod.x,obs.x) 33 | 34 | print lag / np.timedelta64(1,'m') 35 | 36 | assert np.abs( lag - np.timedelta64(-45,'m')) < np.timedelta64(5,'m') 37 | 38 | try: 39 | import matplotlib.pyplot as plt 40 | 41 | plt.figure(1).clf() 42 | fig,ax=plt.subplots(num=1) 43 | 44 | ax.plot(obs.time,obs.x,label='Obs') 45 | ax.plot(mod.time,mod.x,label='Mod') 46 | ax.plot(mod.time-lag,mod.x,label='Mod w/lag') 47 | 48 | ax.legend(fontsize=10) 49 | except ImportError: 50 | pass 51 | 52 | -------------------------------------------------------------------------------- /test/test_live_dt.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | logging.basicConfig(level=logging.INFO) 4 | 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | 8 | from stompy.grid import live_dt, paver, exact_delaunay, trigrid, orthomaker 9 | 10 | def run_basic(klass): 11 | pnts = np.array([ [0,0], 12 | [3,0], 13 | [5,0], 14 | [10,0], 15 | [5,5], 16 | [6,2], 17 | [12,4]], np.float64 ) 18 | 19 | g=klass(points=pnts) 20 | 21 | g.add_edge(0,1) 22 | g.add_edge(1,2) 23 | g.add_edge(2,3) 24 | j24=g.add_edge(2,4) 25 | g.add_edge(0,4) 26 | j25=g.add_edge(2,5) 27 | j56=g.add_edge(5,6) 28 | g.add_edge(3,6) 29 | g.add_edge(4,5) 30 | 31 | # plt.figure(1).clf() 32 | # g.plot() 33 | # g.plot_nodes() 34 | 35 | # point on convex hull away from the triangulation 36 | assert g.shoot_ray(0,[2,1])[0]==j24 37 | assert g.shoot_ray(2,[1,1])[0]==j56 38 | assert g.shoot_ray(3,[-4,2])[0] in (j25,j56) 39 | # py code is failing these: 40 | res=g.shoot_ray(0,[0,1]) 41 | assert res == (None,None) 42 | 43 | def test_basic_cgal(): 44 | run_basic(live_dt.LiveDtCGAL) 45 | 46 | def test_basic_python(): 47 | run_basic(live_dt.LiveDtPython) 48 | 49 | -------------------------------------------------------------------------------- /test/test_merge_grids.py: -------------------------------------------------------------------------------- 1 | import nose 2 | from stompy.grid import unstructured_grid 3 | reload(unstructured_grid) 4 | from nose.tools import assert_raises 5 | 6 | ## 7 | 8 | def test_merge(): 9 | ugA=unstructured_grid.SuntansGrid('/Users/rusty/src/umbra/Umbra/sample_data/sfbay') 10 | ugB=unstructured_grid.SuntansGrid('/Users/rusty/src/umbra/Umbra/sample_data/sfbay') 11 | 12 | x_cut=568000 13 | n_sel=ugA.nodes['x'][:,0]0.99 99 | 100 | -------------------------------------------------------------------------------- /test/test_py_apollonius.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from stompy.spatial import field 3 | 4 | def test_basic(): 5 | pa=field.PyApolloniusField() 6 | 7 | pa.insert([0,0],10) 8 | pa.insert([100,100],5) 9 | 10 | g=pa.to_grid(100,100) 11 | 12 | # good to test a single point, too 13 | pa( [10,10] ) 14 | 15 | ## 16 | 17 | def test_larger(): 18 | # About 1.5s on basic macbook 19 | pa=field.PyApolloniusField() 20 | 21 | for it in range(1000): 22 | xy=1000*np.random.random(2) 23 | pa.insert(xy,10) 24 | 25 | g=pa.to_grid(200,200) 26 | 27 | -------------------------------------------------------------------------------- /test/test_rbr.py: -------------------------------------------------------------------------------- 1 | from stompy.io import rbr 2 | 3 | def test_rbr(): 4 | """ no tests yet, aside from making sure it can import """ 5 | pass 6 | 7 | -------------------------------------------------------------------------------- /test/test_rdb.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import nose 4 | 5 | from stompy.io import rdb, rdb_codes 6 | 7 | datadir=os.path.join(os.path.dirname(__file__),'data') 8 | 9 | def test_basic(): 10 | fn=os.path.join(datadir,'test_rdb.rdb') 11 | r=rdb.Rdb(source_file=fn) 12 | 13 | def test_xarray(): 14 | ds1=rdb.rdb_to_dataset(os.path.join(datadir,'test_rdb.rdb')) 15 | ds2=rdb.rdb_to_dataset(os.path.join(datadir,'coyote.rdb')) 16 | 17 | assert ds2.time.dtype==np.datetime64 18 | 19 | def test_codes(): 20 | pcodes=rdb_codes.parm_codes() 21 | scodes=rdb_codes.stat_codes() 22 | pcodes.loc[60] 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/test_read_ras.py: -------------------------------------------------------------------------------- 1 | import stompy.grid.unstructured_grid as ugrid 2 | 3 | def test_read_ras2d(): 4 | g2=ugrid.UnstructuredGrid.read_ras2d('data/Ex1.g01.hdf') 5 | -------------------------------------------------------------------------------- /test/test_tom.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Added -f 5.0 to coarsen the scale and complete much faster. 4 | # this should take about 1800 steps to complete, maybe 1 minute on a 2019 laptop. 5 | python ../stompy/grid/tom.py -f 5.0 -s data/scale-lines.shp -b data/dumbarton.shp 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/test_transect.py: -------------------------------------------------------------------------------- 1 | from stompy.model.suntans import transect 2 | 3 | def test_transect(): 4 | """no tests yet, just import 5 | """ 6 | pass 7 | -------------------------------------------------------------------------------- /test/test_transect_contour.py: -------------------------------------------------------------------------------- 1 | import xarray as xr 2 | from stompy import xr_transect 3 | import matplotlib.pyplot as plt 4 | 5 | def test_transect_contour(): 6 | tran=xr.open_dataset('data/temp-transect.nc') 7 | 8 | plt.figure(10).clf() 9 | fig,axs=plt.subplots(2,1,num=10,sharex=True,sharey=True) 10 | xr_transect.plot_scalar(tran,'temp',ax=axs[0]) 11 | xr_transect.plot_scalar_pcolormesh(tran,'temp',ax=axs[1]) 12 | 13 | xr_transect.contour(tran,'temp',np.linspace(5,27,28), 14 | ax=axs[1],colors='k',linewidths=0.5) 15 | 16 | -------------------------------------------------------------------------------- /test/test_trigrid.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from stompy.grid import trigrid 4 | 5 | def test_read_sun(): 6 | path=os.path.join( os.path.dirname(__file__),'data','sfbay') 7 | 8 | g = trigrid.TriGrid(suntans_path=path) 9 | g.Ncells() 10 | 11 | 12 | if __name__ == '__main__': 13 | g = TriGrid(sms_fname="/home/rusty/data/sfbay/grids/100km-arc/250m/250m-100km_arc.grd") 14 | g.plot() 15 | g.verify_bc(do_plot=1) 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/test_uk_tides.py: -------------------------------------------------------------------------------- 1 | from stompy.io.local import uk_tides 2 | import numpy as np 3 | 4 | cache_dir="." 5 | 6 | # stations=get_tide_gauges(cache_dir) 7 | uk_tides.get_tide_gauges(cache_dir='.') 8 | 9 | 10 | station='E72124' 11 | ds=uk_tides.fetch_tides(station=station, 12 | start_date=np.datetime64("2019-07-01"), 13 | end_date=np.datetime64("2019-07-10"), 14 | cache_dir=cache_dir) 15 | 16 | -------------------------------------------------------------------------------- /test/test_unstructured_grid_plot.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import numpy as np 4 | import os 5 | 6 | import matplotlib.pyplot as plt 7 | from stompy.grid import unstructured_grid 8 | from stompy.model.delft import dfm_grid 9 | 10 | 11 | def test_square_axes(): 12 | sample_data=os.path.join(os.path.dirname(__file__),'data') 13 | g=unstructured_grid.UnstructuredGrid.read_dfm(os.path.join(sample_data,"lsb_combined_v14_net.nc")) 14 | 15 | expected=np.r_[490243.0, 609718.4589319953, 4137175.7739677625, 4232151.0] 16 | 17 | fig=plt.figure(1) 18 | fig.clf() 19 | g.plot_cells() 20 | plt.draw() 21 | result=plt.axis() 22 | diff = np.abs(np.array(result) - expected) 23 | assert np.all(diff<10.0) 24 | 25 | fig.clf() 26 | g.plot_nodes() 27 | plt.draw() 28 | result=plt.axis() 29 | diff = np.abs(np.array(result) - expected) 30 | assert np.all(diff<10.0) 31 | 32 | fig.clf() 33 | g.plot_edges() 34 | plt.draw() 35 | result=plt.axis() 36 | diff = np.abs(np.array(result) - expected) 37 | assert np.all(diff<10.0) 38 | 39 | # What about shared axes? 40 | # This works: 41 | fig.clf() 42 | fig,axs=plt.subplots(1,2,sharex=True,sharey=True,num=1) 43 | g.plot_nodes(ax=axs[0]) 44 | g.plot_nodes(ax=axs[1]) 45 | plt.draw() 46 | result=plt.axis() 47 | diff = np.abs(np.array(result) - expected) 48 | assert np.all(diff<10.0) 49 | 50 | # This is okay 51 | fig.clf() 52 | fig,axs=plt.subplots(1,2,sharex=True,sharey=True,num=1) 53 | g.plot_edges(ax=axs[0]) 54 | g.plot_edges(ax=axs[1]) 55 | plt.draw() 56 | result=plt.axis() 57 | diff = np.abs(np.array(result) - expected) 58 | assert np.all(diff<10.0) 59 | 60 | fig.clf() 61 | fig,axs=plt.subplots(1,2,sharex=True,sharey=True,num=1) 62 | g.plot_cells(ax=axs[0]) 63 | g.plot_cells(ax=axs[1]) 64 | plt.draw() 65 | result=plt.axis() 66 | diff = np.abs(np.array(result) - expected) 67 | assert np.all(diff<10.0) 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /test/test_usgs_nwis.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import time 4 | 5 | import numpy as np 6 | from stompy.io.local import usgs_nwis 7 | 8 | ## 9 | 10 | def test_basic(): 11 | # This requires internet access! 12 | ds=usgs_nwis.nwis_dataset(station="11337190", 13 | start_date=np.datetime64('2012-08-01'), 14 | end_date =np.datetime64('2012-10-01'), 15 | products=[60, # "Discharge, cubic feet per second" 16 | 10], # "Temperature, water, degrees Celsius" 17 | days_per_request=30) 18 | 19 | 20 | def test_provisional(): 21 | usgs_a8_site="372512121585801" 22 | 23 | ds=usgs_nwis.nwis_dataset(usgs_a8_site, 24 | np.datetime64("2015-12-10"), 25 | np.datetime64("2015-12-20"), 26 | products=[72178]) 27 | 28 | def test_missing(): 29 | station="11162765" 30 | t_start=np.datetime64('2016-10-01') 31 | t_stop=np.datetime64('2016-12-01') 32 | # This period has some missing data identified by '***' which 33 | # caused problems in older versions of rdb.py 34 | ds=usgs_nwis.nwis_dataset(station, 35 | t_start,t_stop, 36 | products=[95,90860], 37 | days_per_request=20) 38 | 39 | def test_caching(): 40 | station="11162765" 41 | t_start=np.datetime64('2016-10-01') 42 | t_stop=np.datetime64('2016-12-01') 43 | 44 | cache_dir='tmp_cache' 45 | if os.path.exists(cache_dir): 46 | # Start clean 47 | shutil.rmtree(cache_dir) 48 | 49 | os.mkdir(cache_dir) 50 | 51 | timings=[] 52 | for trial in [0,1]: 53 | t0=time.time() 54 | ds=usgs_nwis.nwis_dataset(station, 55 | t_start,t_stop, 56 | products=[95,90860], 57 | days_per_request='10D',cache_dir=cache_dir) 58 | timings.append(time.time() - t0) 59 | 60 | assert timings[0]>5*timings[1] 61 | -------------------------------------------------------------------------------- /test/test_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from stompy.utils import segment_segment_intersection 3 | 4 | def test_segment_segment_intersection(): 5 | x1=segment_segment_intersection( np.array( [ [0,0],[1,0]] ), 6 | np.array( [ [0,0],[0,1]] ) ) 7 | assert np.allclose( x1, [0,0]) 8 | 9 | x2=segment_segment_intersection( np.array( [ [0,0],[1,0]] ), 10 | np.array( [ [1,0],[0,0]] ) ) 11 | assert x2 is None 12 | 13 | x3=segment_segment_intersection( np.array( [ [0,0],[1,0]] ), 14 | np.array( [ [0,1],[1,1]] ) ) 15 | assert x3 is None 16 | 17 | x4=segment_segment_intersection( np.array( [ [0,0],[1,1]] ), 18 | np.array( [ [1,0],[0,1]] ) ) 19 | assert np.allclose(x4,[0.5,0.5]) 20 | 21 | -------------------------------------------------------------------------------- /test/test_waq_scenario.py: -------------------------------------------------------------------------------- 1 | from stompy.model.delft import waq_scenario 2 | import datetime 3 | 4 | def test_waq_timestep_timedelta(): 5 | inputs=[100,"100","0010","5000000",5100] 6 | 7 | for x in inputs: 8 | td=waq_scenario.waq_timestep_to_timedelta(x) 9 | ts=waq_scenario.timedelta_to_waq_timestep(td) 10 | td2=waq_scenario.waq_timestep_to_timedelta(ts) 11 | assert td == td2 12 | -------------------------------------------------------------------------------- /test/test_wkb2shp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | from stompy.spatial import wkb2shp 4 | from stompy import utils 5 | import six 6 | six.moves.reload_module(wkb2shp) 7 | 8 | def test_load_shp(): 9 | feats=wkb2shp.shp2geom("data/dem_sources.shp") 10 | assert np.any(~utils.isnat(feats['start_date'])) 11 | 12 | def test_load_shp_query(): 13 | feats=wkb2shp.shp2geom("data/dem_sources.shp") 14 | queries=["priority < 100", 15 | "start_date is null"] 16 | # This appears not to be supported yet. unclear. 17 | # 'start_date is null or (cast(start_date as character) < "2014-09-01")'] 18 | 19 | for query in queries: 20 | print(query) 21 | feat_sel=wkb2shp.shp2geom("data/dem_sources.shp", 22 | query=query) 23 | assert len(feats)>len(feat_sel) 24 | 25 | if 0: 26 | # Doesn't work. 27 | def test_write_gpkg(): 28 | from shapely import geometry 29 | 30 | geoms=[ geometry.Point( -120.0, 37.0 ), 31 | geometry.Point( -121.0, 37.5 ) ] 32 | if os.path.exists('test.gpkg'): 33 | import shutil 34 | shutil.rmtree('test.gpkg') 35 | wkb2shp.wkb2shp("test.gpkg",geoms,driver='GPKG',srs_text='WGS84', 36 | layer_name='points') 37 | 38 | 39 | -------------------------------------------------------------------------------- /test/test_xr_transect.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from stompy import xr_transect 4 | 5 | def test_transect(): 6 | """ 7 | Test loading a 'section_hydro' formatted transect, and resample to evenly 8 | spaced z. 9 | """ 10 | untrim_sec=xr_transect.section_hydro_to_transect("data/section_hydro.txt",name="7B") 11 | 12 | new_z=np.linspace( untrim_sec.z_bed.values.min(), 13 | untrim_sec.z_surf.values.max(), 14 | 100) 15 | 16 | untrim_sec_eq=xr_transect.resample_z(untrim_sec,new_z) 17 | 18 | plt.figure(22).clf() 19 | fig,axs=plt.subplots(2,1,num=22,sharex=True,sharey=True) 20 | 21 | xr_transect.plot_scalar(untrim_sec,'Ve',ax=axs[0]) 22 | xr_transect.plot_scalar(untrim_sec_eq,'Ve',ax=axs[1]) 23 | 24 | axs[0].text(0.02,0.9,'Original',transform=axs[0].transAxes) 25 | axs[1].text(0.02,0.9,'Resample',transform=axs[1].transAxes) 26 | 27 | 28 | 29 | --------------------------------------------------------------------------------