Skip to content

Commit fa9ad9e

Browse files
authored
Merge pull request #50 from mvdh7/develop
Merge for v23.7.1
2 parents 51453cd + 606a693 commit fa9ad9e

137 files changed

Lines changed: 21805 additions & 182 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/python-package.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ jobs:
1212
runs-on: ${{ matrix.os }}
1313
strategy:
1414
matrix:
15-
python-version: ["3.10", "3.11", "3.12", "3.13"]
15+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
1616
os: [ubuntu-latest, windows-latest, macos-latest]
1717

1818
steps:
19-
- uses: actions/checkout@v4
19+
- uses: actions/checkout@v6
2020
- name: Set up Python ${{ matrix.python-version }}
21-
uses: actions/setup-python@v5
21+
uses: actions/setup-python@v6
2222
with:
2323
python-version: ${{ matrix.python-version }}
2424
- name: Install dependencies

.misc/coverage.svg

Lines changed: 1 addition & 21 deletions
Loading

.misc/coverage.txt

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,43 @@
1-
Name Stmts Miss Cover
2-
-----------------------------------------------------------------
3-
calkulate/__init__.py 7 0 100%
4-
calkulate/constants.py 4 0 100%
5-
calkulate/convert.py 40 6 85%
6-
calkulate/core/__init__.py 94 5 95%
7-
calkulate/core/loss.py 61 0 100%
8-
calkulate/dataset.py 185 11 94%
9-
calkulate/default.py 28 0 100%
10-
calkulate/density.py 26 3 88%
11-
calkulate/interface.py 75 3 96%
12-
calkulate/meta.py 6 0 100%
13-
calkulate/plot/__init__.py 63 56 11%
14-
calkulate/plot/misc.py 3 0 100%
15-
calkulate/plot/titration.py 154 2 99%
16-
calkulate/read.py 80 8 90%
17-
calkulate/simulate.py 106 0 100%
18-
calkulate/titration.py 228 32 86%
19-
tests/__init__.py 0 0 100%
20-
tests/test_SOP3b.py 11 0 100%
21-
tests/test_convert.py 21 0 100%
22-
tests/test_core.py 42 0 100%
23-
tests/test_dataset_io.py 13 0 100%
24-
tests/test_dataset_manual.py 61 0 100%
25-
tests/test_dataset_pH_scales.py 6 0 100%
26-
tests/test_dbs_calk.py 32 0 100%
27-
tests/test_density.py 11 0 100%
28-
tests/test_experiment.py 13 4 69%
29-
tests/test_io.py 13 0 100%
30-
tests/test_irlon.py 5 0 100%
31-
tests/test_js_geomar.py 18 0 100%
32-
tests/test_loss_simulation.py 45 1 98%
33-
tests/test_misc.py 5 0 100%
34-
tests/test_oberlin.py 6 0 100%
35-
tests/test_pH_scales_simulated_titration.py 46 0 100%
36-
tests/test_simulate.py 22 0 100%
37-
tests/test_simulate_pH_scales.py 41 0 100%
38-
tests/test_simulate_sulfate.py 103 1 99%
39-
tests/test_simulate_then_solve.py 55 0 100%
40-
tests/test_titration_class.py 66 0 100%
41-
-----------------------------------------------------------------
42-
TOTAL 1795 132 93%
1+
Name Stmts Miss Cover
2+
-------------------------------------------------------
3+
calkulate/__init__.py 7 0 100%
4+
calkulate/classes.py 73 18 75%
5+
calkulate/constants.py 3 0 100%
6+
calkulate/convert.py 68 16 76%
7+
calkulate/core.py 139 6 96%
8+
calkulate/dataset.py 141 10 93%
9+
calkulate/default.py 26 0 100%
10+
calkulate/density.py 28 4 86%
11+
calkulate/files.py 69 18 74%
12+
calkulate/interface.py 74 12 84%
13+
calkulate/meta.py 18 0 100%
14+
calkulate/plot/__init__.py 62 56 10%
15+
calkulate/plot/misc.py 3 1 67%
16+
calkulate/plot/titration.py 169 142 16%
17+
calkulate/read/__init__.py 0 0 100%
18+
calkulate/read/metadata.py 49 7 86%
19+
calkulate/read/titrations.py 60 4 93%
20+
calkulate/settings.py 1 0 100%
21+
calkulate/simulate.py 100 9 91%
22+
tests/__init__.py 0 0 100%
23+
tests/test_SOP3b.py 16 0 100%
24+
tests/test_convert.py 22 0 100%
25+
tests/test_core.py 29 0 100%
26+
tests/test_dataset_io.py 14 0 100%
27+
tests/test_dbs_calk.py 41 0 100%
28+
tests/test_density.py 13 0 100%
29+
tests/test_io.py 13 0 100%
30+
tests/test_irlon.py 5 0 100%
31+
tests/test_js_geomar.py 13 0 100%
32+
tests/test_misc.py 7 0 100%
33+
tests/test_oberlin.py 6 0 100%
34+
tests/test_seao2_opt1.py 12 0 100%
35+
tests/test_seao2_opt2.py 12 0 100%
36+
tests/test_simulate.py 24 0 100%
37+
tests/test_simulate_pH_scales.py 41 0 100%
38+
tests/test_simulate_then_solve.py 69 0 100%
39+
tests/test_ts_tiamo.py 55 0 100%
40+
tests/test_ts_tiamo_ds.py 17 0 100%
41+
tests/test_ts_vindta.py 9 0 100%
42+
-------------------------------------------------------
43+
TOTAL 1508 303 80%

calkulate/classes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import PyCO2SYS as pyco2
55

66
from .convert import get_dilution_factor
7-
from .core import SolveEmfResult, SolvePhGranResult, SolvePhResult
7+
from .core import SolveEmfResult, SolvePhGranResult
88
from .dataset import _backcompat, add_solve_results
99
from .files import solve
1010

calkulate/convert.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,34 @@ def f_to_demf0(f, temperature):
144144
return np.log(f) * R * temperature_K / F
145145

146146

147+
def analyte_volume_to_mass(analyte_volume, temperature, salinity):
148+
"""Convert analyte volume to mass with MP81's 1-atm seawater density.
149+
150+
Parameters
151+
----------
152+
analyte_volume : float
153+
Volume of the analyte in ml.
154+
temperature : float
155+
Temperature in °C.
156+
salinity : float
157+
Practical salinity.
158+
159+
Returns
160+
-------
161+
float
162+
Mass of the analyte in kg.
163+
"""
164+
analyte_mass = (
165+
analyte_volume
166+
* density.seawater_1atm_MP81(
167+
temperature=temperature,
168+
salinity=salinity,
169+
)
170+
* 1e-3
171+
)
172+
return analyte_mass
173+
174+
147175
def amount_units(
148176
dat_data,
149177
salinity,
@@ -235,18 +263,13 @@ def amount_units(
235263
titrant_mass = dd.titrant_amount * 1e-3
236264
elif titrant_amount_unit.lower() == "kg":
237265
titrant_mass = dd.titrant_amount
238-
# Convert analyte_mass to analyte_volume if necessary
266+
# Convert analyte_volume to analyte_mass if necessary
239267
if pd.isnull(analyte_mass):
240268
assert not pd.isnull(analyte_volume), (
241269
"Either `analyte_mass` or `analyte_volume` must be provided"
242270
)
243-
analyte_mass = (
244-
analyte_volume
245-
* density.seawater_1atm_MP81(
246-
temperature=temperature[0],
247-
salinity=salinity,
248-
)
249-
* 1e-3
271+
analyte_mass = analyte_volume_to_mass(
272+
analyte_volume, temperature[0], salinity
250273
)
251274
return Converted(
252275
titrant_mass, dd.measurement, temperature, analyte_mass, salinity

calkulate/dataset.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pandas as pd
99
import PyCO2SYS as pyco2
1010

11-
from . import files
11+
from . import convert, files
1212
from .core import SolveEmfResult, SolvePhGranResult, SolvePhResult
1313
from .meta import _get_kwargs_for
1414

@@ -127,7 +127,7 @@ def prepare(ds):
127127
ds["file_good"] = True
128128

129129

130-
def calibrate(ds, verbose=False, **kwargs):
130+
def calibrate(ds, then_solve=True, verbose=False, **kwargs):
131131
"""Calibrate `titrant_molinity` for all titrations with an
132132
`alkalinity_certified` value and assign means based on `analysis_batch`.
133133
@@ -136,8 +136,10 @@ def calibrate(ds, verbose=False, **kwargs):
136136
ds : pandas.DataFrame
137137
A table containing metadata for each titration (not used if running as
138138
a method).
139+
then_solve : bool, optional
140+
Whether to solve after calibrating, by default `True`.
139141
verbose : bool, optional
140-
Whether to print progress, by default `calk.default.verbose`.
142+
Whether to print progress, by default `False`.
141143
142144
Returns
143145
-------
@@ -163,7 +165,8 @@ def calibrate(ds, verbose=False, **kwargs):
163165
ds.analysis_batch, "titrant_molinity"
164166
].to_numpy()
165167
print("Calkulate: calibration complete!")
166-
ds = solve(ds, verbose=verbose, **kwargs)
168+
if then_solve:
169+
ds = solve(ds, verbose=verbose, **kwargs)
167170
return ds
168171

169172

@@ -220,6 +223,12 @@ def solve_row(row, verbose=False, **kwargs):
220223
**kwargs_solve,
221224
)
222225
solved = add_solve_results(solved, sr)
226+
if pd.isnull(row.analyte_mass):
227+
solved["analyte_mass"] = convert.analyte_volume_to_mass(
228+
row.analyte_volume,
229+
solved["temperature_init"],
230+
row.salinity,
231+
)
223232
except Exception as e:
224233
print(f'Error solving "{row.file_name}":')
225234
print(f"{e}")
@@ -247,7 +256,7 @@ def solve(ds, verbose=False, **kwargs):
247256
# Check for bad kwargs, but don't break on them
248257
kwargs_ignored = []
249258
for k in _backcompat(kwargs.copy(), []):
250-
if k not in files.keys_calibrate | {"pH_range", "read_dat_kwargs"}:
259+
if k not in files.keys_solve | {"pH_range", "read_dat_kwargs"}:
251260
kwargs_ignored.append(k)
252261
if len(kwargs_ignored) > 0:
253262
warn(
@@ -283,6 +292,5 @@ def calkulate(ds, verbose=False, **kwargs):
283292
pd.DataFrame
284293
The titration metadataset with additional columns found by the solver.
285294
"""
286-
calibrate(ds, verbose=verbose, **kwargs)
287-
solve(ds, verbose=verbose, **kwargs)
295+
calibrate(ds, then_solve=True, verbose=verbose, **kwargs)
288296
return ds

calkulate/meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
_authorlist = ["Humphreys, Matthew P.", "Matthews, Ruth S."]
1111
__author__ = " and ".join(_authorlist)
12-
__version__ = "23.7.0"
12+
__version__ = "23.7.1"
1313
__year__ = 2025
1414

1515

calkulate/plot/titration.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ def alkalinity(tt, ax=None):
205205
ax.set_xlabel("Titrant mass / g")
206206
ax.set_ylabel("Alkalinity / μmol/kg-sw")
207207
ax.set_title(
208-
"Total alkalinity = {:.1f} ± {:.1f} μmol/kg-sw; $n$ = {}".format(
208+
"{} (TA = {:.1f} ± {:.1f} μmol/kg-sw; $n$ = {})".format(
209+
tt.bottle,
209210
tt.alkalinity,
210211
ttt.alkalinity_estimate[ttt.used].std() * 1e6,
211212
ttt.used.sum(),

0 commit comments

Comments
 (0)