dapper 1.5.1__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 (163) hide show
  1. {dapper-1.5.1 → dapper-1.7.0}/PKG-INFO +1 -1
  2. {dapper-1.5.1 → dapper-1.7.0}/README.md +59 -45
  3. {dapper-1.5.1 → dapper-1.7.0}/dapper/README.md +4 -4
  4. {dapper-1.5.1 → dapper-1.7.0}/dapper/__init__.py +2 -2
  5. {dapper-1.5.1 → dapper-1.7.0}/dapper/da_methods/README.md +2 -1
  6. {dapper-1.5.1 → dapper-1.7.0}/dapper/da_methods/__init__.py +17 -9
  7. {dapper-1.5.1 → dapper-1.7.0}/dapper/da_methods/baseline.py +42 -38
  8. {dapper-1.5.1 → dapper-1.7.0}/dapper/da_methods/ensemble.py +320 -268
  9. dapper-1.7.0/dapper/da_methods/extended.py +113 -0
  10. {dapper-1.5.1 → dapper-1.7.0}/dapper/da_methods/other.py +63 -54
  11. {dapper-1.5.1 → dapper-1.7.0}/dapper/da_methods/particle.py +179 -166
  12. {dapper-1.5.1 → dapper-1.7.0}/dapper/da_methods/variational.py +127 -110
  13. {dapper-1.5.1 → dapper-1.7.0}/dapper/dpr_config.py +7 -5
  14. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/DoublePendulum/__init__.py +23 -16
  15. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/DoublePendulum/demo.py +19 -9
  16. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/DoublePendulum/settings101.py +6 -7
  17. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Id/__init__.py +3 -2
  18. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Ikeda/__init__.py +15 -16
  19. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Ikeda/demo.py +8 -8
  20. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Ikeda/some_settings_01.py +8 -8
  21. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/KS/__init__.py +53 -46
  22. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/KS/bocquet2019.py +8 -7
  23. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/KS/compare_schemes.py +14 -15
  24. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/KS/demo.py +13 -13
  25. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LA/__init__.py +22 -20
  26. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LA/demo.py +1 -0
  27. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LA/evensen2009.py +8 -8
  28. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LA/raanes2015.py +19 -16
  29. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LA/small.py +8 -8
  30. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz05/__init__.py +28 -21
  31. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz05/demo.py +1 -1
  32. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz05/settings01.py +6 -6
  33. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz63/__init__.py +4 -5
  34. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz63/anderson2010rhf.py +6 -6
  35. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz63/bocquet2012.py +1 -1
  36. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz63/demo.py +6 -6
  37. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz63/extras.py +9 -11
  38. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz63/mandel2016.py +9 -6
  39. dapper-1.7.0/dapper/mods/Lorenz63/ramgraber2022.py +37 -0
  40. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz63/sakov2012.py +6 -6
  41. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz63/wiljes2017.py +1 -1
  42. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz84/__init__.py +6 -7
  43. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz84/demo.py +8 -9
  44. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz84/pajonk2012.py +7 -7
  45. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/__init__.py +1 -1
  46. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/anderson2009.py +14 -9
  47. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/bocquet2010.py +4 -4
  48. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/bocquet2010_m40.py +3 -2
  49. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/bocquet2015loc.py +1 -1
  50. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/demo.py +1 -1
  51. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/extras.py +16 -13
  52. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/frei2013bridging.py +6 -6
  53. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/miyoshi2011.py +13 -10
  54. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/pinheiro2019.py +7 -6
  55. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/sakov2008.py +7 -7
  56. dapper-1.7.0/dapper/mods/Lorenz96/spantini2019.py +41 -0
  57. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/spectral_obs.py +10 -11
  58. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/todter2015.py +5 -5
  59. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/todter2015_G.py +1 -1
  60. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96s/__init__.py +25 -17
  61. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96s/grudzien2020.py +3 -3
  62. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LorenzUV/__init__.py +26 -22
  63. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LorenzUV/demo.py +6 -6
  64. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LorenzUV/illust_LorenzUV.py +54 -44
  65. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LorenzUV/illust_parameterizations.py +47 -37
  66. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LorenzUV/lorenz96.py +11 -11
  67. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LorenzUV/wilks05.py +14 -14
  68. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LotkaVolterra/__init__.py +14 -10
  69. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LotkaVolterra/demo.py +2 -2
  70. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/LotkaVolterra/settings101.py +7 -8
  71. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/__init__.py +63 -41
  72. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/counillon2009.py +3 -3
  73. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/demo.py +12 -10
  74. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/README.md +36 -32
  75. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/illust_obs.py +8 -8
  76. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/sakov2008.py +41 -34
  77. dapper-1.7.0/dapper/mods/README.md +94 -0
  78. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/VL20/__init__.py +28 -23
  79. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/VL20/demo.py +4 -3
  80. dapper-1.7.0/dapper/mods/__init__.py +258 -0
  81. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/explore_props.py +90 -80
  82. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/integration.py +4 -3
  83. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/utils.py +26 -15
  84. {dapper-1.5.1 → dapper-1.7.0}/dapper/stats.py +126 -115
  85. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/chronos.py +78 -58
  86. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/colors.py +26 -20
  87. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/datafiles.py +21 -15
  88. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/linalg.py +5 -5
  89. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/liveplotting.py +434 -383
  90. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/localization.py +59 -61
  91. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/matrices.py +87 -76
  92. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/multiproc.py +15 -6
  93. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/progressbar.py +22 -13
  94. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/randvars.py +47 -45
  95. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/remote/README.md +1 -1
  96. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/remote/autoscaler.py +131 -91
  97. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/remote/uplink.py +69 -49
  98. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/rounding.py +3 -3
  99. dapper-1.7.0/dapper/tools/seeding.py +31 -0
  100. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/series.py +57 -44
  101. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/viz.py +101 -92
  102. {dapper-1.5.1 → dapper-1.7.0}/dapper/xp_launch.py +71 -43
  103. {dapper-1.5.1 → dapper-1.7.0}/dapper/xp_process.py +145 -83
  104. {dapper-1.5.1 → dapper-1.7.0}/dapper.egg-info/PKG-INFO +1 -1
  105. {dapper-1.5.1 → dapper-1.7.0}/dapper.egg-info/SOURCES.txt +3 -0
  106. {dapper-1.5.1 → dapper-1.7.0}/dapper.egg-info/requires.txt +7 -12
  107. {dapper-1.5.1 → dapper-1.7.0}/examples/basic_1.py +8 -10
  108. {dapper-1.5.1 → dapper-1.7.0}/examples/basic_2.py +11 -3
  109. dapper-1.7.0/pyproject.toml +133 -0
  110. {dapper-1.5.1 → dapper-1.7.0}/setup.py +56 -55
  111. dapper-1.7.0/tests/test_HMMs.py +58 -0
  112. {dapper-1.5.1 → dapper-1.7.0}/tests/test_TLMs.py +16 -3
  113. {dapper-1.5.1 → dapper-1.7.0}/tests/test_data.py +18 -18
  114. {dapper-1.5.1 → dapper-1.7.0}/tests/test_example_2.py +44 -39
  115. {dapper-1.5.1 → dapper-1.7.0}/tests/test_iEnKS.py +53 -49
  116. dapper-1.7.0/tests/test_localization.py +64 -0
  117. {dapper-1.5.1 → dapper-1.7.0}/tests/test_matrices.py +1 -1
  118. {dapper-1.5.1 → dapper-1.7.0}/tests/test_plotting.py +14 -15
  119. dapper-1.7.0/tests/test_rng.py +22 -0
  120. {dapper-1.5.1 → dapper-1.7.0}/tests/test_round2.py +49 -55
  121. dapper-1.5.1/dapper/da_methods/extended.py +0 -114
  122. dapper-1.5.1/dapper/mods/README.md +0 -110
  123. dapper-1.5.1/dapper/mods/__init__.py +0 -200
  124. dapper-1.5.1/dapper/tools/seeding.py +0 -68
  125. dapper-1.5.1/pyproject.toml +0 -143
  126. dapper-1.5.1/tests/test_HMMs.py +0 -59
  127. dapper-1.5.1/tests/test_localization.py +0 -56
  128. {dapper-1.5.1 → dapper-1.7.0}/LICENCE.txt +0 -0
  129. {dapper-1.5.1 → dapper-1.7.0}/dapper/dpr_config.yaml +0 -0
  130. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz84/harder.py +0 -0
  131. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/hoteit2015.py +0 -0
  132. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/Lorenz96/raanes2016.py +0 -0
  133. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/Makefile +0 -0
  134. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/__init__.py +0 -0
  135. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/calc.f90 +0 -0
  136. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/data.f90 +0 -0
  137. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/helmholtz.f90 +0 -0
  138. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/interface.f90 +0 -0
  139. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/nfw.f90 +0 -0
  140. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/parameters.f90 +0 -0
  141. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/prms_cou09_ens.txt +0 -0
  142. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/prms_cou09_truth.txt +0 -0
  143. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/prms_counillon2009_ens.txt +0 -0
  144. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/prms_counillon2009_truth.txt +0 -0
  145. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/prms_sak08.txt +0 -0
  146. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/prms_sakov2008.txt +0 -0
  147. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/prms_sample_generation.txt +0 -0
  148. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/prms_test_model.txt +0 -0
  149. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/qg.f90 +0 -0
  150. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/qgflux.f90 +0 -0
  151. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/qgstep.f90 +0 -0
  152. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/f90/utils.f90 +0 -0
  153. {dapper-1.5.1 → dapper-1.7.0}/dapper/mods/QG/governing_eqn.png +0 -0
  154. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/__init__.py +0 -0
  155. {dapper-1.5.1 → dapper-1.7.0}/dapper/tools/remote/__init__.py +0 -0
  156. {dapper-1.5.1 → dapper-1.7.0}/dapper.egg-info/dependency_links.txt +0 -0
  157. {dapper-1.5.1 → dapper-1.7.0}/dapper.egg-info/top_level.txt +0 -0
  158. {dapper-1.5.1 → dapper-1.7.0}/setup.cfg +0 -0
  159. {dapper-1.5.1 → dapper-1.7.0}/tests/__init__.py +0 -0
  160. {dapper-1.5.1 → dapper-1.7.0}/tests/test_demos.py +0 -0
  161. {dapper-1.5.1 → dapper-1.7.0}/tests/test_operator.py +0 -0
  162. {dapper-1.5.1 → dapper-1.7.0}/tests/test_printing.py +0 -0
  163. {dapper-1.5.1 → dapper-1.7.0}/tests/test_randvars.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dapper
3
- Version: 1.5.1
3
+ Version: 1.7.0
4
4
  Summary: DAPPER benchmarks the performance of data assimilation (DA) methods.
5
5
  Author: Patrick N. Raanes
6
6
  Author-email: patrick.n.raanes@gmail.com
@@ -10,10 +10,8 @@
10
10
 
11
11
  <img src="/docs/imgs/logo_wtxt.png" align="left" width="250"/>
12
12
 
13
- DAPPER is a set of templates for **benchmarking** the performance of
14
- **data assimilation** (DA) methods.
15
- The tests provide experimental support and guidance for
16
- new developments in DA.
13
+ DAPPER is a set of templates for **benchmarking** the performance of **data assimilation** (DA) methods.
14
+ The numerical experiments provide support and guidance for new developments in DA.
17
15
  The typical set-up is a **synthetic (twin) experiment**, where you
18
16
  specify a dynamic model and an observational model,
19
17
  and use these to generate a synthetic truth (multivariate time series),
@@ -30,19 +28,18 @@ and then estimate that truth given the models and noisy observations.
30
28
 
31
29
  [Install](#installation), then
32
30
  read, run and try to understand `examples/basic_{1,2,3}.py`.
33
- Some of the examples can also be opened in Jupyter, and thereby run in the cloud
34
- (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).
35
33
  This [screencast](https://www.youtube.com/watch?v=YtalK0Zkzvg&t=6475s)
36
- provides an introduction.
34
+ provides an overview to DAPPER.
37
35
  The [documentation](https://nansencenter.github.io/DAPPER)
38
- includes general guidelines and the API,
39
- but for any serious use you will want to read and adapt the code yourself.
40
- If you use it in a publication, please cite, e.g.,
41
- *The experiments used (inspiration from) DAPPER [ref], version 1.2.1*,
42
- where [ref] points to [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.2029296.svg)](https://doi.org/10.5281/zenodo.2029296).
43
- Lastly, for an introduction to DA theory also using Python,
44
- see these [tutorials](https://github.com/nansencenter/DA-tutorials).
45
-
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
+ *The experiments used (inspiration from) DAPPER [ref], version 1.6.0*,
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.
46
43
 
47
44
  ## Highlights
48
45
 
@@ -52,26 +49,30 @@ through a variety of typical [test cases](#test-cases-models) and statistics. It
52
49
  (b) facilitates comparative studies, thus promoting the
53
50
  (a) reliability and
54
51
  (b) relevance of the results.
55
- For example, this figure is generated by `examples/basic_3.py`,
56
- making use of built-in tools for experiment and result management,
52
+ For example, the figure below is generated by `examples/basic_3.py`,
57
53
  reproduces figure 5.7 of [these lecture notes](http://cerea.enpc.fr/HomePages/bocquet/teaching/assim-mb-en.pdf).
58
-
59
- ![Comparative benchmarks with Lorenz'96 plotted as a function of the ensemble size (N)](./docs/imgs/ex3.svg)
60
-
61
- DAPPER is (c) open source, written in Python, and (d) focuses on readability;
62
- 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,
63
60
  and makes it easy to adapt and extend.
64
61
 
65
- It also illustrates how to parallelise ensemble forecasts (e.g. the QG model),
66
- local analyses (e.g. the LETKF), and independent experiments (e.g. `examples/basic_3.py`).
67
- It comes with a battery of diagnostics and statistics.
68
- 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.
69
68
  Confidence intervals are computed, including correction for auto-correlations,
70
69
  and used for uncertainty quantification, and significant digits printing.
71
70
  Several diagnostics are included in the on-line "liveplotting" illustrated below,
72
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).
73
74
 
74
- ![EnKF - Lorenz'63](./docs/imgs/ex1.jpg)
75
+ ![EnKF - Lorenz-96](./docs/imgs/ex1.jpg)
75
76
 
76
77
  <!-- Non-highlighted features:
77
78
  - Time sequences use via `tools.chronos.Chronology` and `tools.chronos.Ticker`.
@@ -81,12 +82,9 @@ which may be paused for further interactive inspection.
81
82
  provides input flexibility/overloading,
82
83
  lazy eval that facilitates the use of non-diagonal
83
84
  covariance matrices (whether sparse or full).
85
+ - built-in tools for experiment and result management,
84
86
  -->
85
87
 
86
- In summary, DAPPER is well suited for teaching and fundamental DA research.
87
- Also see its [drawbacks](#similar-projects).
88
-
89
-
90
88
  ## Installation
91
89
 
92
90
  Successfully tested on Linux/Mac/Windows.
@@ -100,12 +98,12 @@ open the [Anaconda terminal](https://docs.conda.io/projects/conda/en/latest/user
100
98
  and run the following commands:
101
99
 
102
100
  ```sh
103
- conda create --yes --name dapper-env python=3.9
101
+ conda create --yes --name dapper-env python=3.12
104
102
  conda activate dapper-env
105
103
  python --version
106
104
  ```
107
105
 
108
- Ensure the printed version is 3.9 or more.
106
+ Ensure the printed version is as desired.
109
107
  *Keep using the same terminal for the commands below.*
110
108
 
111
109
  ### Install
@@ -116,19 +114,17 @@ Ensure the printed version is 3.9 or more.
116
114
 
117
115
  - Download and unzip (or `git clone`) DAPPER.
118
116
  - Move the resulting folder wherever you like,
119
- and `cd` into it
120
- *(ensure you're in the folder with a `setup.py` file)*.
121
- - `pip install -e '.[dev]'`
122
- 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 '.'`
123
119
 
124
120
  #### *Or*: Install as library
125
121
 
126
122
  *Do you just want to run a script that requires DAPPER?* Then
127
123
 
128
- - 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
129
125
  `pip install -r path/to/requirements.txt`.
130
126
  - If not, hopefully you know the version of DAPPER needed. Run
131
- `pip install dapper==1.0.0` to get version `1.2.3` (as an example).
127
+ `pip install dapper==1.6.0` to get version `1.6.0` (as an example).
132
128
 
133
129
  #### *Finally*: Test the installation
134
130
 
@@ -176,7 +172,7 @@ The particle filter is tuned with "effective-N monitoring",
176
172
  "regularization/jittering" strength, and more.
177
173
 
178
174
  For a list of ready-made experiments with suitable,
179
- tuned settings for a given method (e.g. the `iEnKS`), use:
175
+ tuned settings for a given method (e.g., the `iEnKS`), use:
180
176
 
181
177
  ```sh
182
178
  grep -r "xp.*iEnKS" dapper/mods
@@ -185,6 +181,9 @@ grep -r "xp.*iEnKS" dapper/mods
185
181
 
186
182
  ## Test cases (models)
187
183
 
184
+ Simple models facilitate the reliability, reproducibility,
185
+ and interpretability of experiment results.
186
+
188
187
  Model | Lin | TLM** | PDE? | Phys.dim. | State len | Lyap≥0 | Implementer
189
188
  ----------- | --- | ----- | ---- | --------- | --------- | ------ | ----------
190
189
  Id | Yes | Yes | No | N/A | * | 0 | Raanes
@@ -233,9 +232,7 @@ DAPPER is aimed at research and teaching (see discussion up top).
233
232
  Example of limitations:
234
233
 
235
234
  - It is not suited for very big models (>60k unknowns).
236
- - Time-dependent error covariances and changes in lengths of state/obs
237
- (although the `Dyn` and `Obs` models may otherwise be time-dependent).
238
- - Non-uniform time sequences not fully supported.
235
+ - Non-uniform time sequences.
239
236
 
240
237
  The scope of DAPPER is restricted because
241
238
 
@@ -321,7 +318,24 @@ in the development of DAPPER.
321
318
  [26]: https://github.com/CliMA/EnsembleKalmanProcesses.jl
322
319
 
323
320
 
324
- ## Contributors
321
+ ## Contributing
322
+
323
+ ### Issues and Pull requests
324
+
325
+ Do not hesitate to open an issue, whether to report a problem or ask a question.
326
+ It may take some time for us to get back to you,
327
+ since DAPPER is primarily a volunteer effort.
328
+ Please start by perusing the [documentation](https://nansencenter.github.io/DAPPER/dapper.html)
329
+ and searching the issue tracker for similar items.
330
+
331
+ Pull requests are very welcome.
332
+ Examples: adding a new DA method, dynamical models,
333
+ experimental configuration reproducing literature results,
334
+ or improving the features and capabilities of DAPPER.
335
+ Please keep in mind the intentional [limitations](https://github.com/nansencenter/DAPPER#similar-projects)
336
+ and read the [developers guidelines](https://nansencenter.github.io/DAPPER/dev_guide).
337
+
338
+ ### Contributors
325
339
 
326
340
  Patrick N. Raanes,
327
341
  Yumeng Chen,
@@ -345,7 +359,7 @@ and the Center for Western Weather and Water Extremes (CW3E).
345
359
  <img src="./docs/imgs/CW3E-Logo-Horizontal-FullColor.png?raw=true" width="400">
346
360
  <!-- markdownlint-restore -->
347
361
 
348
- ## Publication list
362
+ ## Publications
349
363
 
350
364
  - [Combining data assimilation and machine learning to emulate a dynamical model from sparse and noisy observations: A case study with the Lorenz 96 model](https://doi.org/10.1016/j.jocs.2020.101171)
351
365
  - [Adaptive covariance inflation in the ensemble Kalman filter by Gaussian scale mixtures](https://doi.org/10.1002/qj.3386)
@@ -5,11 +5,11 @@ the docs for the various modules.
5
5
 
6
6
  ## Installation
7
7
 
8
- See [README/Installation](https://github.com/nansencenter/DAPPER#Installation)
8
+ See [README/Installation](https://github.com/nansencenter/DAPPER#installation)
9
9
 
10
10
  ## Usage
11
11
 
12
- See [README/Getting-started](https://github.com/nansencenter/DAPPER#Getting-started)
12
+ See [README/Getting-started](https://github.com/nansencenter/DAPPER#getting-started)
13
13
 
14
14
  ##### Examples
15
15
 
@@ -26,8 +26,8 @@ your own **model** or **method**, then
26
26
  rather than trying to squeeze everything into its templates.
27
27
  - If it is relatively simple, however, you may well want to use DAPPER.
28
28
  In that case, read this:
29
- - `mods`
30
- - `da_methods`
29
+ - `dapper.mods`
30
+ - `dapper.da_methods`
31
31
 
32
32
  Since the generality of DAPPER is
33
33
  [limited](https://github.com/nansencenter/DAPPER#similar-projects)
@@ -4,12 +4,12 @@
4
4
  .. include:: ./README.md
5
5
  """
6
6
 
7
- __version__ = "1.5.1"
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
11
11
  from .tools.datafiles import find_latest_run, load_xps
12
12
  from .tools.rounding import round2sigfig
13
- from .tools.seeding import set_seed
13
+ from .tools.seeding import rng, set_seed
14
14
  from .xp_launch import combinator, seed_and_simulate, xpList
15
15
  from .xp_process import xpSpace
@@ -1,5 +1,6 @@
1
- See the README section on
1
+ Also see the section on
2
2
  [DA Methods](https://github.com/nansencenter/DAPPER#DA-Methods)
3
+ in the main README
3
4
  for an overview of the methods included with DAPPER.
4
5
 
5
6
  ## Defining your own method
@@ -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.linear(muC, t)
72
- KG = mrdiv(PC@H.T, H@PC@H.T + HMM.Obs.noise.C.full)
73
- mu = muC + KG@(yy[ko] - HMM.Obs(muC, t))
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.linear(mu, t)
127
- KG = mrdiv(B@H.T, H@B@H.T + HMM.Obs.noise.C.full)
128
- mu = mu + KG@(yy[ko] - HMM.Obs(mu, t))
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))