Skip to content

Commit 075fb45

Browse files
committed
Fix analyte_mass bug
1 parent b0868c3 commit 075fb45

9 files changed

Lines changed: 197 additions & 176 deletions

File tree

.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/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: 14 additions & 6 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}")
@@ -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/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(),

calkulate/simulate.py

Lines changed: 78 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -278,83 +278,83 @@ def _titration(
278278
return titrant_mass, emf, temperature, analyte_mass, totals, k_constants
279279

280280

281-
def titration(
282-
alkalinity,
283-
analyte_mass=0.1,
284-
dic=0,
285-
emf0=600,
286-
fCO2_air=default.fCO2_air,
287-
k_dic_loss=None,
288-
salinity=35,
289-
temperature=25,
290-
titrant_mass_start=0,
291-
titrant_mass_step=0.15e-3,
292-
titrant_mass_stop=4.2e-3,
293-
titrant_molinity=0.1,
294-
least_squares_kwargs=default.least_squares_kwargs,
295-
pH_range=default.pH_range,
296-
**pyco2sys_kwargs,
297-
):
298-
"""Simulate a titration and return a calibrated and solved `Titration`.
281+
# def titration(
282+
# alkalinity,
283+
# analyte_mass=0.1,
284+
# dic=0,
285+
# emf0=600,
286+
# fCO2_air=default.fCO2_air,
287+
# k_dic_loss=None,
288+
# salinity=35,
289+
# temperature=25,
290+
# titrant_mass_start=0,
291+
# titrant_mass_step=0.15e-3,
292+
# titrant_mass_stop=4.2e-3,
293+
# titrant_molinity=0.1,
294+
# least_squares_kwargs=default.least_squares_kwargs,
295+
# pH_range=default.pH_range,
296+
# **pyco2sys_kwargs,
297+
# ):
298+
# """Simulate a titration and return a calibrated and solved `Titration`.
299299

300-
Parameters
301-
----------
302-
alkalinity : float
303-
Total alkalinity content of the analyte in µmol/kg.
304-
analyte_mass : float, optional
305-
Mass of the analyte in kg, by default 0.1 kg.
306-
dic : float, optional
307-
Dissolved inorganic carbon of the analyte in µmol/kg, by default 0 µmol/kg.
308-
emf0 : float, optional
309-
EMF0 of the electrode in mV, by default 600 mV.
310-
salinity : float, optional
311-
Practical salinity of the analyte, by default 35.
312-
temperature : float, optional
313-
Temperature of the analyte in °C, by default 25 °C.
314-
titrant_mass_start : float, optional
315-
Mass of titrant at the start of the titration in kg, by default 0 kg.
316-
titrant_mass_step : float, optional
317-
Mass of each titrant addition step in kg, by default 0.15e-3 kg.
318-
titrant_mass_stop : float, optional
319-
Mass at which to stop the titration (exclusive) in kg, by default 4.2e-3 kg.
320-
titrant_molinity : float, optional
321-
Molinity of the titrant in mol/kg, by default 0.1 mol/kg.
322-
least_squares_kwargs : dict, optional
323-
Additional kwargs passed on to the least-squares solver.
324-
pH_range : tuple, optional
325-
Range of pH values to determine alkalinity within, by default (3, 4).
326-
**pyco2sys_kwargs
327-
Additional kwargs passed on to PyCO2SYS.
300+
# Parameters
301+
# ----------
302+
# alkalinity : float
303+
# Total alkalinity content of the analyte in µmol/kg.
304+
# analyte_mass : float, optional
305+
# Mass of the analyte in kg, by default 0.1 kg.
306+
# dic : float, optional
307+
# Dissolved inorganic carbon of the analyte in µmol/kg, by default 0 µmol/kg.
308+
# emf0 : float, optional
309+
# EMF0 of the electrode in mV, by default 600 mV.
310+
# salinity : float, optional
311+
# Practical salinity of the analyte, by default 35.
312+
# temperature : float, optional
313+
# Temperature of the analyte in °C, by default 25 °C.
314+
# titrant_mass_start : float, optional
315+
# Mass of titrant at the start of the titration in kg, by default 0 kg.
316+
# titrant_mass_step : float, optional
317+
# Mass of each titrant addition step in kg, by default 0.15e-3 kg.
318+
# titrant_mass_stop : float, optional
319+
# Mass at which to stop the titration (exclusive) in kg, by default 4.2e-3 kg.
320+
# titrant_molinity : float, optional
321+
# Molinity of the titrant in mol/kg, by default 0.1 mol/kg.
322+
# least_squares_kwargs : dict, optional
323+
# Additional kwargs passed on to the least-squares solver.
324+
# pH_range : tuple, optional
325+
# Range of pH values to determine alkalinity within, by default (3, 4).
326+
# **pyco2sys_kwargs
327+
# Additional kwargs passed on to PyCO2SYS.
328328

329-
Returns
330-
-------
331-
calkulate.Titration
332-
A self-calibrated and solved titration dataset.
333-
"""
334-
tt = Titration(
335-
salinity=salinity,
336-
analyte_mass=analyte_mass,
337-
simulate_alkalinity=alkalinity,
338-
simulate_kwargs=dict(
339-
dic=dic,
340-
emf0=emf0,
341-
fCO2_air=fCO2_air,
342-
k_dic_loss=k_dic_loss,
343-
salinity=salinity,
344-
temperature=temperature,
345-
titrant_mass_start=titrant_mass_start,
346-
titrant_mass_step=titrant_mass_step,
347-
titrant_mass_stop=titrant_mass_stop,
348-
titrant_molinity=titrant_molinity,
349-
**pyco2sys_kwargs,
350-
),
351-
)
352-
tt.calkulate(
353-
alkalinity,
354-
analyte_total_sulfate=None, # H2SO4 simulations not implemented yet
355-
least_squares_kwargs=least_squares_kwargs,
356-
pH_range=pH_range,
357-
titrant_molinity_guess=titrant_molinity,
358-
titrant="HCl", # H2SO4 simulations not implemented yet
359-
)
360-
return tt
329+
# Returns
330+
# -------
331+
# calkulate.Titration
332+
# A self-calibrated and solved titration dataset.
333+
# """
334+
# tt = Titration(
335+
# salinity=salinity,
336+
# analyte_mass=analyte_mass,
337+
# simulate_alkalinity=alkalinity,
338+
# simulate_kwargs=dict(
339+
# dic=dic,
340+
# emf0=emf0,
341+
# fCO2_air=fCO2_air,
342+
# k_dic_loss=k_dic_loss,
343+
# salinity=salinity,
344+
# temperature=temperature,
345+
# titrant_mass_start=titrant_mass_start,
346+
# titrant_mass_step=titrant_mass_step,
347+
# titrant_mass_stop=titrant_mass_stop,
348+
# titrant_molinity=titrant_molinity,
349+
# **pyco2sys_kwargs,
350+
# ),
351+
# )
352+
# tt.calkulate(
353+
# alkalinity,
354+
# analyte_total_sulfate=None, # H2SO4 simulations not implemented yet
355+
# least_squares_kwargs=least_squares_kwargs,
356+
# pH_range=pH_range,
357+
# titrant_molinity_guess=titrant_molinity,
358+
# titrant="HCl", # H2SO4 simulations not implemented yet
359+
# )
360+
# return tt

0 commit comments

Comments
 (0)