dapper 1.6.0__tar.gz → 1.7.0__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 (162) hide show
  1. dapper-1.7.0/PKG-INFO +29 -0
  2. {dapper-1.6.0 → dapper-1.7.0}/README.md +36 -35
  3. {dapper-1.6.0 → dapper-1.7.0}/dapper/__init__.py +1 -1
  4. {dapper-1.6.0 → dapper-1.7.0}/dapper/da_methods/__init__.py +17 -9
  5. {dapper-1.6.0 → dapper-1.7.0}/dapper/da_methods/baseline.py +42 -38
  6. {dapper-1.6.0 → dapper-1.7.0}/dapper/da_methods/ensemble.py +311 -262
  7. dapper-1.7.0/dapper/da_methods/extended.py +113 -0
  8. {dapper-1.6.0 → dapper-1.7.0}/dapper/da_methods/other.py +56 -46
  9. {dapper-1.6.0 → dapper-1.7.0}/dapper/da_methods/particle.py +157 -150
  10. {dapper-1.6.0 → dapper-1.7.0}/dapper/da_methods/variational.py +117 -103
  11. {dapper-1.6.0 → dapper-1.7.0}/dapper/dpr_config.py +7 -5
  12. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/DoublePendulum/__init__.py +23 -16
  13. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/DoublePendulum/demo.py +19 -9
  14. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/DoublePendulum/settings101.py +6 -7
  15. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Id/__init__.py +3 -2
  16. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Ikeda/__init__.py +15 -16
  17. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Ikeda/demo.py +8 -8
  18. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Ikeda/some_settings_01.py +8 -8
  19. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/KS/__init__.py +53 -46
  20. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/KS/bocquet2019.py +8 -7
  21. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/KS/compare_schemes.py +14 -15
  22. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/KS/demo.py +13 -13
  23. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LA/__init__.py +20 -20
  24. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LA/demo.py +1 -0
  25. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LA/evensen2009.py +8 -8
  26. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LA/raanes2015.py +19 -16
  27. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LA/small.py +8 -8
  28. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz05/__init__.py +28 -21
  29. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz05/demo.py +1 -1
  30. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz05/settings01.py +6 -6
  31. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/__init__.py +4 -5
  32. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/anderson2010rhf.py +6 -6
  33. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/demo.py +6 -6
  34. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/extras.py +9 -11
  35. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/mandel2016.py +9 -6
  36. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/ramgraber2022.py +6 -6
  37. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/sakov2012.py +6 -6
  38. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/wiljes2017.py +1 -1
  39. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz84/__init__.py +6 -7
  40. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz84/demo.py +8 -9
  41. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz84/pajonk2012.py +7 -7
  42. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/__init__.py +1 -1
  43. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/anderson2009.py +14 -9
  44. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/bocquet2010.py +4 -4
  45. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/bocquet2010_m40.py +3 -2
  46. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/bocquet2015loc.py +1 -1
  47. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/demo.py +1 -1
  48. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/extras.py +16 -13
  49. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/frei2013bridging.py +6 -6
  50. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/miyoshi2011.py +13 -10
  51. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/pinheiro2019.py +7 -6
  52. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/sakov2008.py +7 -7
  53. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/spantini2019.py +8 -8
  54. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/spectral_obs.py +10 -11
  55. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/todter2015.py +5 -5
  56. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96s/__init__.py +23 -16
  57. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96s/grudzien2020.py +3 -3
  58. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LorenzUV/__init__.py +26 -22
  59. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LorenzUV/demo.py +6 -6
  60. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LorenzUV/illust_LorenzUV.py +54 -44
  61. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LorenzUV/illust_parameterizations.py +47 -37
  62. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LorenzUV/lorenz96.py +11 -11
  63. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LorenzUV/wilks05.py +14 -14
  64. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LotkaVolterra/__init__.py +14 -10
  65. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LotkaVolterra/demo.py +2 -2
  66. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/LotkaVolterra/settings101.py +7 -8
  67. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/__init__.py +62 -40
  68. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/counillon2009.py +3 -3
  69. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/demo.py +12 -10
  70. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/illust_obs.py +8 -8
  71. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/sakov2008.py +12 -12
  72. dapper-1.7.0/dapper/mods/README.md +94 -0
  73. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/VL20/__init__.py +28 -23
  74. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/VL20/demo.py +4 -3
  75. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/__init__.py +50 -29
  76. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/explore_props.py +90 -80
  77. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/integration.py +2 -2
  78. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/utils.py +26 -15
  79. {dapper-1.6.0 → dapper-1.7.0}/dapper/stats.py +125 -112
  80. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/chronos.py +78 -58
  81. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/colors.py +26 -20
  82. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/datafiles.py +20 -14
  83. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/linalg.py +5 -5
  84. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/liveplotting.py +389 -347
  85. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/localization.py +54 -46
  86. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/matrices.py +76 -74
  87. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/multiproc.py +15 -6
  88. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/progressbar.py +22 -13
  89. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/randvars.py +38 -36
  90. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/remote/autoscaler.py +131 -91
  91. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/remote/uplink.py +69 -49
  92. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/rounding.py +3 -3
  93. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/seeding.py +6 -4
  94. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/series.py +57 -44
  95. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/viz.py +101 -92
  96. {dapper-1.6.0 → dapper-1.7.0}/dapper/xp_launch.py +71 -43
  97. {dapper-1.6.0 → dapper-1.7.0}/dapper/xp_process.py +145 -83
  98. dapper-1.7.0/dapper.egg-info/PKG-INFO +29 -0
  99. {dapper-1.6.0 → dapper-1.7.0}/dapper.egg-info/requires.txt +7 -12
  100. {dapper-1.6.0 → dapper-1.7.0}/examples/basic_1.py +6 -8
  101. {dapper-1.6.0 → dapper-1.7.0}/examples/basic_2.py +9 -1
  102. dapper-1.7.0/pyproject.toml +133 -0
  103. {dapper-1.6.0 → dapper-1.7.0}/setup.py +56 -54
  104. {dapper-1.6.0 → dapper-1.7.0}/tests/test_HMMs.py +3 -3
  105. {dapper-1.6.0 → dapper-1.7.0}/tests/test_TLMs.py +16 -3
  106. {dapper-1.6.0 → dapper-1.7.0}/tests/test_data.py +18 -18
  107. {dapper-1.6.0 → dapper-1.7.0}/tests/test_example_2.py +2 -2
  108. {dapper-1.6.0 → dapper-1.7.0}/tests/test_iEnKS.py +53 -49
  109. dapper-1.7.0/tests/test_localization.py +64 -0
  110. {dapper-1.6.0 → dapper-1.7.0}/tests/test_matrices.py +1 -1
  111. {dapper-1.6.0 → dapper-1.7.0}/tests/test_plotting.py +14 -15
  112. {dapper-1.6.0 → dapper-1.7.0}/tests/test_round2.py +49 -55
  113. dapper-1.6.0/PKG-INFO +0 -83
  114. dapper-1.6.0/dapper/da_methods/extended.py +0 -114
  115. dapper-1.6.0/dapper/mods/README.md +0 -110
  116. dapper-1.6.0/dapper.egg-info/PKG-INFO +0 -83
  117. dapper-1.6.0/pyproject.toml +0 -143
  118. dapper-1.6.0/tests/test_localization.py +0 -56
  119. {dapper-1.6.0 → dapper-1.7.0}/LICENCE.txt +0 -0
  120. {dapper-1.6.0 → dapper-1.7.0}/dapper/README.md +0 -0
  121. {dapper-1.6.0 → dapper-1.7.0}/dapper/da_methods/README.md +0 -0
  122. {dapper-1.6.0 → dapper-1.7.0}/dapper/dpr_config.yaml +0 -0
  123. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz63/bocquet2012.py +0 -0
  124. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz84/harder.py +0 -0
  125. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/hoteit2015.py +0 -0
  126. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/raanes2016.py +0 -0
  127. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/Lorenz96/todter2015_G.py +0 -0
  128. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/Makefile +0 -0
  129. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/README.md +0 -0
  130. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/__init__.py +0 -0
  131. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/calc.f90 +0 -0
  132. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/data.f90 +0 -0
  133. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/helmholtz.f90 +0 -0
  134. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/interface.f90 +0 -0
  135. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/nfw.f90 +0 -0
  136. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/parameters.f90 +0 -0
  137. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/prms_cou09_ens.txt +0 -0
  138. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/prms_cou09_truth.txt +0 -0
  139. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/prms_counillon2009_ens.txt +0 -0
  140. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/prms_counillon2009_truth.txt +0 -0
  141. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/prms_sak08.txt +0 -0
  142. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/prms_sakov2008.txt +0 -0
  143. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/prms_sample_generation.txt +0 -0
  144. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/prms_test_model.txt +0 -0
  145. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/qg.f90 +0 -0
  146. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/qgflux.f90 +0 -0
  147. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/qgstep.f90 +0 -0
  148. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/f90/utils.f90 +0 -0
  149. {dapper-1.6.0 → dapper-1.7.0}/dapper/mods/QG/governing_eqn.png +0 -0
  150. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/__init__.py +0 -0
  151. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/remote/README.md +0 -0
  152. {dapper-1.6.0 → dapper-1.7.0}/dapper/tools/remote/__init__.py +0 -0
  153. {dapper-1.6.0 → dapper-1.7.0}/dapper.egg-info/SOURCES.txt +0 -0
  154. {dapper-1.6.0 → dapper-1.7.0}/dapper.egg-info/dependency_links.txt +0 -0
  155. {dapper-1.6.0 → dapper-1.7.0}/dapper.egg-info/top_level.txt +0 -0
  156. {dapper-1.6.0 → dapper-1.7.0}/setup.cfg +0 -0
  157. {dapper-1.6.0 → dapper-1.7.0}/tests/__init__.py +0 -0
  158. {dapper-1.6.0 → dapper-1.7.0}/tests/test_demos.py +0 -0
  159. {dapper-1.6.0 → dapper-1.7.0}/tests/test_operator.py +0 -0
  160. {dapper-1.6.0 → dapper-1.7.0}/tests/test_printing.py +0 -0
  161. {dapper-1.6.0 → dapper-1.7.0}/tests/test_randvars.py +0 -0
  162. {dapper-1.6.0 → dapper-1.7.0}/tests/test_rng.py +0 -0
dapper-1.7.0/PKG-INFO ADDED
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.1
2
+ Name: dapper
3
+ Version: 1.7.0
4
+ Summary: DAPPER benchmarks the performance of data assimilation (DA) methods.
5
+ Author: Patrick N. Raanes
6
+ Author-email: patrick.n.raanes@gmail.com
7
+ Project-URL: Documentation, https://nansencenter.github.io/DAPPER/
8
+ Project-URL: Source, https://github.com/nansencenter/DAPPER
9
+ Project-URL: Tracker, https://github.com/nansencenter/DAPPER/issues
10
+ Keywords: data-assimilation enkf kalman-filtering state-estimation particle-filter kalman bayesian-methods bayesian-filter chaos
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ Provides-Extra: Qt
20
+ Provides-Extra: debug
21
+ Provides-Extra: test
22
+ Provides-Extra: lint
23
+ Provides-Extra: build
24
+ Provides-Extra: dev
25
+ License-File: LICENCE.txt
26
+
27
+ It is usually best to install from source (github),
28
+ so that you the code is readily available to play with.
29
+ See full README on [github](https://github.com/nansencenter/DAPPER).
@@ -28,19 +28,18 @@ and then estimate that truth given the models and noisy observations.
28
28
 
29
29
  [Install](#installation), then
30
30
  read, run and try to understand `examples/basic_{1,2,3}.py`.
31
- Some of the examples can also be opened in Jupyter, and thereby run in the cloud
32
- (i.e. *without installation*, but requiring Google login): [![Open In Collab](https://colab.research.google.com/assets/colab-badge.svg)](http://colab.research.google.com/github/nansencenter/DAPPER).
31
+ Some of the examples also exist as Jupyter notebooks, and can be run in the cloud
32
+ *without installation* (but requiring Google login): [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://colab.research.google.com/github/nansencenter/DAPPER).
33
33
  This [screencast](https://www.youtube.com/watch?v=YtalK0Zkzvg&t=6475s)
34
- provides an introduction.
34
+ provides an overview to DAPPER.
35
35
  The [documentation](https://nansencenter.github.io/DAPPER)
36
- includes general guidelines and the API,
37
- but for any serious use you will want to read and adapt the code yourself.
38
- If you use it in a publication, please cite, e.g.,
36
+ includes general guidelines and the API reference,
37
+ but most users must expect to read the code as well.
38
+ If used towards a publication, please cite as
39
39
  *The experiments used (inspiration from) DAPPER [ref], version 1.6.0*,
40
- where [ref] points to [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.10710355.svg)](https://doi.org/10.5281/zenodo.10710355).
41
- Lastly, for an introduction to DA theory also using Python,
42
- see these [tutorials](https://github.com/nansencenter/DA-tutorials).
43
-
40
+ or similar, where [ref] points to [![DOI](https://joss.theoj.org/papers/10.21105/joss.05150/status.svg)](https://doi.org/10.21105/joss.05150).
41
+ Also see the interactive [tutorials on DA theory](https://github.com/nansencenter/DA-tutorials)
42
+ with Python.
44
43
 
45
44
  ## Highlights
46
45
 
@@ -50,26 +49,30 @@ through a variety of typical [test cases](#test-cases-models) and statistics. It
50
49
  (b) facilitates comparative studies, thus promoting the
51
50
  (a) reliability and
52
51
  (b) relevance of the results.
53
- For example, this figure is generated by `examples/basic_3.py`,
54
- making use of built-in tools for experiment and result management,
52
+ For example, the figure below is generated by `examples/basic_3.py`,
55
53
  reproduces figure 5.7 of [these lecture notes](http://cerea.enpc.fr/HomePages/bocquet/teaching/assim-mb-en.pdf).
56
-
57
- ![Comparative benchmarks with Lorenz'96 plotted as a function of the ensemble size (N)](./docs/imgs/ex3.svg)
58
-
59
- DAPPER is (c) open source, written in Python, and (d) focuses on readability;
60
- this promotes the (c) reproduction and (d) dissemination of the underlying science,
54
+ DAPPER is
55
+ (c) open source, written in Python, and
56
+ (d) focuses on readability;
57
+ this promotes the
58
+ (c) reproduction and
59
+ (d) dissemination of the underlying science,
61
60
  and makes it easy to adapt and extend.
62
61
 
63
- It also illustrates how to parallelise ensemble forecasts (e.g. the QG model),
64
- local analyses (e.g. the LETKF), and independent experiments (e.g. `examples/basic_3.py`).
65
- It comes with a battery of diagnostics and statistics.
66
- These all get averaged over subdomains (e..g "ocean" and "land") and then in time.
62
+ ![Comparative benchmarks with Lorenz-96 plotted as a function of the ensemble size (N)](./docs/imgs/ex3.svg)
63
+
64
+ DAPPER demonstrates how to parallelise ensemble forecasts (e.g., the QG model),
65
+ local analyses (e.g., the LETKF), and independent experiments (e.g., `examples/basic_3.py`).
66
+ It includes a battery of diagnostics and statistics,
67
+ which all get averaged over subdomains (e.g., "ocean" and "land") and then in time.
67
68
  Confidence intervals are computed, including correction for auto-correlations,
68
69
  and used for uncertainty quantification, and significant digits printing.
69
70
  Several diagnostics are included in the on-line "liveplotting" illustrated below,
70
71
  which may be paused for further interactive inspection.
72
+ In summary, DAPPER is well suited for teaching and fundamental DA research.
73
+ Also see its [drawbacks](#similar-projects).
71
74
 
72
- ![EnKF - Lorenz'63](./docs/imgs/ex1.jpg)
75
+ ![EnKF - Lorenz-96](./docs/imgs/ex1.jpg)
73
76
 
74
77
  <!-- Non-highlighted features:
75
78
  - Time sequences use via `tools.chronos.Chronology` and `tools.chronos.Ticker`.
@@ -79,12 +82,9 @@ which may be paused for further interactive inspection.
79
82
  provides input flexibility/overloading,
80
83
  lazy eval that facilitates the use of non-diagonal
81
84
  covariance matrices (whether sparse or full).
85
+ - built-in tools for experiment and result management,
82
86
  -->
83
87
 
84
- In summary, DAPPER is well suited for teaching and fundamental DA research.
85
- Also see its [drawbacks](#similar-projects).
86
-
87
-
88
88
  ## Installation
89
89
 
90
90
  Successfully tested on Linux/Mac/Windows.
@@ -98,12 +98,12 @@ open the [Anaconda terminal](https://docs.conda.io/projects/conda/en/latest/user
98
98
  and run the following commands:
99
99
 
100
100
  ```sh
101
- conda create --yes --name dapper-env python=3.9
101
+ conda create --yes --name dapper-env python=3.12
102
102
  conda activate dapper-env
103
103
  python --version
104
104
  ```
105
105
 
106
- Ensure the printed version is 3.9 or more.
106
+ Ensure the printed version is as desired.
107
107
  *Keep using the same terminal for the commands below.*
108
108
 
109
109
  ### Install
@@ -114,19 +114,17 @@ Ensure the printed version is 3.9 or more.
114
114
 
115
115
  - Download and unzip (or `git clone`) DAPPER.
116
116
  - Move the resulting folder wherever you like,
117
- and `cd` into it
118
- *(ensure you're in the folder with a `setup.py` file)*.
119
- - `pip install -e '.[dev]'`
120
- You can omit `[dev]` if you don't need to do serious development.
117
+ and `cd` into it *(ensure you're in the folder with a `setup.py` file)*.
118
+ - `pip install -e '.'`
121
119
 
122
120
  #### *Or*: Install as library
123
121
 
124
122
  *Do you just want to run a script that requires DAPPER?* Then
125
123
 
126
- - If the script comes with a `requirements.txt` file, then do
124
+ - If the script comes with a `requirements.txt` file that lists DAPPER, then do
127
125
  `pip install -r path/to/requirements.txt`.
128
126
  - If not, hopefully you know the version of DAPPER needed. Run
129
- `pip install dapper==1.5.1` to get version `1.5.1` (as an example).
127
+ `pip install dapper==1.6.0` to get version `1.6.0` (as an example).
130
128
 
131
129
  #### *Finally*: Test the installation
132
130
 
@@ -174,7 +172,7 @@ The particle filter is tuned with "effective-N monitoring",
174
172
  "regularization/jittering" strength, and more.
175
173
 
176
174
  For a list of ready-made experiments with suitable,
177
- tuned settings for a given method (e.g. the `iEnKS`), use:
175
+ tuned settings for a given method (e.g., the `iEnKS`), use:
178
176
 
179
177
  ```sh
180
178
  grep -r "xp.*iEnKS" dapper/mods
@@ -183,6 +181,9 @@ grep -r "xp.*iEnKS" dapper/mods
183
181
 
184
182
  ## Test cases (models)
185
183
 
184
+ Simple models facilitate the reliability, reproducibility,
185
+ and interpretability of experiment results.
186
+
186
187
  Model | Lin | TLM** | PDE? | Phys.dim. | State len | Lyap≥0 | Implementer
187
188
  ----------- | --- | ----- | ---- | --------- | --------- | ------ | ----------
188
189
  Id | Yes | Yes | No | N/A | * | 0 | Raanes
@@ -4,7 +4,7 @@
4
4
  .. include:: ./README.md
5
5
  """
6
6
 
7
- __version__ = "1.6.0"
7
+ __version__ = "1.7.0"
8
8
 
9
9
  # A parsimonious list of imports used in the examples
10
10
  from .dpr_config import rc
@@ -2,6 +2,7 @@
2
2
 
3
3
  .. include:: ./README.md
4
4
  """
5
+
5
6
  from pathlib import Path
6
7
 
7
8
 
@@ -82,7 +83,7 @@ def da_method(*default_dataclasses):
82
83
  def set_field(name, type_, val):
83
84
  """Set the inherited (i.e. default, i.e. has value) field."""
84
85
  # Ensure annotations
85
- cls.__annotations__ = getattr(cls, '__annotations__', {})
86
+ cls.__annotations__ = getattr(cls, "__annotations__", {})
86
87
  # Set annotation
87
88
  cls.__annotations__[name] = type_
88
89
  # Set value
@@ -102,7 +103,7 @@ def da_method(*default_dataclasses):
102
103
  # Define the new assimilate method (has bells and whistles)
103
104
  def assimilate(self, HMM, xx, yy, desc=None, fail_gently=False, **stat_kwargs):
104
105
  # Progressbar name
105
- pb_name_hook = self.da_method if desc is None else desc # noqa
106
+ pb_name_hook = self.da_method if desc is None else desc # noqa
106
107
 
107
108
  # Init stats
108
109
  self.stats = dapper.stats.Stats(self, HMM, xx, yy, **stat_kwargs)
@@ -120,7 +121,7 @@ def da_method(*default_dataclasses):
120
121
  # Don't use _print_cropped_traceback here -- It would
121
122
  # crop out errors in the DAPPER infrastructure itself.
122
123
  raise
123
- self.stat("duration", time.time()-time0)
124
+ self.stat("duration", time.time() - time0)
124
125
 
125
126
  # Overwrite the assimilate method with the new one
126
127
  try:
@@ -128,12 +129,14 @@ def da_method(*default_dataclasses):
128
129
  except AttributeError as error:
129
130
  raise AttributeError(
130
131
  "Classes decorated by da_method()"
131
- " must define a method called 'assimilate'.") from error
132
+ " must define a method called 'assimilate'."
133
+ ) from error
132
134
  cls.assimilate = functools.wraps(_assimilate)(assimilate)
133
135
 
134
136
  # Shortcut for register_stat
135
137
  def stat(self, name, value):
136
138
  dapper.stats.register_stat(self.stats, name, value)
139
+
137
140
  cls.stat = stat
138
141
 
139
142
  # Make self.__class__.__name__ an attrib.
@@ -141,6 +144,7 @@ def da_method(*default_dataclasses):
141
144
  cls.da_method = cls.__name__
142
145
 
143
146
  return cls
147
+
144
148
  return dataclass_with_defaults
145
149
 
146
150
 
@@ -155,15 +159,17 @@ def _print_cropped_traceback(ERR):
155
159
  msg = "Traceback (most recent call last):\n"
156
160
  try:
157
161
  # If in IPython, use its coloring functionality
158
- __IPYTHON__ # type: ignore
162
+ __IPYTHON__ # type: ignore # noqa: B018
159
163
  except (NameError, ImportError):
160
164
  msg += "".join(traceback.format_tb(ERR.__traceback__))
161
165
  else:
162
166
  from IPython.core.debugger import Pdb
167
+
163
168
  pdb_instance = Pdb()
164
169
  pdb_instance.curframe = inspect.currentframe()
165
170
 
166
171
  import dapper.da_methods
172
+
167
173
  keep = False
168
174
  for frame in traceback.walk_tb(ERR.__traceback__):
169
175
  if keep:
@@ -175,10 +181,12 @@ def _print_cropped_traceback(ERR):
175
181
  return msg
176
182
 
177
183
  msg = crop_traceback(ERR) + "\nError message: " + str(ERR)
178
- msg += ("\n\nResuming execution."
179
- "\nIf instead you wish to raise the exceptions as usual,"
180
- "\nwhich will halt the execution (and enable post-mortem debug),"
181
- "\nthen use `fail_gently=False`")
184
+ msg += (
185
+ "\n\nResuming execution."
186
+ "\nIf instead you wish to raise the exceptions as usual,"
187
+ "\nwhich will halt the execution (and enable post-mortem debug),"
188
+ "\nthen use `fail_gently=False`"
189
+ )
182
190
  print(msg, file=sys.stderr)
183
191
 
184
192
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  Many are based on `bib.raanes2016thesis`.
4
4
  """
5
+
5
6
  from typing import Callable, Optional
6
7
 
7
8
  import numpy as np
@@ -25,14 +26,14 @@ class Climatology:
25
26
 
26
27
  def assimilate(self, HMM, xx, yy):
27
28
  muC = np.mean(xx, 0)
28
- AC = xx - muC
29
- PC = CovMat(AC, 'A')
29
+ AC = xx - muC
30
+ PC = CovMat(AC, "A")
30
31
 
31
32
  self.stats.assess(0, mu=muC, Cov=PC)
32
33
  self.stats.trHK[:] = 0
33
34
 
34
35
  for k, ko, _, _ in progbar(HMM.tseq.ticker):
35
- fau = 'u' if ko is None else 'fau'
36
+ fau = "u" if ko is None else "fau"
36
37
  self.stats.assess(k, ko, fau, mu=muC, Cov=PC)
37
38
 
38
39
 
@@ -49,13 +50,13 @@ class OptInterp:
49
50
 
50
51
  # Compute "climatological" Kalman gain
51
52
  muC = np.mean(xx, 0)
52
- AC = xx - muC
53
- PC = (AC.T @ AC) / (xx.shape[0] - 1)
53
+ AC = xx - muC
54
+ PC = (AC.T @ AC) / (xx.shape[0] - 1)
54
55
 
55
56
  # Setup scalar "time-series" covariance dynamics.
56
57
  # ONLY USED FOR DIAGNOSTICS, not to affect the Kalman gain.
57
- L = series.estimate_corr_length(AC.ravel(order='F'))
58
- SM = fit_sigmoid(1/2, L, 0)
58
+ L = series.estimate_corr_length(AC.ravel(order="F"))
59
+ SM = fit_sigmoid(1 / 2, L, 0)
59
60
 
60
61
  # Init
61
62
  mu = muC
@@ -63,19 +64,19 @@ class OptInterp:
63
64
 
64
65
  for k, ko, t, dt in progbar(HMM.tseq.ticker):
65
66
  # Forecast
66
- mu = HMM.Dyn(mu, t-dt, dt)
67
+ mu = HMM.Dyn(mu, t - dt, dt)
67
68
  if ko is not None:
68
- self.stats.assess(k, ko, 'f', mu=muC, Cov=PC)
69
+ self.stats.assess(k, ko, "f", mu=muC, Cov=PC)
69
70
 
70
71
  # Analysis
71
- H = HMM.Obs(ko).linear(muC)
72
- KG = mrdiv(PC@H.T, H@PC@H.T + HMM.Obs(ko).noise.C.full)
73
- mu = muC + KG@(yy[ko] - HMM.Obs(ko)(muC))
72
+ H = HMM.Obs(ko).linear(muC)
73
+ KG = mrdiv(PC @ H.T, H @ PC @ H.T + HMM.Obs(ko).noise.C.full)
74
+ mu = muC + KG @ (yy[ko] - HMM.Obs(ko)(muC))
74
75
 
75
- P = (Id - KG@H) @ PC
76
- SM = fit_sigmoid(P.trace()/PC.trace(), L, k)
76
+ P = (Id - KG @ H) @ PC
77
+ SM = fit_sigmoid(P.trace() / PC.trace(), L, k)
77
78
 
78
- self.stats.assess(k, ko, mu=mu, Cov=2*PC*SM(k))
79
+ self.stats.assess(k, ko, mu=mu, Cov=2 * PC * SM(k))
79
80
 
80
81
 
81
82
  @da_method()
@@ -88,27 +89,27 @@ class Var3D:
88
89
  """
89
90
 
90
91
  B: Optional[np.ndarray] = None
91
- xB: float = 1.0
92
+ xB: float = 1.0
92
93
 
93
94
  def assimilate(self, HMM, xx, yy):
94
95
  Id = np.eye(HMM.Nx)
95
96
  if isinstance(self.B, np.ndarray):
96
97
  # compare ndarray 1st to avoid == error for ndarray
97
98
  B = self.B.astype(float)
98
- elif self.B in (None, 'clim'):
99
+ elif self.B in (None, "clim"):
99
100
  # Use climatological cov, estimated from truth
100
101
  B = np.cov(xx.T)
101
- elif self.B == 'eye':
102
+ elif self.B == "eye":
102
103
  B = Id
103
104
  else:
104
105
  raise ValueError("Bad input B.")
105
106
  B *= self.xB
106
107
 
107
108
  # ONLY USED FOR DIAGNOSTICS, not to change the Kalman gain.
108
- CC = 2*np.cov(xx.T)
109
- L = series.estimate_corr_length(center(xx)[0].ravel(order='F'))
110
- P = HMM.X0.C.full
111
- SM = fit_sigmoid(P.trace()/CC.trace(), L, 0)
109
+ CC = 2 * np.cov(xx.T)
110
+ L = series.estimate_corr_length(center(xx)[0].ravel(order="F"))
111
+ P = HMM.X0.C.full
112
+ SM = fit_sigmoid(P.trace() / CC.trace(), L, 0)
112
113
 
113
114
  # Init
114
115
  mu = HMM.X0.mu
@@ -116,20 +117,20 @@ class Var3D:
116
117
 
117
118
  for k, ko, t, dt in progbar(HMM.tseq.ticker):
118
119
  # Forecast
119
- mu = HMM.Dyn(mu, t-dt, dt)
120
- P = CC*SM(k)
120
+ mu = HMM.Dyn(mu, t - dt, dt)
121
+ P = CC * SM(k)
121
122
 
122
123
  if ko is not None:
123
- self.stats.assess(k, ko, 'f', mu=mu, Cov=P)
124
+ self.stats.assess(k, ko, "f", mu=mu, Cov=P)
124
125
 
125
126
  # Analysis
126
- H = HMM.Obs(ko).linear(mu)
127
- KG = mrdiv(B@H.T, H@B@H.T + HMM.Obs(ko).noise.C.full)
128
- mu = mu + KG@(yy[ko] - HMM.Obs(ko)(mu))
127
+ H = HMM.Obs(ko).linear(mu)
128
+ KG = mrdiv(B @ H.T, H @ B @ H.T + HMM.Obs(ko).noise.C.full)
129
+ mu = mu + KG @ (yy[ko] - HMM.Obs(ko)(mu))
129
130
 
130
131
  # Re-calibrate fit_sigmoid with new W0 = Pa/B
131
- P = (Id - KG@H) @ B
132
- SM = fit_sigmoid(P.trace()/CC.trace(), L, k)
132
+ P = (Id - KG @ H) @ B
133
+ SM = fit_sigmoid(P.trace() / CC.trace(), L, k)
133
134
 
134
135
  self.stats.assess(k, ko, mu=mu, Cov=P)
135
136
 
@@ -151,14 +152,17 @@ def fit_sigmoid(Sb, L, kb):
151
152
  - `b` to match values of `S(kb)` and `Sb`
152
153
  """
153
154
 
154
- def sigmoid(k): return 1/(1+np.exp(-k)) # normalized sigmoid
155
- def inv_sig(s): return np.log(s/(1-s)) # its inverse
155
+ def sigmoid(k):
156
+ return 1 / (1 + np.exp(-k)) # normalized sigmoid
157
+
158
+ def inv_sig(s):
159
+ return np.log(s / (1 - s)) # its inverse
156
160
 
157
- a = 1/L
161
+ a = 1 / L
158
162
  b = inv_sig(Sb)
159
163
 
160
164
  def S(k):
161
- return sigmoid(b + a*(k-kb))
165
+ return sigmoid(b + a * (k - kb))
162
166
 
163
167
  return S
164
168
 
@@ -176,9 +180,9 @@ class Persistence:
176
180
  prev = xx[0]
177
181
  self.stats.assess(0, mu=prev)
178
182
  for k, ko, _t, _dt in progbar(HMM.tseq.ticker):
179
- self.stats.assess(k, ko, 'fu', mu=xx[k-1])
183
+ self.stats.assess(k, ko, "fu", mu=xx[k - 1])
180
184
  if ko is not None:
181
- self.stats.assess(k, ko, 'a', mu=prev)
185
+ self.stats.assess(k, ko, "a", mu=prev)
182
186
  prev = xx[k]
183
187
 
184
188
 
@@ -196,6 +200,6 @@ class PreProg:
196
200
  def assimilate(self, HMM, xx, yy):
197
201
  self.stats.assess(0, mu=self.schedule(0, xx, yy))
198
202
  for k, ko, _t, _dt in progbar(HMM.tseq.ticker):
199
- self.stats.assess(k, ko, 'fu', mu=self.schedule(k, xx, yy))
203
+ self.stats.assess(k, ko, "fu", mu=self.schedule(k, xx, yy))
200
204
  if ko is not None:
201
- self.stats.assess(k, ko, 'a', mu=self.schedule(k, xx, yy))
205
+ self.stats.assess(k, ko, "a", mu=self.schedule(k, xx, yy))