jerlov 0.5.2__tar.gz → 0.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 (49) hide show
  1. {jerlov-0.5.2/jerlov.egg-info → jerlov-0.7.0}/PKG-INFO +45 -4
  2. {jerlov-0.5.2 → jerlov-0.7.0}/README.md +44 -3
  3. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/__init__.py +11 -1
  4. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/_data.py +4 -0
  5. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/backscattering.py +16 -4
  6. jerlov-0.7.0/jerlov/data/petzold1972_stations.csv +9 -0
  7. jerlov-0.7.0/jerlov/data/petzold1972_vsf.csv +441 -0
  8. jerlov-0.7.0/jerlov/data/williamson2022_aw.csv +502 -0
  9. jerlov-0.7.0/jerlov/petzold.py +167 -0
  10. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/scene.py +11 -3
  11. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/water.py +289 -15
  12. {jerlov-0.5.2 → jerlov-0.7.0/jerlov.egg-info}/PKG-INFO +45 -4
  13. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov.egg-info/SOURCES.txt +5 -0
  14. {jerlov-0.5.2 → jerlov-0.7.0}/pyproject.toml +1 -1
  15. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_api.py +115 -0
  16. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_backscattering.py +12 -0
  17. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_depth.py +62 -0
  18. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_packaging.py +3 -3
  19. jerlov-0.7.0/tests/test_petzold.py +144 -0
  20. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_quoted_figures.py +30 -0
  21. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_scene.py +9 -0
  22. {jerlov-0.5.2 → jerlov-0.7.0}/LICENSE +0 -0
  23. {jerlov-0.5.2 → jerlov-0.7.0}/NOTICE +0 -0
  24. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/colour.py +0 -0
  25. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/__init__.py +0 -0
  26. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/austin1986_kd.csv +0 -0
  27. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/austin1986_model.csv +0 -0
  28. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/boss2001_chi.csv +0 -0
  29. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/cie1931_2deg_cmf.csv +0 -0
  30. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/cie_d65.csv +0 -0
  31. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/jerlov1968_kd.csv +0 -0
  32. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/jerlov1968_total_irradiance.csv +0 -0
  33. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/jerlov1976_kd.csv +0 -0
  34. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/paulson1977_shortwave.csv +0 -0
  35. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/smart2007_b_from_c.csv +0 -0
  36. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/solonenko2015_iop.csv +0 -0
  37. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/williamson2022_iop.csv +0 -0
  38. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/williamson2022_measured.csv +0 -0
  39. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/data/williamson2023_depth.csv +0 -0
  40. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/py.typed +0 -0
  41. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/shortwave.py +0 -0
  42. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov/sources.py +0 -0
  43. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov.egg-info/dependency_links.txt +0 -0
  44. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov.egg-info/requires.txt +0 -0
  45. {jerlov-0.5.2 → jerlov-0.7.0}/jerlov.egg-info/top_level.txt +0 -0
  46. {jerlov-0.5.2 → jerlov-0.7.0}/setup.cfg +0 -0
  47. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_colour.py +0 -0
  48. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_reproduces_papers.py +0 -0
  49. {jerlov-0.5.2 → jerlov-0.7.0}/tests/test_shortwave.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jerlov
3
- Version: 0.5.2
3
+ Version: 0.7.0
4
4
  Summary: Inherent optical properties of Jerlov water types, with provenance
5
5
  Author: T. Ishibashi
6
6
  License-Expression: Apache-2.0
@@ -228,6 +228,28 @@ warns: its quoted spread is **34.8 percent** against 2.6-6.4 from 90 to 160.
228
228
  This gives the bb of the water your instrument was in. It still gives no bb
229
229
  for a Jerlov water type, because nothing does.
230
230
 
231
+ ## Measured scattering functions
232
+
233
+ Without an instrument, the next best thing to a guess is a water that was
234
+ measured. Petzold (1972) measured the volume scattering function from 0.1 to
235
+ 180 degrees in eight ocean waters, from the clear Tongue of the Ocean to San
236
+ Diego Harbor:
237
+
238
+ ```python
239
+ jerlov.PETZOLD_STATIONS # clearest first: 'AUTEC 8', ..., 'NUC 2040'
240
+ f = jerlov.petzold_scattering("HAOCE 11")
241
+ f.c, f.b, f.backscatter_ratio # 0.398, 0.21933, 0.013 (1/m, 1/m, bb/b)
242
+ f.vsf_at(120.0) # 1/(m sr), at 530 nm
243
+ f.phase_function() # vsf / b, at f.angles_deg
244
+
245
+ w.bb(532, backscatter_ratio=f.backscatter_ratio) # bb/b of a measured water
246
+ ```
247
+
248
+ The ratio spans **0.013 to 0.044** across the eight. These are eight waters
249
+ on eight days in 1971, at 530 nm, water and particles together; none of them
250
+ is a Jerlov type. Every value was transcribed from the report and checked
251
+ against its own integrals and its second printing; DATA.md section 21.
252
+
231
253
  ## The type changes with depth
232
254
 
233
255
  The classification is defined on the top 10 m, but clarity does not stay put.
@@ -244,6 +266,18 @@ jerlov.water_type_at_depth("9C", 15.0) # None: the paper declined to say
244
266
  nothing is asserted. This is a lookup, not a correction applied on your
245
267
  behalf.
246
268
 
269
+ To carry the downwelling irradiance down through that profile, with each 10 m
270
+ layer attenuating by the Kd of its own type:
271
+
272
+ ```python
273
+ d = jerlov.descend("1C", 45.0, wl) # source="jerlov1976" by default
274
+ d.layers # ((0, 10, '1C'), (10, 20, '1C'), (20, 30, 'III'), ..., (40, 45, 'II'))
275
+ scene = jerlov.Scene(jerlov.water("II"), surface * d.transmittance, wl, depth_m=45.0)
276
+ ```
277
+
278
+ It refuses to go through a layer the paper declared nothing for, rather than
279
+ carrying the last type on: 3C stops at 70 m, 9C at 10 m.
280
+
247
281
  ## Other entry points
248
282
 
249
283
  ```python
@@ -257,8 +291,15 @@ jerlov.pure_water_scattering(555) # Morel's bw, if that is what c was correcte
257
291
  # The measured points behind the williamson2022 spectra, with their spread.
258
292
  m = jerlov.measured_points("III", "a") # m.wavelengths, m.values, m.std_dev
259
293
 
260
- # Use your own measurements; they take exactly the same path.
261
- jerlov.Water.from_measurements(wavelengths, a=..., b=...)
294
+ # The pure water absorption williamson2022 built its a on (300-800 nm).
295
+ jerlov.pure_water_absorption(440) # 0.0104; other sources say why they have none
296
+
297
+ # Use your own measurements; they take exactly the same path, flags included.
298
+ w = jerlov.Water.from_measurements(
299
+ wavelengths, a=..., b=...,
300
+ flags={"a": statuses}, # "ok", "suspect", ...: as in DATA.md; flagged ones warn
301
+ uncertainty={"a": sigma_a}, # 1/m; w.uncertainty("a", 532)
302
+ )
262
303
  ```
263
304
 
264
305
  ## Solar heating of the upper ocean
@@ -302,7 +343,7 @@ made.
302
343
  ## Provenance and design
303
344
 
304
345
  `DATA.md` records, for every shipped table, where it came from, what was
305
- verified, and what is known to be wrong with it. Nineteen entries are
346
+ verified, and what is known to be wrong with it. Twenty-one entries are
306
347
  documented there: eight confirmed defects in the source literature, three
307
348
  questions the first edition of Jerlov settled, and the rest notes.
308
349
 
@@ -203,6 +203,28 @@ warns: its quoted spread is **34.8 percent** against 2.6-6.4 from 90 to 160.
203
203
  This gives the bb of the water your instrument was in. It still gives no bb
204
204
  for a Jerlov water type, because nothing does.
205
205
 
206
+ ## Measured scattering functions
207
+
208
+ Without an instrument, the next best thing to a guess is a water that was
209
+ measured. Petzold (1972) measured the volume scattering function from 0.1 to
210
+ 180 degrees in eight ocean waters, from the clear Tongue of the Ocean to San
211
+ Diego Harbor:
212
+
213
+ ```python
214
+ jerlov.PETZOLD_STATIONS # clearest first: 'AUTEC 8', ..., 'NUC 2040'
215
+ f = jerlov.petzold_scattering("HAOCE 11")
216
+ f.c, f.b, f.backscatter_ratio # 0.398, 0.21933, 0.013 (1/m, 1/m, bb/b)
217
+ f.vsf_at(120.0) # 1/(m sr), at 530 nm
218
+ f.phase_function() # vsf / b, at f.angles_deg
219
+
220
+ w.bb(532, backscatter_ratio=f.backscatter_ratio) # bb/b of a measured water
221
+ ```
222
+
223
+ The ratio spans **0.013 to 0.044** across the eight. These are eight waters
224
+ on eight days in 1971, at 530 nm, water and particles together; none of them
225
+ is a Jerlov type. Every value was transcribed from the report and checked
226
+ against its own integrals and its second printing; DATA.md section 21.
227
+
206
228
  ## The type changes with depth
207
229
 
208
230
  The classification is defined on the top 10 m, but clarity does not stay put.
@@ -219,6 +241,18 @@ jerlov.water_type_at_depth("9C", 15.0) # None: the paper declined to say
219
241
  nothing is asserted. This is a lookup, not a correction applied on your
220
242
  behalf.
221
243
 
244
+ To carry the downwelling irradiance down through that profile, with each 10 m
245
+ layer attenuating by the Kd of its own type:
246
+
247
+ ```python
248
+ d = jerlov.descend("1C", 45.0, wl) # source="jerlov1976" by default
249
+ d.layers # ((0, 10, '1C'), (10, 20, '1C'), (20, 30, 'III'), ..., (40, 45, 'II'))
250
+ scene = jerlov.Scene(jerlov.water("II"), surface * d.transmittance, wl, depth_m=45.0)
251
+ ```
252
+
253
+ It refuses to go through a layer the paper declared nothing for, rather than
254
+ carrying the last type on: 3C stops at 70 m, 9C at 10 m.
255
+
222
256
  ## Other entry points
223
257
 
224
258
  ```python
@@ -232,8 +266,15 @@ jerlov.pure_water_scattering(555) # Morel's bw, if that is what c was correcte
232
266
  # The measured points behind the williamson2022 spectra, with their spread.
233
267
  m = jerlov.measured_points("III", "a") # m.wavelengths, m.values, m.std_dev
234
268
 
235
- # Use your own measurements; they take exactly the same path.
236
- jerlov.Water.from_measurements(wavelengths, a=..., b=...)
269
+ # The pure water absorption williamson2022 built its a on (300-800 nm).
270
+ jerlov.pure_water_absorption(440) # 0.0104; other sources say why they have none
271
+
272
+ # Use your own measurements; they take exactly the same path, flags included.
273
+ w = jerlov.Water.from_measurements(
274
+ wavelengths, a=..., b=...,
275
+ flags={"a": statuses}, # "ok", "suspect", ...: as in DATA.md; flagged ones warn
276
+ uncertainty={"a": sigma_a}, # 1/m; w.uncertainty("a", 532)
277
+ )
237
278
  ```
238
279
 
239
280
  ## Solar heating of the upper ocean
@@ -277,7 +318,7 @@ made.
277
318
  ## Provenance and design
278
319
 
279
320
  `DATA.md` records, for every shipped table, where it came from, what was
280
- verified, and what is known to be wrong with it. Nineteen entries are
321
+ verified, and what is known to be wrong with it. Twenty-one entries are
281
322
  documented there: eight confirmed defects in the source literature, three
282
323
  questions the first edition of Jerlov settled, and the rest notes.
283
324
 
@@ -23,6 +23,7 @@ from .colour import (
23
23
  spectrum_to_xyz,
24
24
  xyz_to_srgb,
25
25
  )
26
+ from .petzold import PETZOLD_STATIONS, ScatteringFunction, petzold_scattering
26
27
  from .scene import (
27
28
  AttenuationCoefficients,
28
29
  Observation,
@@ -37,13 +38,16 @@ from .shortwave import (
37
38
  )
38
39
  from .sources import SOURCES, Source, get_source
39
40
  from .water import (
41
+ Descent,
40
42
  MeasuredPoints,
41
43
  MissingQuantityError,
42
44
  ProvenanceWarning,
43
45
  Water,
44
46
  b_from_c,
47
+ descend,
45
48
  kd_spectrum,
46
49
  measured_points,
50
+ pure_water_absorption,
47
51
  water,
48
52
  water_type_at_depth,
49
53
  )
@@ -67,6 +71,12 @@ __all__ = [
67
71
  "Water",
68
72
  "water",
69
73
  "water_type_at_depth",
74
+ "descend",
75
+ "Descent",
76
+ "pure_water_absorption",
77
+ "petzold_scattering",
78
+ "PETZOLD_STATIONS",
79
+ "ScatteringFunction",
70
80
  "shortwave_parameters",
71
81
  "solar_fraction",
72
82
  "jerlov1968_solar_fraction",
@@ -86,4 +96,4 @@ __all__ = [
86
96
  "MissingQuantityError",
87
97
  ]
88
98
 
89
- __version__ = "0.5.2"
99
+ __version__ = "0.7.0"
@@ -59,6 +59,10 @@ QUESTIONABLE = frozenset({"suspect", "missing", "extrapolated",
59
59
  "model_extrapolation", "reconstructed",
60
60
  "extrapolated_by_dataset"})
61
61
 
62
+ #: Every status a spectrum can carry: the table in DATA.md, "The status
63
+ #: column". A caller's own flags must be one of these.
64
+ STATUSES = QUESTIONABLE | {"ok", "interpolated"}
65
+
62
66
 
63
67
  def _read(name: str) -> list[dict[str, str]]:
64
68
  path = resources.files("jerlov.data").joinpath(name)
@@ -57,6 +57,20 @@ class AngleWarning(UserWarning):
57
57
  """
58
58
 
59
59
 
60
+ def _amplitude(wavelength_nm, salinity_psu) -> np.ndarray:
61
+ """Morel's A(lambda, S), Boss & Pegau Eq. (4), in 1/(m sr)."""
62
+ salinity = np.asarray(salinity_psu, dtype=float)
63
+ if not np.all(np.isfinite(salinity)) or np.any(salinity < 0):
64
+ # A negative salinity made pure water scatter less than fresh water,
65
+ # without complaint.
66
+ raise ValueError(
67
+ f"salinity_psu must be a finite number, 0 or more, not "
68
+ f"{salinity_psu!r}"
69
+ )
70
+ return (1.38 * (np.asarray(wavelength_nm, dtype=float) / 500.0) ** -4.32
71
+ * (1 + 0.3 * salinity / 37.0) * 1e-4)
72
+
73
+
60
74
  def _ratio() -> float:
61
75
  return (1 - DEPOLARISATION_RATIO) / (1 + DEPOLARISATION_RATIO)
62
76
 
@@ -73,8 +87,7 @@ def pure_water_vsf(angle_deg, wavelength_nm, salinity_psu: float = 37.0):
73
87
  as the agreement between measurement and theory.
74
88
  """
75
89
  angle = np.asarray(angle_deg, dtype=float)
76
- amplitude = (1.38 * (np.asarray(wavelength_nm, dtype=float) / 500.0) ** -4.32
77
- * (1 + 0.3 * salinity_psu / 37.0) * 1e-4)
90
+ amplitude = _amplitude(wavelength_nm, salinity_psu)
78
91
  out = amplitude * (1 + _ratio() * np.cos(np.radians(angle)) ** 2)
79
92
  # Angle and wavelength broadcast against each other; the answer is a
80
93
  # float only when neither of them was an array.
@@ -90,8 +103,7 @@ def pure_water_backscattering(wavelength_nm, salinity_psu: float = 37.0):
90
103
  hemisphere, which for this angular shape is analytic.
91
104
  """
92
105
  r = _ratio()
93
- amplitude = (1.38 * (np.asarray(wavelength_nm, dtype=float) / 500.0) ** -4.32
94
- * (1 + 0.3 * salinity_psu / 37.0) * 1e-4)
106
+ amplitude = _amplitude(wavelength_nm, salinity_psu)
95
107
  # integral of (1 + r cos^2) sin over 90..180 degrees is 1 + r/3
96
108
  out = 2.0 * math.pi * amplitude * (1 + r / 3.0)
97
109
  return _like_input(np.atleast_1d(out), wavelength_nm)
@@ -0,0 +1,9 @@
1
+ station,locale,date,wavelength_nm,c_per_m,b_per_m,a_per_m,bb_over_b,slope_below_0.1deg,median_angle_deg,report_figure
2
+ AUTEC 7,"Tongue of the Ocean, Bahamas",1971-07-09,530,0.199,0.117,0.082,0.025,-1.599,2.815,23
3
+ AUTEC 8,"Tongue of the Ocean, Bahamas",1971-07-13,530,0.151,0.037,0.114,0.044,-1.191,6.252,25
4
+ AUTEC 9,"Tongue of the Ocean, Bahamas",1971-07-14,530,0.165,0.043,0.122,0.038,-1.248,5.203,27
5
+ HAOCE 5,"Avalon Cove, Catalina Island",1971-07-31,530,0.470,0.275,0.195,0.014,-1.546,2.152,29
6
+ HAOCE 11,"Catalina Channel, offshore southern California",1971-08-05,530,0.398,0.219,0.179,0.013,-1.545,2.534,31
7
+ NUC 2200,San Diego Harbor,1971-10-04,530,1.920,1.583,0.337,0.019,-1.249,4.497,33
8
+ NUC 2040,San Diego Harbor,1971-10-05,530,2.190,1.824,0.366,0.020,-1.346,4.680,35
9
+ NUC 2240,San Diego Harbor,1971-10-05,530,1.330,1.205,0.125,0.018,-1.442,4.682,37