cubedynamics 0.1.0rc1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. cubedynamics-0.1.0rc1/CITATION.cff +23 -0
  2. cubedynamics-0.1.0rc1/LICENSE +21 -0
  3. cubedynamics-0.1.0rc1/MANIFEST.in +5 -0
  4. cubedynamics-0.1.0rc1/PKG-INFO +434 -0
  5. cubedynamics-0.1.0rc1/README.md +329 -0
  6. cubedynamics-0.1.0rc1/pyproject.toml +115 -0
  7. cubedynamics-0.1.0rc1/setup.cfg +4 -0
  8. cubedynamics-0.1.0rc1/src/climate_cube_math/__init__.py +33 -0
  9. cubedynamics-0.1.0rc1/src/climate_cube_math/data/__init__.py +10 -0
  10. cubedynamics-0.1.0rc1/src/climate_cube_math/data/gridmet.py +24 -0
  11. cubedynamics-0.1.0rc1/src/climate_cube_math/data/prism.py +41 -0
  12. cubedynamics-0.1.0rc1/src/climate_cube_math/data/sentinel2.py +3 -0
  13. cubedynamics-0.1.0rc1/src/climate_cube_math/demo.py +80 -0
  14. cubedynamics-0.1.0rc1/src/climate_cube_math/hulls.py +175 -0
  15. cubedynamics-0.1.0rc1/src/cubedynamics/__init__.py +207 -0
  16. cubedynamics-0.1.0rc1/src/cubedynamics/biology/__init__.py +6 -0
  17. cubedynamics-0.1.0rc1/src/cubedynamics/biology/align.py +50 -0
  18. cubedynamics-0.1.0rc1/src/cubedynamics/biology/rasterize.py +72 -0
  19. cubedynamics-0.1.0rc1/src/cubedynamics/config.py +12 -0
  20. cubedynamics-0.1.0rc1/src/cubedynamics/correlation_cubes.py +30 -0
  21. cubedynamics-0.1.0rc1/src/cubedynamics/data/__init__.py +101 -0
  22. cubedynamics-0.1.0rc1/src/cubedynamics/data/_ranges.py +88 -0
  23. cubedynamics-0.1.0rc1/src/cubedynamics/data/_transport.py +272 -0
  24. cubedynamics-0.1.0rc1/src/cubedynamics/data/catalog.py +319 -0
  25. cubedynamics-0.1.0rc1/src/cubedynamics/data/certification.py +116 -0
  26. cubedynamics-0.1.0rc1/src/cubedynamics/data/daymet.py +92 -0
  27. cubedynamics-0.1.0rc1/src/cubedynamics/data/gridmet.py +536 -0
  28. cubedynamics-0.1.0rc1/src/cubedynamics/data/lifecycle.py +410 -0
  29. cubedynamics-0.1.0rc1/src/cubedynamics/data/nouns.py +467 -0
  30. cubedynamics-0.1.0rc1/src/cubedynamics/data/prism.py +927 -0
  31. cubedynamics-0.1.0rc1/src/cubedynamics/data/qa.py +328 -0
  32. cubedynamics-0.1.0rc1/src/cubedynamics/data/revisions.py +147 -0
  33. cubedynamics-0.1.0rc1/src/cubedynamics/data/roads.py +236 -0
  34. cubedynamics-0.1.0rc1/src/cubedynamics/data/schema.py +256 -0
  35. cubedynamics-0.1.0rc1/src/cubedynamics/data/sentinel2.py +166 -0
  36. cubedynamics-0.1.0rc1/src/cubedynamics/data/serving_history.json +16 -0
  37. cubedynamics-0.1.0rc1/src/cubedynamics/data/three_dep.py +115 -0
  38. cubedynamics-0.1.0rc1/src/cubedynamics/data/usgs.py +260 -0
  39. cubedynamics-0.1.0rc1/src/cubedynamics/demo.py +80 -0
  40. cubedynamics-0.1.0rc1/src/cubedynamics/demo_vase.py +105 -0
  41. cubedynamics-0.1.0rc1/src/cubedynamics/deprecations.py +33 -0
  42. cubedynamics-0.1.0rc1/src/cubedynamics/events/__init__.py +6 -0
  43. cubedynamics-0.1.0rc1/src/cubedynamics/events/detection.py +182 -0
  44. cubedynamics-0.1.0rc1/src/cubedynamics/events/matching.py +76 -0
  45. cubedynamics-0.1.0rc1/src/cubedynamics/events/schemas.py +21 -0
  46. cubedynamics-0.1.0rc1/src/cubedynamics/fire_time_hull.py +1894 -0
  47. cubedynamics-0.1.0rc1/src/cubedynamics/fire_vase_lakehouse/__init__.py +49 -0
  48. cubedynamics-0.1.0rc1/src/cubedynamics/fire_vase_lakehouse/cache.py +174 -0
  49. cubedynamics-0.1.0rc1/src/cubedynamics/fire_vase_lakehouse/manifest.py +78 -0
  50. cubedynamics-0.1.0rc1/src/cubedynamics/fire_vase_lakehouse/partitioning.py +65 -0
  51. cubedynamics-0.1.0rc1/src/cubedynamics/fire_vase_lakehouse/qc.py +56 -0
  52. cubedynamics-0.1.0rc1/src/cubedynamics/fire_vase_lakehouse/query.py +76 -0
  53. cubedynamics-0.1.0rc1/src/cubedynamics/fire_vase_lakehouse/schemas.py +57 -0
  54. cubedynamics-0.1.0rc1/src/cubedynamics/grammar.py +949 -0
  55. cubedynamics-0.1.0rc1/src/cubedynamics/hulls.py +175 -0
  56. cubedynamics-0.1.0rc1/src/cubedynamics/indices/__init__.py +1 -0
  57. cubedynamics-0.1.0rc1/src/cubedynamics/indices/vegetation.py +39 -0
  58. cubedynamics-0.1.0rc1/src/cubedynamics/ops/__init__.py +18 -0
  59. cubedynamics-0.1.0rc1/src/cubedynamics/ops/io.py +18 -0
  60. cubedynamics-0.1.0rc1/src/cubedynamics/ops/ndvi.py +33 -0
  61. cubedynamics-0.1.0rc1/src/cubedynamics/ops/stats.py +103 -0
  62. cubedynamics-0.1.0rc1/src/cubedynamics/ops/transforms.py +50 -0
  63. cubedynamics-0.1.0rc1/src/cubedynamics/ops/viz.py +53 -0
  64. cubedynamics-0.1.0rc1/src/cubedynamics/ops_fire/__init__.py +1 -0
  65. cubedynamics-0.1.0rc1/src/cubedynamics/ops_fire/climate_hull_extract.py +52 -0
  66. cubedynamics-0.1.0rc1/src/cubedynamics/ops_fire/fired_api.py +78 -0
  67. cubedynamics-0.1.0rc1/src/cubedynamics/ops_fire/fired_io.py +46 -0
  68. cubedynamics-0.1.0rc1/src/cubedynamics/ops_fire/time_hull.py +113 -0
  69. cubedynamics-0.1.0rc1/src/cubedynamics/ops_io/__init__.py +1 -0
  70. cubedynamics-0.1.0rc1/src/cubedynamics/ops_io/gridmet_api.py +72 -0
  71. cubedynamics-0.1.0rc1/src/cubedynamics/ops_io/gridmet_loader.py +13 -0
  72. cubedynamics-0.1.0rc1/src/cubedynamics/piping.py +296 -0
  73. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/__init__.py +29 -0
  74. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/axis_rig.py +746 -0
  75. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/cube_plot.py +1008 -0
  76. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/cube_viewer.py +1345 -0
  77. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/geom.py +16 -0
  78. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/progress.py +43 -0
  79. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/stats.py +33 -0
  80. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/tail_association.py +482 -0
  81. cubedynamics-0.1.0rc1/src/cubedynamics/plotting/viewer.py +46 -0
  82. cubedynamics-0.1.0rc1/src/cubedynamics/prism_streaming.py +35 -0
  83. cubedynamics-0.1.0rc1/src/cubedynamics/progress.py +45 -0
  84. cubedynamics-0.1.0rc1/src/cubedynamics/sentinel.py +271 -0
  85. cubedynamics-0.1.0rc1/src/cubedynamics/stats/__init__.py +1 -0
  86. cubedynamics-0.1.0rc1/src/cubedynamics/stats/anomalies.py +83 -0
  87. cubedynamics-0.1.0rc1/src/cubedynamics/stats/correlation.py +53 -0
  88. cubedynamics-0.1.0rc1/src/cubedynamics/stats/rolling.py +65 -0
  89. cubedynamics-0.1.0rc1/src/cubedynamics/stats/spatial.py +67 -0
  90. cubedynamics-0.1.0rc1/src/cubedynamics/stats/spatial_units.py +386 -0
  91. cubedynamics-0.1.0rc1/src/cubedynamics/stats/tails.py +172 -0
  92. cubedynamics-0.1.0rc1/src/cubedynamics/streaming/__init__.py +12 -0
  93. cubedynamics-0.1.0rc1/src/cubedynamics/streaming/global_climate.py +177 -0
  94. cubedynamics-0.1.0rc1/src/cubedynamics/streaming/gridmet.py +379 -0
  95. cubedynamics-0.1.0rc1/src/cubedynamics/streaming/virtual.py +222 -0
  96. cubedynamics-0.1.0rc1/src/cubedynamics/synchrony/__init__.py +21 -0
  97. cubedynamics-0.1.0rc1/src/cubedynamics/synchrony/coupling.py +99 -0
  98. cubedynamics-0.1.0rc1/src/cubedynamics/synchrony/occurrence.py +210 -0
  99. cubedynamics-0.1.0rc1/src/cubedynamics/synchrony/severity.py +188 -0
  100. cubedynamics-0.1.0rc1/src/cubedynamics/synchrony/spatial.py +413 -0
  101. cubedynamics-0.1.0rc1/src/cubedynamics/synchrony/states.py +283 -0
  102. cubedynamics-0.1.0rc1/src/cubedynamics/synchrony/timing.py +243 -0
  103. cubedynamics-0.1.0rc1/src/cubedynamics/tubes.py +277 -0
  104. cubedynamics-0.1.0rc1/src/cubedynamics/utils/__init__.py +8 -0
  105. cubedynamics-0.1.0rc1/src/cubedynamics/utils/chunking.py +31 -0
  106. cubedynamics-0.1.0rc1/src/cubedynamics/utils/cube_css.py +446 -0
  107. cubedynamics-0.1.0rc1/src/cubedynamics/utils/dims.py +90 -0
  108. cubedynamics-0.1.0rc1/src/cubedynamics/utils/drift_centering.py +137 -0
  109. cubedynamics-0.1.0rc1/src/cubedynamics/utils/drop_bad_assets.py +108 -0
  110. cubedynamics-0.1.0rc1/src/cubedynamics/utils/provenance.py +57 -0
  111. cubedynamics-0.1.0rc1/src/cubedynamics/utils/reference.py +29 -0
  112. cubedynamics-0.1.0rc1/src/cubedynamics/variables.py +491 -0
  113. cubedynamics-0.1.0rc1/src/cubedynamics/vase.py +289 -0
  114. cubedynamics-0.1.0rc1/src/cubedynamics/vase_viz.py +211 -0
  115. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/__init__.py +804 -0
  116. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/biology.py +55 -0
  117. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/combine.py +108 -0
  118. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/custom.py +29 -0
  119. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/diagnostics.py +407 -0
  120. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/events.py +34 -0
  121. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/fire.py +871 -0
  122. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/flatten.py +51 -0
  123. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/landsat_mpc.py +344 -0
  124. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/models.py +12 -0
  125. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/plot.py +252 -0
  126. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/plot_mean.py +75 -0
  127. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/states.py +127 -0
  128. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/stats.py +794 -0
  129. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/synchrony.py +189 -0
  130. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/tubes.py +107 -0
  131. cubedynamics-0.1.0rc1/src/cubedynamics/verbs/vase.py +202 -0
  132. cubedynamics-0.1.0rc1/src/cubedynamics/version.py +5 -0
  133. cubedynamics-0.1.0rc1/src/cubedynamics/viewers/__init__.py +11 -0
  134. cubedynamics-0.1.0rc1/src/cubedynamics/viewers/cube_viewer.py +186 -0
  135. cubedynamics-0.1.0rc1/src/cubedynamics/viewers/simple_plot.py +162 -0
  136. cubedynamics-0.1.0rc1/src/cubedynamics/viewers/templates/cube_viewer_template.html +546 -0
  137. cubedynamics-0.1.0rc1/src/cubedynamics/viz/__init__.py +8 -0
  138. cubedynamics-0.1.0rc1/src/cubedynamics/viz/lexcube_viz.py +70 -0
  139. cubedynamics-0.1.0rc1/src/cubedynamics/viz/qa_plots.py +97 -0
  140. cubedynamics-0.1.0rc1/src/cubedynamics.egg-info/PKG-INFO +434 -0
  141. cubedynamics-0.1.0rc1/src/cubedynamics.egg-info/SOURCES.txt +142 -0
  142. cubedynamics-0.1.0rc1/src/cubedynamics.egg-info/dependency_links.txt +1 -0
  143. cubedynamics-0.1.0rc1/src/cubedynamics.egg-info/requires.txt +66 -0
  144. cubedynamics-0.1.0rc1/src/cubedynamics.egg-info/top_level.txt +2 -0
@@ -0,0 +1,23 @@
1
+ cff-version: 1.2.0
2
+ title: "CubeDynamics: A Composable Grammar for Spatiotemporal Science"
3
+ message: "If you use CubeDynamics, please cite it as described here."
4
+ type: software
5
+ authors:
6
+ - family-names: Tuff
7
+ given-names: Ty
8
+ version: 0.1.0rc1
9
+ repository-code: "https://github.com/CU-ESIIL/cubedynamics"
10
+ url: "https://cu-esiil.github.io/cubedynamics/"
11
+ keywords:
12
+ - climate
13
+ - streaming
14
+ - data cubes
15
+ - xarray
16
+ - dask
17
+ license: MIT
18
+ abstract: |
19
+ CubeDynamics is an early scientific Python software release centered on
20
+ pipe(value) | verb() composition over labeled spatiotemporal objects.
21
+ It includes documented source loaders, provenance and QA interfaces,
22
+ visualization, and project-specific vocabularies. Version 0.1.0rc1 is alpha;
23
+ source suitability and operational maturity are documented separately.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Environmental Data Science Innovation and Inclusion Lab (ESIIL)
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.
@@ -0,0 +1,5 @@
1
+ # Repository tests and reviewed scientific inputs are not distribution data.
2
+ prune tests
3
+ prune src/cubedynamics/tests
4
+ global-exclude __pycache__ *.py[cod] .DS_Store
5
+ include CITATION.cff
@@ -0,0 +1,434 @@
1
+ Metadata-Version: 2.4
2
+ Name: cubedynamics
3
+ Version: 0.1.0rc1
4
+ Summary: A composable grammar of verbs for streaming spatiotemporal cubes
5
+ Author: Ty Tuff
6
+ License: MIT License
7
+
8
+ Copyright (c) 2023 Environmental Data Science Innovation and Inclusion Lab (ESIIL)
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/CU-ESIIL/cubedynamics
29
+ Project-URL: Repository, https://github.com/CU-ESIIL/cubedynamics
30
+ Project-URL: Documentation, https://cu-esiil.github.io/cubedynamics/
31
+ Project-URL: Issues, https://github.com/CU-ESIIL/cubedynamics/issues
32
+ Keywords: climate,data-cubes,xarray,dask,geospatial
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.9
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Topic :: Scientific/Engineering
43
+ Classifier: Topic :: Scientific/Engineering :: GIS
44
+ Requires-Python: >=3.9
45
+ Description-Content-Type: text/markdown
46
+ License-File: LICENSE
47
+ Requires-Dist: numpy<2
48
+ Requires-Dist: xarray
49
+ Requires-Dist: pandas
50
+ Requires-Dist: rasterio
51
+ Requires-Dist: rioxarray
52
+ Requires-Dist: pystac-client
53
+ Requires-Dist: planetary-computer
54
+ Requires-Dist: pyproj
55
+ Requires-Dist: requests
56
+ Requires-Dist: dask
57
+ Requires-Dist: h5netcdf
58
+ Requires-Dist: h5py
59
+ Requires-Dist: cubo
60
+ Requires-Dist: matplotlib
61
+ Requires-Dist: Pillow
62
+ Requires-Dist: crc32c
63
+ Requires-Dist: plotly
64
+ Requires-Dist: geopandas
65
+ Requires-Dist: shapely
66
+ Requires-Dist: scipy
67
+ Requires-Dist: ipython
68
+ Provides-Extra: roads
69
+ Requires-Dist: pyarrow<22,>=17; extra == "roads"
70
+ Provides-Extra: browser
71
+ Requires-Dist: playwright==1.58.0; python_version >= "3.10" and extra == "browser"
72
+ Requires-Dist: pytest-playwright==0.7.2; python_version >= "3.10" and extra == "browser"
73
+ Provides-Extra: viz
74
+ Requires-Dist: lexcube; extra == "viz"
75
+ Provides-Extra: test
76
+ Requires-Dist: pytest; extra == "test"
77
+ Requires-Dist: build; extra == "test"
78
+ Requires-Dist: twine; extra == "test"
79
+ Provides-Extra: docs
80
+ Requires-Dist: mkdocs>=1.6; extra == "docs"
81
+ Requires-Dist: mkdocs-material; extra == "docs"
82
+ Requires-Dist: mkdocstrings[python]; extra == "docs"
83
+ Requires-Dist: mkdocs-git-revision-date-plugin; extra == "docs"
84
+ Requires-Dist: mkdocs-jupyter; extra == "docs"
85
+ Requires-Dist: nbclient; extra == "docs"
86
+ Requires-Dist: nbformat; extra == "docs"
87
+ Provides-Extra: vignettes
88
+ Requires-Dist: nbclient; extra == "vignettes"
89
+ Requires-Dist: nbformat; extra == "vignettes"
90
+ Requires-Dist: ipykernel; extra == "vignettes"
91
+ Provides-Extra: dev
92
+ Requires-Dist: pytest; extra == "dev"
93
+ Requires-Dist: build; extra == "dev"
94
+ Requires-Dist: twine; extra == "dev"
95
+ Requires-Dist: mkdocs>=1.6; extra == "dev"
96
+ Requires-Dist: mkdocs-material; extra == "dev"
97
+ Requires-Dist: mkdocstrings[python]; extra == "dev"
98
+ Requires-Dist: mkdocs-git-revision-date-plugin; extra == "dev"
99
+ Requires-Dist: mkdocs-jupyter; extra == "dev"
100
+ Requires-Dist: nbclient; extra == "dev"
101
+ Requires-Dist: nbformat; extra == "dev"
102
+ Requires-Dist: ipykernel; extra == "dev"
103
+ Requires-Dist: lexcube; extra == "dev"
104
+ Dynamic: license-file
105
+
106
+ # CubeDynamics
107
+
108
+ <p align="center">
109
+ <img src="https://raw.githubusercontent.com/CU-ESIIL/cubedynamics/main/docs/assets/img/cubedynamics_logo.png" alt="CubeDynamics" width="520">
110
+ </p>
111
+
112
+ ![Tests](https://github.com/CU-ESIIL/cubedynamics/actions/workflows/tests.yml/badge.svg) ![Docs](https://github.com/CU-ESIIL/cubedynamics/actions/workflows/pages.yml/badge.svg)
113
+
114
+ CubeDynamics is an inspectable grammar for spatiotemporal environmental data:
115
+ **source-qualified nouns describe observations, semantic verbs describe
116
+ operations, and pipes preserve authored order.** Its core is deliberately small:
117
+ `pipe(cube) | verb() | verb()`. It builds on xarray, NumPy, Dask, and geospatial
118
+ tools rather than replacing them.
119
+
120
+ Rerunnable code is not automatically a recoverable scientific argument. A pipe
121
+ retains semantic state and an ordered trace so a reader can inspect what object
122
+ each transformation produced, while source records and bounded QA keep the
123
+ evidence beneath a concise noun reachable. A common noun does not make sources
124
+ interchangeable, a trace is not complete workflow provenance, and validation
125
+ does not choose or certify the scientific question. See the
126
+ [scientific framing](docs/concepts/scientific_inspectability.md).
127
+
128
+ The checkout currently declares **version 0.1.0rc1, alpha**, with Python **3.9+**
129
+ support. Development and documentation CI use Python 3.11; the offline test
130
+ matrix covers 3.9–3.12. These are repository metadata and CI targets, not a claim
131
+ that an installed PyPI release contains every change on `main`.
132
+
133
+ For the first public alpha release candidate, see the [0.1 support contract](docs/project/api_support_0_1.md),
134
+ [release-note draft](docs/project/release_0_1_0.md), and [non-publishing release checklist](RELEASING.md).
135
+ The candidate artifact targets `0.1.0rc1`; it is not a published release or tag.
136
+
137
+ ## Start here
138
+
139
+ The [website](https://cu-esiil.github.io/cubedynamics/) has five entry points:
140
+
141
+ - [Home](https://cu-esiil.github.io/cubedynamics/) — what the grammar is for.
142
+ - [Learn](https://cu-esiil.github.io/cubedynamics/learn/) — a progressive introduction.
143
+ - [Library](https://cu-esiil.github.io/cubedynamics/library/) — environmental nouns and source flavors.
144
+ - [Documents](https://cu-esiil.github.io/cubedynamics/documentation/) — operations, arguments, and return values.
145
+ - [Vignettes](https://cu-esiil.github.io/cubedynamics/vignettes/) — real-data analysis stories with runnable notebooks and figures.
146
+
147
+ [Developer documentation](https://cu-esiil.github.io/cubedynamics/developer/)
148
+ separates architecture, CI, source maintenance, and audits from user reference.
149
+
150
+ ## Install
151
+
152
+ **Current state: preparing the first public release candidate; not published.**
153
+ There is no installable CubeDynamics distribution on PyPI or in the existing
154
+ GitHub Releases. Do not substitute a source clone for outside-user acceptance
155
+ testing. A reviewer can install a maintainer-supplied, checksum-verified
156
+ `cubedynamics-0.1.0rc1-py3-none-any.whl` directly with pip today.
157
+
158
+ **After the v0.1.0rc1 GitHub Release is published**, the external install command
159
+ will be (this URL is not available yet):
160
+
161
+ ```bash
162
+ python -m pip install "https://github.com/CU-ESIIL/cubedynamics/releases/download/v0.1.0rc1/cubedynamics-0.1.0rc1-py3-none-any.whl"
163
+ ```
164
+
165
+ After a separate PyPI publication, `python -m pip install cubedynamics==0.1.0rc1`
166
+ will install the same version. Plain `pip install cubedynamics` is reserved for
167
+ the future final release. See the [installation guide](docs/getting_started/install.md)
168
+ and [package-only quickstart](docs/quickstart.md); neither requires a clone.
169
+
170
+ ### Developer checkout (not the release-candidate install)
171
+
172
+ Contributors who need this checkout's code, real-data fixtures, and notebooks
173
+ can clone and install from its root. This uses the Python 3.11 development target:
174
+
175
+ ```bash
176
+ git clone https://github.com/CU-ESIIL/cubedynamics.git
177
+ cd cubedynamics
178
+ python3.11 -m venv .venv
179
+ source .venv/bin/activate
180
+ python -m pip install -e ".[dev]"
181
+ ```
182
+
183
+ On Windows, activate with `.venv\Scripts\activate` instead. For an exact
184
+ reproduction, check out a recorded commit or release tag before installing.
185
+ `make install` and `make test` provide the existing local development shortcuts.
186
+
187
+ Optional extras in [pyproject.toml](pyproject.toml):
188
+
189
+ | Extra | Adds |
190
+ | --- | --- |
191
+ | `test` | Pytest and distribution-checking tools |
192
+ | `docs` | MkDocs, reference rendering, and notebook rendering |
193
+ | `vignettes` | Notebook execution and a Python kernel |
194
+ | `viz` | Optional Lexcube widget support; not required for the custom HTML cube viewer |
195
+ | `dev` | Test, docs, vignette, and Lexcube tooling |
196
+ | `browser` | Playwright and its pytest plugin, Python 3.10+; install Chromium separately |
197
+ | `roads` | Optional PyArrow reader for the bounded Overture roads candidate |
198
+
199
+ The former `climate_cube_math` namespace remains a deprecated compatibility
200
+ path. Use `cubedynamics` for new code.
201
+
202
+ ## A short pipe, a visible result
203
+
204
+ Run this from the repository root after installation. It uses a small,
205
+ checksum-controlled **real PRISM extract**, not generated observations, and
206
+ requires no network request. The input contains January 1–30, 2024 daily
207
+ maximum temperatures near Boulder, Colorado, in degrees Celsius.
208
+
209
+ <!-- readme-example: offline -->
210
+ ```python
211
+ import matplotlib.pyplot as plt
212
+ import xarray as xr
213
+ from cubedynamics import pipe, verbs as v
214
+
215
+ # Load only the small, reviewed fixture; the file closes after loading.
216
+ with xr.open_dataset(
217
+ "tests/fixtures/real_data/prism_boulder_january_2024.nc", engine="scipy"
218
+ ) as observations:
219
+ cube = observations["tmax"].load()
220
+
221
+ # Average daily maximum temperature over these dates at each grid cell.
222
+ result = pipe(cube) | v.mean(over="time", keep_dim=False)
223
+ result.unwrap().plot(cmap="magma", cbar_kwargs={"label": "Temperature (°C)"})
224
+ plt.title("Boulder region · Mean daily maximum · January 1–30, 2024")
225
+ plt.show()
226
+ ```
227
+
228
+ This describes the observed period, not a long-term climate normal. The
229
+ [fixture provenance](tests/fixtures/real_data/prism_boulder_january_2024.provenance.json)
230
+ records its source and checksum; [validation](https://cu-esiil.github.io/cubedynamics/validation/)
231
+ documents the checks.
232
+
233
+ Pipes also expose `explain()`, `suggest()`, `validate()`, `semantic_state`, and
234
+ `semantic_trace`. These inspect metadata and analytical order; they do not
235
+ certify the scientific question or independently validate source observations.
236
+ `|` invokes each stage when composed; Dask-backed stages can retain deferred
237
+ computation. `unwrap()` returns the current value, not a forced computation.
238
+
239
+ ## Choose observations by scientific noun
240
+
241
+ The current catalog contains these implemented source flavors:
242
+
243
+ <!-- readme-catalog: start -->
244
+ | Noun | Source flavors |
245
+ | --- | --- |
246
+ | `temperature` | `gridmet`, `prism` |
247
+ | `precipitation` | `gridmet`, `prism` |
248
+ | `vpd` | `gridmet` |
249
+ | `wind` | `gridmet` |
250
+ | `humidity` | `gridmet` |
251
+ | `radiation` | `gridmet` |
252
+ | `surface_reflectance` | `sentinel2` |
253
+ | `vegetation_index` | `sentinel2` |
254
+ <!-- readme-catalog: end -->
255
+
256
+ Inspect support without downloading observations:
257
+
258
+ <!-- readme-example: discovery -->
259
+ ```python
260
+ from cubedynamics import data
261
+
262
+ print(data.list_sources())
263
+ print(data.describe("temperature", "prism"))
264
+ ```
265
+
266
+ For a live request, choose a source and statistic explicitly. This example
267
+ requires provider access and may fail if the remote service is unavailable:
268
+
269
+ <!-- readme-example: live -->
270
+ ```python
271
+ import matplotlib.pyplot as plt
272
+ from cubedynamics import data, pipe, verbs as v
273
+
274
+ cube = data.temperature(
275
+ source="prism", statistic="maximum",
276
+ bbox=[-105.55, 39.85, -105.05, 40.15],
277
+ start="2024-01-01", end="2024-01-03",
278
+ )
279
+ (pipe(cube) | v.mean(over="time", keep_dim=False)).unwrap().plot(cmap="magma")
280
+ plt.show()
281
+ ```
282
+
283
+ The same noun does **not** make sources interchangeable. gridMET temperature
284
+ uses kelvin and provides maximum/minimum; PRISM uses degrees Celsius and also
285
+ provides a mean statistic. These climate sources cover the contiguous United
286
+ States. Sentinel-2 has different spatial, spectral, acquisition, and quality
287
+ constraints. Inspect units, CRS, coordinates, missingness, and source provenance
288
+ before combining observations; CubeDynamics does not silently harmonize them.
289
+
290
+ Noun loaders reject synthetic fallback. Provider-specific loaders remain
291
+ available for lower-level access. Landsat and FIRED integrations are additional
292
+ workflows, not extra registered noun flavors. `stream_global_climate_cube`
293
+ adapts an already-open xarray object; it is not a global-data downloader.
294
+
295
+ Lazy output is not proof of bounded remote access: PRISM uses daily THREDDS
296
+ NcSS subsets; gridMET can use OPeNDAP with an optional compatible engine, but
297
+ falls back to fetching annual HTTPS files. A long record can still be expensive.
298
+ Source QA separates a serving revision's scientific validity from live endpoint
299
+ health. See the [source reference](https://cu-esiil.github.io/cubedynamics/library/sources/).
300
+
301
+ ## Built-in vocabulary and your own verbs
302
+
303
+ | Layer | Role |
304
+ | --- | --- |
305
+ | Core grammar | `pipe`, `Pipe`, callable stages, and semantic contracts |
306
+ | Shared vocabulary | Transformations, summaries, states/events, comparison, and alignment |
307
+ | Integrations | Source adapters, plotting/viewers, and output helpers |
308
+ | Project extensions | Synchrony, biological workflows, tubes, Fire VASE, and your own methods |
309
+
310
+ Project vocabularies currently ship in the same distribution for compatibility;
311
+ their scientific assumptions are not part of the minimal grammar contract.
312
+
313
+ A project-owned operation can be an ordinary callable factory. Continuing with
314
+ the **offline Celsius cube** above, ask what fraction of observed days exceeded
315
+ a chosen temperature. The threshold is an analytical choice, not a provider fact:
316
+
317
+ <!-- readme-example: custom -->
318
+ ```python
319
+ def fraction_above(threshold):
320
+ def _op(cube):
321
+ # Count only observed days; missing values must not become cool days.
322
+ observed = cube.notnull().sum("time")
323
+ return ((cube > threshold).sum("time") / observed.where(observed > 0)).rename(
324
+ "fraction_above"
325
+ ).assign_attrs(units="1")
326
+ return _op
327
+
328
+ warm_days = (pipe(cube) | fraction_above(10)).unwrap()
329
+ warm_days.plot(vmin=0, vmax=1, cbar_kwargs={"label": "Fraction of observed days"})
330
+ plt.title("Boulder region · Daily maximum above 10°C")
331
+ plt.show()
332
+ ```
333
+
334
+ No registration or subclass is required. Start from the
335
+ [custom-verb scaffold](examples/custom_verb_project/README.md) and test direct
336
+ and piped use before presenting a method as reviewed.
337
+
338
+ [Browse operations by purpose](https://cu-esiil.github.io/cubedynamics/reference/verbs/)
339
+ or use the [full A–Z inventory](https://cu-esiil.github.io/cubedynamics/reference/verbs/a-z/).
340
+ Not every callable in `v` is a pipe-stage factory: `v.fire_plot`, for example,
341
+ is a direct visualization helper. `v.correlation_cube` and `v.fit_model` are
342
+ reserved placeholders, not implemented analysis operations. Compatibility
343
+ aliases are labeled separately in the reference. `v.month_filter` is a
344
+ supported stage; only its legacy `cubedynamics.ops` import warns.
345
+
346
+ `v.plot()` uses the custom HTML/CSS/JavaScript cube viewer. Fire plotting still
347
+ has a Plotly renderer; it has not been fully migrated to that viewer.
348
+ `FireEventDaily` and `FireHull` are the canonical fire object names;
349
+ `TimeHull` is retained for compatibility.
350
+
351
+ ## Reproduce and validate
352
+
353
+ There are **twelve supported offline notebooks**: eight core lessons and three
354
+ real-data noun lessons (elevation, roads, streamflow) under
355
+ `docs/vignettes/` and the Working Lands analysis under `docs/decision_vignettes/`.
356
+ They cover arrays, tidy tables, Datasets, composition, transformations,
357
+ states/events, custom verbs, lazy computation, and a two-noun decision story.
358
+ Each has real-data provenance, executable code, and required static plots.
359
+ The runner checks those outputs without modifying notebook sources:
360
+
361
+ ```bash
362
+ python scripts/run_vignettes.py
363
+ ```
364
+
365
+ Use these checks from an installed development checkout:
366
+
367
+ ```bash
368
+ python -m pytest -m "not integration and not online" -q
369
+ python scripts/run_validation.py --run-vignettes
370
+ python scripts/run_source_qa.py
371
+ python scripts/run_decision_qa.py
372
+ python scripts/build_reference_docs.py --check
373
+ python -m mkdocs build --strict
374
+ python scripts/check_site_links.py site
375
+ python scripts/check_repository_size.py --mode tracked
376
+ ```
377
+
378
+ QA evidence is written under ignored `artifacts/`. Passing a bounded fixture
379
+ does not certify every product, location, period, or current provider endpoint.
380
+ Online checks and live-source certification are separate from offline tests.
381
+
382
+ For website changes, also run the opt-in browser suite (Python 3.10+):
383
+
384
+ ```bash
385
+ python -m pip install -e ".[browser]"
386
+ python -m playwright install chromium
387
+ python -m pytest tests/browser -m browser --site-dir site --browser chromium \
388
+ --tracing retain-on-failure --output artifacts/browser/playwright -q
389
+ ```
390
+
391
+ It checks built-page links and anchors, decoded images, deferred embeds,
392
+ desktop/mobile journeys, and cube interaction. CI gates documentation and Pages
393
+ publication on browser failures; external-link availability is advisory.
394
+ See [CI and testing](docs/dev/ci_testing.md) for evidence and platform setup.
395
+
396
+ ## Repository and contribution guide
397
+
398
+ The main noun library includes [elevation](docs/library/nouns/elevation.md),
399
+ [roads](docs/library/nouns/roads.md), and [streamflow](docs/library/nouns/streamflow.md),
400
+ with source-specific installed imports, complete references, and offline
401
+ real-data lessons. Their bounded QA status is documented separately from their
402
+ place in the grammar; `data.list_sources()` and production serving histories
403
+ are unchanged. Historical acquisition reports live in
404
+ [developer source engineering](docs/data/source_projects/index.md).
405
+
406
+ - `src/cubedynamics/` is the installed package; `code/cubedynamics/` is a legacy mirror.
407
+ - `docs/` holds the website, generated references, and supported notebooks.
408
+ - `tests/fixtures/real_data/` holds small observational fixtures and provenance.
409
+ - `scripts/` holds reference/notebook builders and QA runners.
410
+ - [paper/](paper/README.md) holds manuscript working material, including the
411
+ supplied citation-map draft; draft markers are not completed references.
412
+
413
+ Large scientific products and runtime manifests belong outside Git. Use
414
+ [config/storage.example.yml](config/storage.example.yml) for local/object-store
415
+ paths. The size policy permits small reviewed fixtures under `tests/fixtures/`;
416
+ do not remove their NetCDF files merely because bulk NetCDF output is blocked.
417
+ Historical Fire VASE outputs still in Git require an explicit archival plan,
418
+ not automatic deletion. See [data policy](data/README.md) and the
419
+ [publication plan](docs/project/publication_plan.md).
420
+
421
+ Read [CONTRIBUTING.md](CONTRIBUTING.md) and [AGENTS.md](AGENTS.md) before changing
422
+ the package. Edit reference generators, not files marked generated. Keep the
423
+ five website entry points and distinguish implemented, compatibility, and
424
+ reserved APIs. See the [public API policy](docs/project/public_api.md).
425
+
426
+ ## Citation and license
427
+
428
+ Use [CITATION.cff](CITATION.cff), recording the package version/commit and the
429
+ source products used. Its DOI field is not populated; manuscript citation
430
+ markers do not supply a release DOI. Release publishing is handled by
431
+ [publish.yml](.github/workflows/publish.yml) on `v*` tags or manual dispatch;
432
+ tagging/publishing and completing manuscript references are separate tasks.
433
+
434
+ CubeDynamics is distributed under the [MIT License](LICENSE).