jerlov 0.2.0__tar.gz → 0.2.2__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.
- {jerlov-0.2.0/jerlov.egg-info → jerlov-0.2.2}/PKG-INFO +12 -11
- {jerlov-0.2.0 → jerlov-0.2.2}/README.md +11 -10
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/__init__.py +1 -1
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/colour.py +10 -7
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/water.py +11 -4
- {jerlov-0.2.0 → jerlov-0.2.2/jerlov.egg-info}/PKG-INFO +12 -11
- {jerlov-0.2.0 → jerlov-0.2.2}/pyproject.toml +1 -1
- {jerlov-0.2.0 → jerlov-0.2.2}/tests/test_api.py +27 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/tests/test_colour.py +8 -3
- {jerlov-0.2.0 → jerlov-0.2.2}/tests/test_packaging.py +64 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/tests/test_reproduces_papers.py +72 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/LICENSE +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/NOTICE +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/_data.py +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/__init__.py +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/austin1986_kd.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/austin1986_model.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/cie1931_2deg_cmf.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/cie_d65.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/jerlov1968_kd.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/jerlov1968_total_irradiance.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/jerlov1976_kd.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/paulson1977_shortwave.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/smart2007_b_from_c.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/solonenko2015_iop.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/williamson2022_iop.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/williamson2022_measured.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/data/williamson2023_depth.csv +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/scene.py +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/shortwave.py +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov/sources.py +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov.egg-info/SOURCES.txt +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov.egg-info/dependency_links.txt +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov.egg-info/requires.txt +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/jerlov.egg-info/top_level.txt +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/setup.cfg +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/tests/test_depth.py +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/tests/test_scene.py +0 -0
- {jerlov-0.2.0 → jerlov-0.2.2}/tests/test_shortwave.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jerlov
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Inherent optical properties of Jerlov water types, with provenance
|
|
5
5
|
Author: T. Ishibashi
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -257,22 +257,23 @@ rather than substituting a neighbour.
|
|
|
257
257
|
## Examples
|
|
258
258
|
|
|
259
259
|
```
|
|
260
|
-
python examples/sources_disagree.py
|
|
261
|
-
python examples/
|
|
260
|
+
python examples/sources_disagree.py why the source is an argument
|
|
261
|
+
python examples/from_one_measurement.py from an instrument reading
|
|
262
|
+
python examples/synthetic_underwater_images.py appearance at range and depth
|
|
263
|
+
python examples/solar_heating.py for ocean circulation models
|
|
264
|
+
python examples/what_an_eye_sees.py any spectral sensitivity
|
|
262
265
|
```
|
|
263
266
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
Both are run by CI, so an example that has stopped working is a failed build.
|
|
267
|
+
Five scripts, each aimed at a different reader; `examples/README.md` says
|
|
268
|
+
which to start with. CI runs all of them on every push, so an example that has
|
|
269
|
+
stopped working is a failed build. Every one ends with the assumptions it
|
|
270
|
+
made.
|
|
270
271
|
|
|
271
272
|
## Provenance and design
|
|
272
273
|
|
|
273
274
|
`DATA.md` records, for every shipped table, where it came from, what was
|
|
274
|
-
verified, and what is known to be wrong with it.
|
|
275
|
-
documented there:
|
|
275
|
+
verified, and what is known to be wrong with it. Seventeen entries are
|
|
276
|
+
documented there: eight confirmed defects in the source literature, three
|
|
276
277
|
questions the first edition of Jerlov settled, and the rest notes.
|
|
277
278
|
|
|
278
279
|
`DECISIONS.md` records why the package is shaped the way it is, including the
|
|
@@ -232,22 +232,23 @@ rather than substituting a neighbour.
|
|
|
232
232
|
## Examples
|
|
233
233
|
|
|
234
234
|
```
|
|
235
|
-
python examples/sources_disagree.py
|
|
236
|
-
python examples/
|
|
235
|
+
python examples/sources_disagree.py why the source is an argument
|
|
236
|
+
python examples/from_one_measurement.py from an instrument reading
|
|
237
|
+
python examples/synthetic_underwater_images.py appearance at range and depth
|
|
238
|
+
python examples/solar_heating.py for ocean circulation models
|
|
239
|
+
python examples/what_an_eye_sees.py any spectral sensitivity
|
|
237
240
|
```
|
|
238
241
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
Both are run by CI, so an example that has stopped working is a failed build.
|
|
242
|
+
Five scripts, each aimed at a different reader; `examples/README.md` says
|
|
243
|
+
which to start with. CI runs all of them on every push, so an example that has
|
|
244
|
+
stopped working is a failed build. Every one ends with the assumptions it
|
|
245
|
+
made.
|
|
245
246
|
|
|
246
247
|
## Provenance and design
|
|
247
248
|
|
|
248
249
|
`DATA.md` records, for every shipped table, where it came from, what was
|
|
249
|
-
verified, and what is known to be wrong with it.
|
|
250
|
-
documented there:
|
|
250
|
+
verified, and what is known to be wrong with it. Seventeen entries are
|
|
251
|
+
documented there: eight confirmed defects in the source literature, three
|
|
251
252
|
questions the first edition of Jerlov settled, and the rest notes.
|
|
252
253
|
|
|
253
254
|
`DECISIONS.md` records why the package is shaped the way it is, including the
|
|
@@ -151,15 +151,18 @@ def integrate_response(spectrum, wavelengths, response, response_wavelengths,
|
|
|
151
151
|
if wavelengths.size < 2:
|
|
152
152
|
raise ValueError("at least two wavelengths are needed to integrate")
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
)
|
|
154
|
+
covered = [_coverage(wavelengths, response_wavelengths, response[:, k])
|
|
155
|
+
for k in range(response.shape[1])]
|
|
156
|
+
worst = min(covered)
|
|
158
157
|
if worst < 0.999:
|
|
158
|
+
which = (f"channel {covered.index(worst)} of {len(covered)}"
|
|
159
|
+
if len(covered) > 1 else "it")
|
|
159
160
|
warnings.warn(
|
|
160
|
-
f"the spectrum spans {wavelengths[0]:g}-{wavelengths[-1]:g} nm
|
|
161
|
-
f"
|
|
162
|
-
"
|
|
161
|
+
f"the spectrum spans {wavelengths[0]:g}-{wavelengths[-1]:g} nm; "
|
|
162
|
+
f"of the {name}, {which} is only {worst:.1%} covered "
|
|
163
|
+
f"(all channels: {', '.join(f'{c:.1%}' for c in covered)}). "
|
|
164
|
+
"The integral is over the overlap and is biased by what was left "
|
|
165
|
+
"out",
|
|
163
166
|
CoverageWarning,
|
|
164
167
|
stacklevel=2,
|
|
165
168
|
)
|
|
@@ -131,11 +131,18 @@ class Water:
|
|
|
131
131
|
values = self._series[quantity]
|
|
132
132
|
out = np.interp(query, self.wavelengths, values)
|
|
133
133
|
# np.interp happily bridges a NaN-free path around a NaN, so check the
|
|
134
|
-
#
|
|
134
|
+
# samples the answer actually rests on. A query that lands exactly on
|
|
135
|
+
# a sample rests on that sample alone: it is not interpolated, so a
|
|
136
|
+
# missing neighbour must not poison it.
|
|
135
137
|
idx = np.searchsorted(self.wavelengths, query)
|
|
136
|
-
for k, i in enumerate(idx):
|
|
137
|
-
|
|
138
|
-
if
|
|
138
|
+
for k, (i, w_query) in enumerate(zip(idx, query)):
|
|
139
|
+
exact = i < self.wavelengths.size and self.wavelengths[i] == w_query
|
|
140
|
+
if exact:
|
|
141
|
+
if np.isnan(values[i]):
|
|
142
|
+
out[k] = np.nan
|
|
143
|
+
continue
|
|
144
|
+
left, right = max(i - 1, 0), min(i, values.size - 1)
|
|
145
|
+
if np.any(np.isnan(values[left:right + 1])):
|
|
139
146
|
out[k] = np.nan
|
|
140
147
|
return _like_input(out, wl)
|
|
141
148
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jerlov
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Inherent optical properties of Jerlov water types, with provenance
|
|
5
5
|
Author: T. Ishibashi
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -257,22 +257,23 @@ rather than substituting a neighbour.
|
|
|
257
257
|
## Examples
|
|
258
258
|
|
|
259
259
|
```
|
|
260
|
-
python examples/sources_disagree.py
|
|
261
|
-
python examples/
|
|
260
|
+
python examples/sources_disagree.py why the source is an argument
|
|
261
|
+
python examples/from_one_measurement.py from an instrument reading
|
|
262
|
+
python examples/synthetic_underwater_images.py appearance at range and depth
|
|
263
|
+
python examples/solar_heating.py for ocean circulation models
|
|
264
|
+
python examples/what_an_eye_sees.py any spectral sensitivity
|
|
262
265
|
```
|
|
263
266
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
Both are run by CI, so an example that has stopped working is a failed build.
|
|
267
|
+
Five scripts, each aimed at a different reader; `examples/README.md` says
|
|
268
|
+
which to start with. CI runs all of them on every push, so an example that has
|
|
269
|
+
stopped working is a failed build. Every one ends with the assumptions it
|
|
270
|
+
made.
|
|
270
271
|
|
|
271
272
|
## Provenance and design
|
|
272
273
|
|
|
273
274
|
`DATA.md` records, for every shipped table, where it came from, what was
|
|
274
|
-
verified, and what is known to be wrong with it.
|
|
275
|
-
documented there:
|
|
275
|
+
verified, and what is known to be wrong with it. Seventeen entries are
|
|
276
|
+
documented there: eight confirmed defects in the source literature, three
|
|
276
277
|
questions the first edition of Jerlov settled, and the rest notes.
|
|
277
278
|
|
|
278
279
|
`DECISIONS.md` records why the package is shaped the way it is, including the
|
|
@@ -186,3 +186,30 @@ def test_a_missing_value_is_nan_whether_scalar_or_array():
|
|
|
186
186
|
warnings.simplefilter("ignore", ProvenanceWarning)
|
|
187
187
|
assert np.isnan(w.a(675))
|
|
188
188
|
assert np.isnan(w.a([675.0])).all()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# -- landing exactly on a sample -----------------------------------------
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_an_exact_hit_survives_a_missing_neighbour():
|
|
195
|
+
"""A query on a sample is not interpolated, so a gap beside it is not its
|
|
196
|
+
problem. Found by checking the shipped 1976 file against the printed
|
|
197
|
+
table: Jerlov 7C has no data below 350 nm, and kd(350) was returning nan
|
|
198
|
+
although 350 nm itself is tabulated.
|
|
199
|
+
"""
|
|
200
|
+
w = jerlov.water("7C", source="jerlov1976")
|
|
201
|
+
with warnings.catch_warnings():
|
|
202
|
+
warnings.simplefilter("ignore", ProvenanceWarning)
|
|
203
|
+
assert w.kd(350.0) == pytest.approx(3.0)
|
|
204
|
+
assert np.isnan(w.kd(349.0)) # inside the gap, still nan
|
|
205
|
+
assert np.isnan(w.kd(348.0))
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_interpolating_across_a_gap_still_gives_nan():
|
|
209
|
+
"""The guard this rests on must not have been loosened."""
|
|
210
|
+
w = jerlov.water("5C", source="solonenko2015")
|
|
211
|
+
with warnings.catch_warnings():
|
|
212
|
+
warnings.simplefilter("ignore", ProvenanceWarning)
|
|
213
|
+
assert np.isnan(w.a(650)) # a published value that is wrong
|
|
214
|
+
assert np.isnan(w.a(660)) # and interpolation across it
|
|
215
|
+
assert np.isnan(w.a(675))
|
|
@@ -73,7 +73,7 @@ def test_each_primary_lights_only_its_own_channel():
|
|
|
73
73
|
def test_a_narrow_spectrum_warns():
|
|
74
74
|
"""450-650 nm looks like a full spectrum but is not."""
|
|
75
75
|
wl = np.arange(450.0, 651.0, 1.0)
|
|
76
|
-
with pytest.warns(CoverageWarning, match="
|
|
76
|
+
with pytest.warns(CoverageWarning, match="covered"):
|
|
77
77
|
spectrum_to_xyz(np.ones_like(wl), wl)
|
|
78
78
|
|
|
79
79
|
|
|
@@ -88,8 +88,13 @@ def test_the_warning_states_what_was_missed():
|
|
|
88
88
|
with pytest.warns(CoverageWarning) as caught:
|
|
89
89
|
spectrum_to_xyz(np.ones_like(wl), wl)
|
|
90
90
|
message = str(caught[0].message)
|
|
91
|
-
assert "500-600 nm" in message
|
|
92
|
-
assert "CIE 1931" in message
|
|
91
|
+
assert "500-600 nm" in message # the span it actually had
|
|
92
|
+
assert "CIE 1931" in message # what it was integrated against
|
|
93
|
+
assert "all channels:" in message # not just the worst one
|
|
94
|
+
# z-bar peaks at 445 nm, so 500-600 nm barely touches it. Naming the
|
|
95
|
+
# channel matters: "4.1% covered" alone reads as if the whole observer
|
|
96
|
+
# were 4.1% covered, which would be wrong.
|
|
97
|
+
assert "channel 2 of 3" in message
|
|
93
98
|
|
|
94
99
|
|
|
95
100
|
# -- white, the other one ------------------------------------------------
|
|
@@ -76,3 +76,67 @@ def test_every_shipped_table_has_a_build_script_or_is_explained():
|
|
|
76
76
|
scripts = " ".join(p.read_text() for p in (ROOT / "tools").glob("*.py"))
|
|
77
77
|
for csv in sorted((ROOT / "jerlov" / "data").glob("*.csv")):
|
|
78
78
|
assert csv.name in scripts, f"no build script writes {csv.name}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# -- the documentation counts itself -------------------------------------
|
|
82
|
+
|
|
83
|
+
_SPELLED = ["zero", "one", "two", "three", "four", "five", "six", "seven",
|
|
84
|
+
"eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
|
|
85
|
+
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen",
|
|
86
|
+
"twenty", "twenty-one", "twenty-two", "twenty-three",
|
|
87
|
+
"twenty-four", "twenty-five"]
|
|
88
|
+
WORDS = {word: value for value, word in enumerate(_SPELLED)}
|
|
89
|
+
WORDS.update({word.capitalize(): value
|
|
90
|
+
for value, word in enumerate(_SPELLED)})
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _numbered_sections(text: str) -> int:
|
|
94
|
+
return len(re.findall(r"^## \d+\. ", text, re.MULTILINE))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _confirmed_sections(text: str) -> int:
|
|
98
|
+
return len(re.findall(r"^## \d+\..*\(confirmed", text, re.MULTILINE))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@source_tree
|
|
102
|
+
def test_DATA_md_counts_its_own_sections():
|
|
103
|
+
"""The opening summary is written by hand and has drifted three times."""
|
|
104
|
+
text = (ROOT / "DATA.md").read_text()
|
|
105
|
+
match = re.search(r"^(\w+) entries are recorded below\. (\w+) are confirmed",
|
|
106
|
+
text, re.MULTILINE)
|
|
107
|
+
assert match, "DATA.md no longer opens with a countable summary"
|
|
108
|
+
claimed_total, claimed_confirmed = (WORDS[g] for g in match.groups())
|
|
109
|
+
assert claimed_total == _numbered_sections(text)
|
|
110
|
+
assert claimed_confirmed == _confirmed_sections(text)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@source_tree
|
|
114
|
+
def test_the_zenodo_record_will_carry_what_it_should():
|
|
115
|
+
"""`.zenodo.json` is only read at archive time, so nothing else checks it."""
|
|
116
|
+
import json
|
|
117
|
+
|
|
118
|
+
record = json.loads((ROOT / ".zenodo.json").read_text())
|
|
119
|
+
assert record["upload_type"] == "software"
|
|
120
|
+
assert record["license"] == "Apache-2.0"
|
|
121
|
+
assert record["creators"], "a record with no author is not citable"
|
|
122
|
+
for creator in record["creators"]:
|
|
123
|
+
assert creator.get("orcid"), (
|
|
124
|
+
f"{creator['name']} has no ORCID, so the record will not attach "
|
|
125
|
+
"to their publication list"
|
|
126
|
+
)
|
|
127
|
+
# Every source the package ships data from should be reachable from the
|
|
128
|
+
# record, not only from DATA.md.
|
|
129
|
+
derived = [r["identifier"] for r in record["related_identifiers"]
|
|
130
|
+
if r["relation"] == "isDerivedFrom"]
|
|
131
|
+
assert len(derived) >= 8, derived
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@source_tree
|
|
135
|
+
def test_the_README_agrees_with_DATA_md_on_the_count():
|
|
136
|
+
data = (ROOT / "DATA.md").read_text()
|
|
137
|
+
readme = (ROOT / "README.md").read_text()
|
|
138
|
+
match = re.search(r"(\w+) entries are\s+documented there: (\w+) confirmed",
|
|
139
|
+
readme)
|
|
140
|
+
assert match, "the README no longer states the count"
|
|
141
|
+
assert WORDS[match.group(1)] == _numbered_sections(data)
|
|
142
|
+
assert WORDS[match.group(2)] == _confirmed_sections(data)
|
|
@@ -221,3 +221,75 @@ def test_jerlov_type_I_falls_below_pure_sea_water():
|
|
|
221
221
|
if j < float(np.interp(nm, wl_m, kw)):
|
|
222
222
|
below += 1
|
|
223
223
|
assert below == 9, f"{below} of {len(checked)} wavelengths below Kw"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# -- checked against independent transcriptions ---------------------------
|
|
227
|
+
|
|
228
|
+
#: Jerlov (1976) Table XXVII, Kd x 100 in 1/m, as reprinted by Paglierani et
|
|
229
|
+
#: al. (2023) Table 10 and by Wozniak & Pelevin (1991) Table 1. The two agree
|
|
230
|
+
#: with each other and with the original scan except at IB 700 nm, where
|
|
231
|
+
#: Wozniak & Pelevin print 59; see DATA.md section 16.
|
|
232
|
+
JERLOV1976_TABLE_XXVII = {
|
|
233
|
+
"I": [15, 6.2, 3.8, 2.8, 2.2, 1.9, 1.8, 2.7, 4.3, 6.3, 8.9, 23.5, 30.5,
|
|
234
|
+
36, 42, 56],
|
|
235
|
+
"IA": [18, 7.8, 5.2, 3.8, 3.1, 2.6, 2.5, 3.2, 4.8, 6.7, 9.4, 24, 31, 37,
|
|
236
|
+
43, 57],
|
|
237
|
+
"IB": [22, 10, 6.6, 5.1, 4.2, 3.6, 3.3, 4.2, 5.4, 7.2, 9.9, 24.5, 31.5,
|
|
238
|
+
37.5, 43.5, 58],
|
|
239
|
+
"II": [37, 17.5, 12.2, 9.6, 8.1, 6.8, 6.2, 7.0, 7.6, 8.9, 11.5, 26, 33.5,
|
|
240
|
+
40, 46.5, 61],
|
|
241
|
+
"III": [65, 32, 22, 18.5, 16, 13.5, 11.6, 11.5, 11.6, 12.0, 14.8, 29.5,
|
|
242
|
+
37.5, 44.5, 52, 66],
|
|
243
|
+
"1C": [180, 120, 80, 51, 36, 25, 17, 14, 13, 12, 15, 30, 37, 45, 51, 65],
|
|
244
|
+
"3C": [240, 170, 110, 78, 54, 39, 29, 22, 20, 19, 21, 33, 40, 46, 56, 71],
|
|
245
|
+
"5C": [350, 230, 160, 110, 78, 56, 43, 36, 31, 30, 33, 40, 48, 54, 65, 80],
|
|
246
|
+
"7C": [None, 300, 210, 160, 120, 89, 71, 58, 49, 46, 46, 48, 54, 63, 78,
|
|
247
|
+
92],
|
|
248
|
+
"9C": [None, 390, 300, 240, 190, 160, 123, 99, 78, 63, 58, 60, 65, 76, 92,
|
|
249
|
+
110],
|
|
250
|
+
}
|
|
251
|
+
TABLE_XXVII_WAVELENGTHS = [310, 350, 375, 400, 425, 450, 475, 500, 525, 550,
|
|
252
|
+
575, 600, 625, 650, 675, 700]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_the_shipped_1976_file_matches_the_printed_table():
|
|
256
|
+
"""The Dstl file reaches us second-hand; the printed table does not."""
|
|
257
|
+
w = {t: jerlov.water(t, source="jerlov1976")
|
|
258
|
+
for t in JERLOV1976_TABLE_XXVII}
|
|
259
|
+
checked = 0
|
|
260
|
+
for water_type, row in JERLOV1976_TABLE_XXVII.items():
|
|
261
|
+
for nm, printed in zip(TABLE_XXVII_WAVELENGTHS, row):
|
|
262
|
+
if printed is None:
|
|
263
|
+
continue
|
|
264
|
+
got = w[water_type].kd(float(nm))
|
|
265
|
+
assert got == pytest.approx(printed / 100.0, rel=0.005), (
|
|
266
|
+
f"{water_type} at {nm} nm: shipped {got}, printed "
|
|
267
|
+
f"{printed / 100.0}"
|
|
268
|
+
)
|
|
269
|
+
checked += 1
|
|
270
|
+
assert checked == 158
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def test_the_wozniak_pelevin_reprint_differs_at_exactly_one_cell():
|
|
274
|
+
"""DATA.md section 16. A standing record of what that reprint got wrong."""
|
|
275
|
+
reprint = {k: list(v) for k, v in JERLOV1976_TABLE_XXVII.items()}
|
|
276
|
+
reprint["IB"][TABLE_XXVII_WAVELENGTHS.index(700)] = 59 # as printed
|
|
277
|
+
differing = [
|
|
278
|
+
(t, nm)
|
|
279
|
+
for t, row in reprint.items()
|
|
280
|
+
for nm, value in zip(TABLE_XXVII_WAVELENGTHS, row)
|
|
281
|
+
if value is not None
|
|
282
|
+
and value != JERLOV1976_TABLE_XXVII[t][
|
|
283
|
+
TABLE_XXVII_WAVELENGTHS.index(nm)]
|
|
284
|
+
]
|
|
285
|
+
assert differing == [("IB", 700)]
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def test_the_stated_unit_of_that_reprint_is_impossible():
|
|
289
|
+
"""Its header says 1e-3 m^-1, which puts Jerlov I below pure sea water."""
|
|
290
|
+
printed = JERLOV1976_TABLE_XXVII["I"][TABLE_XXVII_WAVELENGTHS.index(475)]
|
|
291
|
+
as_stated = printed * 1e-3
|
|
292
|
+
correct = printed * 1e-2
|
|
293
|
+
pure_water_absorption_at_475 = 0.011 # Austin & Petzold Kw, 1/m
|
|
294
|
+
assert as_stated < pure_water_absorption_at_475
|
|
295
|
+
assert correct > pure_water_absorption_at_475
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|