cloudnetpy 1.95.7__tar.gz → 1.96.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 (148) hide show
  1. {cloudnetpy-1.95.7/cloudnetpy.egg-info → cloudnetpy-1.96.0}/PKG-INFO +1 -1
  2. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/cli.py +3 -1
  3. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/output.py +12 -1
  4. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/plotting/plot_meta.py +44 -0
  5. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/plotting/plotting.py +39 -2
  6. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/__init__.py +1 -0
  7. cloudnetpy-1.96.0/cloudnetpy/products/cod.py +491 -0
  8. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/lwc.py +10 -2
  9. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/version.py +2 -2
  10. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0/cloudnetpy.egg-info}/PKG-INFO +1 -1
  11. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy.egg-info/SOURCES.txt +1 -0
  12. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/LICENSE +0 -0
  13. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/MANIFEST.in +0 -0
  14. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/README.md +0 -0
  15. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/__init__.py +0 -0
  16. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/arm.py +0 -0
  17. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/__init__.py +0 -0
  18. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/atmos_utils.py +0 -0
  19. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/attenuation.py +0 -0
  20. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/attenuations/__init__.py +0 -0
  21. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/attenuations/gas_attenuation.py +0 -0
  22. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/attenuations/liquid_attenuation.py +0 -0
  23. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/attenuations/melting_attenuation.py +0 -0
  24. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/attenuations/rain_attenuation.py +0 -0
  25. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/categorize.py +0 -0
  26. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/classify.py +0 -0
  27. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/containers.py +0 -0
  28. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/disdrometer.py +0 -0
  29. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/droplet.py +0 -0
  30. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/falling.py +0 -0
  31. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/freezing.py +0 -0
  32. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/insects.py +0 -0
  33. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/lidar.py +0 -0
  34. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/melting.py +0 -0
  35. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/model.py +0 -0
  36. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/mwr.py +0 -0
  37. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/categorize/radar.py +0 -0
  38. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/cloudnetarray.py +0 -0
  39. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/concat_lib.py +0 -0
  40. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/constants.py +0 -0
  41. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/datasource.py +0 -0
  42. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/disdronator/__init__.py +0 -0
  43. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/disdronator/lpm.py +0 -0
  44. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/disdronator/parsivel.py +0 -0
  45. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/disdronator/process.py +0 -0
  46. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/disdronator/rd80.py +0 -0
  47. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/disdronator/utils.py +0 -0
  48. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/exceptions.py +0 -0
  49. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/__init__.py +0 -0
  50. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/arm_ceilo.py +0 -0
  51. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/arm_ld.py +0 -0
  52. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/arm_utils.py +0 -0
  53. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/basta.py +0 -0
  54. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/bowtie.py +0 -0
  55. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/ceilo.py +0 -0
  56. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/ceilometer.py +0 -0
  57. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/cl61d.py +0 -0
  58. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
  59. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/copernicus.py +0 -0
  60. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/da10.py +0 -0
  61. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/dealias.py +0 -0
  62. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/disdrometer.py +0 -0
  63. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/fd12p.py +0 -0
  64. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/galileo.py +0 -0
  65. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/hatpro.py +0 -0
  66. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/instruments.py +0 -0
  67. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/kazr.py +0 -0
  68. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/lufft.py +0 -0
  69. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/mira.py +0 -0
  70. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/mmcr.py +0 -0
  71. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/mrr.py +0 -0
  72. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/mwr3c.py +0 -0
  73. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/mwrlos.py +0 -0
  74. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/nc_lidar.py +0 -0
  75. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/nc_radar.py +0 -0
  76. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/pollyxt.py +0 -0
  77. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/radiometrics.py +0 -0
  78. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/rain_e_h3.py +0 -0
  79. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/rpg.py +0 -0
  80. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/rpg_reader.py +0 -0
  81. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/toa5.py +0 -0
  82. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/vaisala.py +0 -0
  83. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/weather_radar.py +0 -0
  84. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/instruments/weather_station.py +0 -0
  85. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/metadata.py +0 -0
  86. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/__init__.py +0 -0
  87. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/file_handler.py +0 -0
  88. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/metadata.py +0 -0
  89. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/model_metadata.py +0 -0
  90. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
  91. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
  92. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
  93. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/plotting/plotting.py +0 -0
  94. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
  95. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/products/advance_methods.py +0 -0
  96. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/products/grid_methods.py +0 -0
  97. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
  98. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/products/observation_products.py +0 -0
  99. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
  100. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/products/tools.py +0 -0
  101. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
  102. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
  103. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
  104. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
  105. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
  106. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
  107. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
  108. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
  109. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
  110. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
  111. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
  112. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
  113. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
  114. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
  115. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
  116. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/conftest.py +0 -0
  117. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -0
  118. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +0 -0
  119. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
  120. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
  121. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
  122. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
  123. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
  124. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
  125. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/model_evaluation/utils.py +0 -0
  126. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/plotting/__init__.py +0 -0
  127. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/classification.py +0 -0
  128. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/der.py +0 -0
  129. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/drizzle.py +0 -0
  130. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/drizzle_error.py +0 -0
  131. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/drizzle_tools.py +0 -0
  132. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/epsilon_lidar.py +0 -0
  133. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/epsilon_radar.py +0 -0
  134. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/ier.py +0 -0
  135. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/iwc.py +0 -0
  136. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/iwc_weather_radar.py +0 -0
  137. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/mie_lu_tables.nc +0 -0
  138. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/mwr_tools.py +0 -0
  139. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/products/product_tools.py +0 -0
  140. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/py.typed +0 -0
  141. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy/utils.py +0 -0
  142. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy.egg-info/dependency_links.txt +0 -0
  143. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy.egg-info/entry_points.txt +0 -0
  144. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy.egg-info/requires.txt +0 -0
  145. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/cloudnetpy.egg-info/top_level.txt +0 -0
  146. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/docs/source/conf.py +0 -0
  147. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/pyproject.toml +0 -0
  148. {cloudnetpy-1.95.7 → cloudnetpy-1.96.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.95.7
3
+ Version: 1.96.0
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -62,9 +62,11 @@ def run(args: argparse.Namespace, tmpdir: str, client: APIClient) -> None:
62
62
  process_categorize = "categorize" in args.products
63
63
  cat_filepath = None
64
64
  if is_arm and cat_products and not process_categorize:
65
- # ARM categorize files are not in the data portal: use a local one
65
+ # Prefer a local file, then the portal; process only if neither exists
66
66
  if not ARM_PRODUCTS & set(args.products):
67
67
  cat_filepath = _find_existing_categorize(args)
68
+ if cat_filepath is None:
69
+ cat_filepath = _fetch_product(args, "categorize", client)
68
70
  process_categorize = cat_filepath is None
69
71
 
70
72
  # Instrument based products
@@ -181,7 +181,7 @@ def get_references(identifier: str | None = None, extra: list | None = None) ->
181
181
  match identifier:
182
182
  case "der":
183
183
  references += (
184
- ", https://doi.org/10.1175/1520-0426(2002)019<0835:TROSCD>2.0.CO;2"
184
+ ", https://doi.org/10.1175/1520-0426(2002)019%3C0835:TROSCD%3E2.0.CO;2"
185
185
  )
186
186
  case "ier":
187
187
  references += (
@@ -195,6 +195,14 @@ def get_references(identifier: str | None = None, extra: list | None = None) ->
195
195
  references += ", https://doi.org/10.1175/JAM2340.1"
196
196
  case "drizzle":
197
197
  references += ", https://doi.org/10.1175/JAM-2181.1"
198
+ case "cod":
199
+ references += (
200
+ ", https://doi.org/10.1175/BAMS-88-6-883"
201
+ ", https://doi.org/10.1175/1520-0426(2002)019%3C0835:TROSCD%3E2.0.CO;2"
202
+ ", https://doi.org/10.1175/JAM2340.1"
203
+ ", https://doi.org/10.1175/JAM2543.1"
204
+ ", https://doi.org/10.5194/amt-13-5335-2020"
205
+ )
198
206
  case "epsilon-radar":
199
207
  references += (
200
208
  ", https://doi.org/10.5194/amt-13-5335-2020"
@@ -472,6 +480,7 @@ def _get_identifier(short_id: str) -> str:
472
480
  "classification",
473
481
  "der",
474
482
  "ier",
483
+ "cod",
475
484
  "classification-voodoo",
476
485
  "epsilon-radar",
477
486
  )
@@ -486,6 +495,8 @@ def _get_identifier(short_id: str) -> str:
486
495
  return "ice effective radius"
487
496
  if short_id == "der":
488
497
  return "droplet effective radius"
498
+ if short_id == "cod":
499
+ return "cloud optical depth"
489
500
  if short_id == "epsilon-radar":
490
501
  return "dissipation rate of turbulent kinetic energy"
491
502
  return short_id
@@ -170,6 +170,15 @@ _CLABEL = {
170
170
  ("Unquantifiable", _COLORS["seaweed_roll"]),
171
171
  ("Undetected", _COLORS["skyblue"]),
172
172
  ),
173
+ "extinction_retrieval_status": (
174
+ ("_No cloud", _COLORS["white"]),
175
+ ("Liquid", _COLORS["lightblue"]),
176
+ ("Liquid, assumed radius", _COLORS["yellow"]),
177
+ ("Ice", _COLORS["lightsteel"]),
178
+ ("Ice, corrected atten.", _COLORS["skyblue"]),
179
+ ("Liquid & ice", _COLORS["darkpurple"]),
180
+ ("No retrieval", _COLORS["seaweed_roll"]),
181
+ ),
173
182
  "dominant_hydrometeor_type": (
174
183
  ("_Clear sky", _COLORS["white"]),
175
184
  ("Ice", _COLORS["lightgray"]),
@@ -684,5 +693,40 @@ ATTRIBUTES = {
684
693
  "dominant_hydrometeor_type": PlotMeta(
685
694
  clabel=_CLABEL["dominant_hydrometeor_type"],
686
695
  ),
696
+ "extinction_liquid": PlotMeta(
697
+ cmap="Blues",
698
+ plot_range=(1e-4, 1e-1),
699
+ log_scale=True,
700
+ ),
701
+ "extinction_ice": PlotMeta(
702
+ plot_range=(1e-5, 1e-2),
703
+ log_scale=True,
704
+ ),
705
+ "extinction_retrieval_status": PlotMeta(
706
+ clabel=_CLABEL["extinction_retrieval_status"],
707
+ ),
708
+ "extinction_liquid_error": PlotMeta(
709
+ cmap="RdYlGn_r",
710
+ plot_range=(0, 5),
711
+ ),
712
+ "extinction_ice_error": PlotMeta(
713
+ cmap="RdYlGn_r",
714
+ plot_range=(0, 5),
715
+ ),
716
+ "optical_depth_error": PlotMeta(
717
+ zero_line=True,
718
+ ),
719
+ "optical_depth": PlotMeta(
720
+ plot_range=(0.02, 1000),
721
+ log_scale=True,
722
+ ),
723
+ "optical_depth_liquid": PlotMeta(
724
+ plot_range=(0.02, 1000),
725
+ log_scale=True,
726
+ ),
727
+ "optical_depth_ice": PlotMeta(
728
+ plot_range=(0.02, 1000),
729
+ log_scale=True,
730
+ ),
687
731
  },
688
732
  }
@@ -904,6 +904,9 @@ class Plot1D(Plot):
904
904
  units = self._convert_units()
905
905
  if self._plot_meta.mask_zeros:
906
906
  self._mask_zeros()
907
+ is_zero = ~ma.getmaskarray(self._data) & (ma.filled(self._data, 1) == 0)
908
+ if self._is_log:
909
+ self._mask_non_positive()
907
910
  self._mark_gaps(figure_data)
908
911
  self._ax.plot(
909
912
  figure_data.time_including_gaps,
@@ -914,14 +917,34 @@ class Plot1D(Plot):
914
917
  )
915
918
  if self._plot_meta.moving_average:
916
919
  self._plot_moving_average(figure_data, hacky_freq_ind)
917
- if self._plot_meta.zero_line:
920
+ if self._plot_meta.zero_line and not self._is_log:
918
921
  self._ax.axhline(0, color="black", alpha=0.5, label="_nolegend_")
919
922
  self._fill_between_data_gaps(figure_data)
920
- self.sub_plot.set_yax(ylabel=units, y_limits=self._get_y_limits())
923
+ if self._is_log:
924
+ self._ax.set_yscale("log")
925
+ y_limits = self._get_y_limits()
926
+ self.sub_plot.set_yax(ylabel=units, y_limits=y_limits)
927
+ if self._is_log and np.any(is_zero):
928
+ self._plot_zeros(figure_data.time[is_zero], y_limits[0])
921
929
  pos = self._ax.get_position()
922
930
  self._ax.set_position((pos.x0, pos.y0, pos.width * 0.965, pos.height))
923
931
  self._plot_flags(figure_data)
924
932
 
933
+ def _plot_zeros(self, time: ndarray, y_min: float) -> None:
934
+ """Marks zero values along the bottom of a logarithmic axis."""
935
+ is_cloud_variable = self.sub_plot.variable.name.startswith("optical_depth")
936
+ self._ax.plot(
937
+ time,
938
+ np.full(len(time), y_min * 1.3),
939
+ color="lightgrey",
940
+ marker=".",
941
+ lw=0,
942
+ markersize=3,
943
+ label="Clear sky" if is_cloud_variable else "Zero",
944
+ zorder=_get_zorder("data"),
945
+ )
946
+ self._ax.legend(markerscale=3, numpoints=1, frameon=False)
947
+
925
948
  def _plot_flags(self, figure_data: FigureData) -> None:
926
949
  if figure_data.is_mwrpy_product():
927
950
  flags = self._read_flagged_data(figure_data)
@@ -1021,9 +1044,18 @@ class Plot1D(Plot):
1021
1044
  frameon=False,
1022
1045
  )
1023
1046
 
1047
+ def _mask_non_positive(self) -> None:
1048
+ self._data = ma.masked_less_equal(self._data, 0)
1049
+ self._data_orig = ma.masked_less_equal(self._data_orig, 0)
1050
+
1024
1051
  def _get_y_limits(self) -> tuple[float, float]:
1025
1052
  percent_gap = 0.05
1026
1053
  fallback = (-percent_gap, percent_gap)
1054
+ if self._is_log:
1055
+ if self._plot_meta.plot_range is not None:
1056
+ return self._plot_meta.plot_range
1057
+ valid = self._data[~ma.getmaskarray(self._data)]
1058
+ return (valid.min() / 2, valid.max() * 2) if valid.size else (0.1, 10)
1027
1059
  if ma.all(self._data.mask):
1028
1060
  return fallback
1029
1061
  min_data = self._data.min()
@@ -1066,6 +1098,9 @@ class Plot1D(Plot):
1066
1098
  ) -> None:
1067
1099
  time = figure_data.time.copy()
1068
1100
  data = self._data_orig.copy()
1101
+ if self._is_log:
1102
+ # Average in log space so large values do not dominate
1103
+ data = ma.log10(ma.masked_less_equal(data, 0))
1069
1104
 
1070
1105
  if figure_data.is_mwrpy_product() or self.sub_plot.variable.name in (
1071
1106
  "tb",
@@ -1097,6 +1132,8 @@ class Plot1D(Plot):
1097
1132
  )
1098
1133
  else:
1099
1134
  sma = self._calculate_moving_average(data1, time1, window=5)
1135
+ if self._is_log:
1136
+ sma = 10**sma
1100
1137
  gap_time = _get_max_gap_in_minutes(figure_data)
1101
1138
  gaps = self._find_time_gap_indices(time1, max_gap_min=gap_time) + 1
1102
1139
 
@@ -1,4 +1,5 @@
1
1
  from .classification import generate_classification
2
+ from .cod import generate_cod
2
3
  from .der import generate_der
3
4
  from .drizzle import generate_drizzle
4
5
  from .epsilon_lidar import generate_epsilon_from_lidar