ferm-units 0.1.1__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 (43) hide show
  1. ferm_units-0.1.1/.gitignore +14 -0
  2. ferm_units-0.1.1/CHANGELOG.md +45 -0
  3. ferm_units-0.1.1/LICENSE +21 -0
  4. ferm_units-0.1.1/PKG-INFO +328 -0
  5. ferm_units-0.1.1/README.md +308 -0
  6. ferm_units-0.1.1/THIRD_PARTY_LICENSES.md +9 -0
  7. ferm_units-0.1.1/docs/asbc-verification.md +273 -0
  8. ferm_units-0.1.1/docs/reference/Acid Tier Units.txt +71 -0
  9. ferm_units-0.1.1/docs/reference/Base Metric Units.txt +131 -0
  10. ferm_units-0.1.1/docs/reference/Biofuel Units.txt +72 -0
  11. ferm_units-0.1.1/docs/reference/Brewing Units.txt +220 -0
  12. ferm_units-0.1.1/docs/reference/Cider & Perry Units.txt +93 -0
  13. ferm_units-0.1.1/docs/reference/Distilling Units.txt +150 -0
  14. ferm_units-0.1.1/docs/reference/README.md +109 -0
  15. ferm_units-0.1.1/docs/reference/Sake Units.txt +103 -0
  16. ferm_units-0.1.1/docs/reference/Wine Units.txt +562 -0
  17. ferm_units-0.1.1/docs/reference/solution-chemistry.md +483 -0
  18. ferm_units-0.1.1/docs/sources.md +208 -0
  19. ferm_units-0.1.1/pyproject.toml +68 -0
  20. ferm_units-0.1.1/src/fermunits/__init__.py +79 -0
  21. ferm_units-0.1.1/src/fermunits/bitterness.py +46 -0
  22. ferm_units-0.1.1/src/fermunits/carbonation.py +45 -0
  23. ferm_units-0.1.1/src/fermunits/color.py +67 -0
  24. ferm_units-0.1.1/src/fermunits/definitions/__init__.py +1 -0
  25. ferm_units-0.1.1/src/fermunits/definitions/solution_chemistry.txt +7 -0
  26. ferm_units-0.1.1/src/fermunits/definitions/vessels.txt +21 -0
  27. ferm_units-0.1.1/src/fermunits/diastatic.py +53 -0
  28. ferm_units-0.1.1/src/fermunits/gravity.py +140 -0
  29. ferm_units-0.1.1/src/fermunits/py.typed +0 -0
  30. ferm_units-0.1.1/src/fermunits/registry.py +143 -0
  31. ferm_units-0.1.1/src/fermunits/solution_chemistry.py +260 -0
  32. ferm_units-0.1.1/tests/test_bitterness.py +100 -0
  33. ferm_units-0.1.1/tests/test_carbonation.py +97 -0
  34. ferm_units-0.1.1/tests/test_color.py +117 -0
  35. ferm_units-0.1.1/tests/test_diastatic.py +100 -0
  36. ferm_units-0.1.1/tests/test_gravity.py +292 -0
  37. ferm_units-0.1.1/tests/test_registry.py +33 -0
  38. ferm_units-0.1.1/tests/test_solution_chemistry.py +422 -0
  39. ferm_units-0.1.1/tests/test_solution_chemistry_density.py +196 -0
  40. ferm_units-0.1.1/tests/test_solution_chemistry_molar_mass.py +248 -0
  41. ferm_units-0.1.1/tests/test_vessel_units.py +50 -0
  42. ferm_units-0.1.1/tests/test_water_engine_unit_contract.py +208 -0
  43. ferm_units-0.1.1/tests/typecheck/q_constructor.py +19 -0
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ dist/
9
+ build/
10
+ .DS_Store
11
+ *~
12
+
13
+ # Project Specific
14
+ references/
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ All notable changes to FermUnits will be documented in this file.
4
+
5
+ ## 0.1.1 - 2026-08-09
6
+
7
+ Typing-only maintenance release.
8
+
9
+ ### Fixed
10
+
11
+ - Preserve Pint's inferred quantity magnitude type through the public `Q_`
12
+ constructor instead of exposing constructed quantities as `Quantity[Any]`.
13
+ - Add static regression coverage for `float`, `int`, `Decimal`, and `Fraction`
14
+ quantity magnitudes constructed through `from fermunits import Q_`.
15
+
16
+ ### Notes
17
+
18
+ - No runtime unit or conversion behavior changed.
19
+ - Pint remains constrained to the existing supported `>=0.25,<0.26` range.
20
+
21
+ ## 0.1.0 - 2026-08-06
22
+
23
+ Initial pre-alpha distributable release.
24
+
25
+ ### Added
26
+
27
+ - Pint-based FermUnits registry and `Q_` quantity constructor.
28
+ - Fermentation-specific vessel definitions with qualified names where Pint
29
+ names are ambiguous or represent a different legitimate meaning.
30
+ - Brewing gravity, color, bitterness, diastatic-power, carbonation, and
31
+ refractometer calculations.
32
+ - Chemical-equivalent units and explicit mole/equivalent conversions.
33
+ - Equivalent-concentration conversions using explicit equivalent mass.
34
+ - Conventional calcium-carbonate reporting conversion for water analysis.
35
+ - Density-assisted mass-concentration/mass-fraction conversions.
36
+ - Molar-mass-assisted mass-concentration/amount-concentration conversions.
37
+ - Water-treatment downstream unit-contract tests.
38
+ - Source-verification policy and domain reference documentation.
39
+
40
+ ### Notes
41
+
42
+ - FermUnits remains pre-alpha.
43
+ - Relationships that still require authoritative ASBC or EBC verification are
44
+ documented as provisional rather than presented as stable universal
45
+ definitions.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Greg Roe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,328 @@
1
+ Metadata-Version: 2.4
2
+ Name: ferm-units
3
+ Version: 0.1.1
4
+ Summary: Fermentation beverage units and conversions built on Pint
5
+ Project-URL: Repository, https://github.com/GregRR/ferm-units
6
+ Project-URL: Issues, https://github.com/GregRR/ferm-units/issues
7
+ Author: Greg Roe
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Requires-Python: >=3.14
18
+ Requires-Dist: pint<0.26,>=0.25
19
+ Description-Content-Type: text/markdown
20
+
21
+ # FermUnits
22
+
23
+ FermUnits is a Pint-based Python library for units, measurement scales, and
24
+ conversions used in brewing, winemaking, cider making, mead making, distilling,
25
+ and related fermentation industries.
26
+
27
+ > **Status:** pre-alpha. The brewing implementation now includes vessel units,
28
+ > gravity calculations, color indices, analytical bitterness, diastatic power,
29
+ > and carbonation conversions. Some relationships remain provisional pending
30
+ > verification against original ASBC or EBC methods and tables.
31
+
32
+ ## Installation for development
33
+
34
+ ```bash
35
+ uv sync --dev
36
+ ```
37
+
38
+ ## Physical-unit example
39
+
40
+ ```python
41
+ from fermunits import Q_
42
+
43
+ cask = Q_(1, "firkin")
44
+ print(cask.to("liter"))
45
+ ```
46
+
47
+ ## Gravity examples
48
+
49
+ ```python
50
+ from fermunits import (
51
+ gravity_points_to_sg,
52
+ plato_to_sg,
53
+ sg_to_gravity_points,
54
+ sg_to_plato,
55
+ )
56
+
57
+ points = sg_to_gravity_points(1.050)
58
+ specific_gravity = gravity_points_to_sg(points)
59
+
60
+ plato = sg_to_plato(1.048)
61
+ estimated_sg = plato_to_sg(plato)
62
+ ```
63
+
64
+ The SG-to-Plato polynomial remains provisional pending verification against
65
+ authoritative ASBC extract tables or methods. The inverse function numerically
66
+ inverts the same polynomial to preserve internal consistency.
67
+
68
+ The current numerical inversion interval is an implementation limit rather
69
+ than an ASBC-approved scientific range.
70
+
71
+ ## Wort refractometer correction
72
+
73
+ These functions represent a wort-specific refractometer correction. They are
74
+ not general conversions between the Brix and Plato scales.
75
+
76
+ The correction factor must be supplied explicitly. FermUnits does not assume a
77
+ default wort correction factor.
78
+
79
+ ```python
80
+ from fermunits import (
81
+ plato_to_wort_refractometer_brix,
82
+ wort_refractometer_brix_to_plato,
83
+ )
84
+
85
+ plato = wort_refractometer_brix_to_plato(
86
+ apparent_brix=12.48,
87
+ wort_correction_factor=1.04,
88
+ )
89
+
90
+ apparent_brix = plato_to_wort_refractometer_brix(
91
+ plato=12.0,
92
+ wort_correction_factor=1.04,
93
+ )
94
+ ```
95
+
96
+ ## Beer color
97
+
98
+ Modern SRM and EBC color indices can be converted directly.
99
+
100
+ ```python
101
+ from fermunits import ebc_to_srm, srm_to_ebc
102
+
103
+ ebc = srm_to_ebc(10.0)
104
+ srm = ebc_to_srm(ebc)
105
+ ```
106
+
107
+ Lovibond conversions are explicitly labeled as approximations because the
108
+ older visual Lovibond scale is not equivalent to the modern
109
+ spectrophotometric SRM and EBC scales.
110
+
111
+ ```python
112
+ from fermunits import (
113
+ lovibond_to_srm_approx,
114
+ srm_to_lovibond_approx,
115
+ )
116
+
117
+ srm = lovibond_to_srm_approx(10.0)
118
+ lovibond = srm_to_lovibond_approx(srm)
119
+ ```
120
+
121
+ ## Analytical bitterness
122
+
123
+ FermUnits implements the coordinated ASBC/EBC-style analytical relationship
124
+ between absorbance at 275 nm and bitterness units.
125
+
126
+ ```python
127
+ from fermunits import (
128
+ absorbance_275nm_to_bitterness_units,
129
+ bitterness_units_to_absorbance_275nm,
130
+ )
131
+
132
+ bitterness_units = absorbance_275nm_to_bitterness_units(0.5)
133
+ absorbance = bitterness_units_to_absorbance_275nm(bitterness_units)
134
+ ```
135
+
136
+ Bitterness units are operational analytical results. They are not represented
137
+ as an exact concentration of iso-alpha-acids or as a direct measurement of
138
+ perceived bitterness.
139
+
140
+ FermUnits does not currently provide a separate arithmetic conversion between
141
+ IBU and EBU because those names refer to coordinated analytical methods rather
142
+ than clearly distinct numerical scales.
143
+
144
+ ## Diastatic power
145
+
146
+ ```python
147
+ from fermunits import (
148
+ lintner_to_windisch_kolbach,
149
+ windisch_kolbach_to_lintner,
150
+ )
151
+
152
+ windisch_kolbach = lintner_to_windisch_kolbach(60.0)
153
+ lintner = windisch_kolbach_to_lintner(windisch_kolbach)
154
+ ```
155
+
156
+ The Lintner and Windisch-Kolbach relationship remains provisional pending
157
+ verification against the original ASBC and EBC analytical methods.
158
+
159
+ ## Carbonation
160
+
161
+ ```python
162
+ from fermunits import (
163
+ co2_grams_per_liter_to_volumes,
164
+ co2_volumes_to_grams_per_liter,
165
+ )
166
+
167
+ grams_per_liter = co2_volumes_to_grams_per_liter(2.5)
168
+ volumes = co2_grams_per_liter_to_volumes(grams_per_liter)
169
+ ```
170
+
171
+ The current factor is derived from an ASBC-hosted technical source. Its exact
172
+ reference temperature, pressure, and relationship to the complete analytical
173
+ method remain pending verification.
174
+
175
+ The same factor is used in both directions to preserve round-trip consistency.
176
+
177
+ ## Hydrometer temperature correction
178
+
179
+ FermUnits does not currently implement hydrometer temperature correction.
180
+
181
+ The provisional formula listed in the original project inventory was rejected
182
+ because it omitted the hydrometer calibration temperature and produced
183
+ physically implausible results.
184
+
185
+ A correction will not be added until an authoritative method or table can be
186
+ implemented with:
187
+
188
+ * explicit sample temperature;
189
+ * explicit hydrometer calibration temperature;
190
+ * a defined temperature scale;
191
+ * supported temperature and specific-gravity ranges;
192
+ * a clearly identified sample matrix.
193
+
194
+ ## Solution chemistry and water treatment
195
+
196
+ FermUnits also provides shared solution-chemistry conversions intended for
197
+ water-treatment and other fermentation engineering applications. These APIs
198
+ keep chemical semantics explicit rather than hiding them inside ambiguous unit
199
+ labels.
200
+
201
+ Chemical-equivalent concentration uses FermUnits' separate `equivalent`
202
+ dimension. Converting from amount concentration requires an explicit
203
+ equivalence factor:
204
+
205
+ ```python
206
+ from fermunits import Q_, amount_concentration_to_equivalent_concentration
207
+
208
+ calcium = Q_(1.0, "millimole / liter")
209
+ charge_equivalents = amount_concentration_to_equivalent_concentration(
210
+ calcium,
211
+ equivalence_factor=2.0,
212
+ )
213
+ ```
214
+
215
+ For conventional water-analysis reporting, FermUnits implements the relationship
216
+ `50 mg/L as CaCO3 = 1 mEq/L`. The `as CaCO3` reporting basis remains application
217
+ metadata; it is not encoded as though calcium carbonate were necessarily the
218
+ dissolved analyte.
219
+
220
+ ```python
221
+ from fermunits import (
222
+ Q_,
223
+ caco3_basis_mass_concentration_to_equivalent_concentration,
224
+ )
225
+
226
+ alkalinity_as_caco3 = Q_(100.0, "milligram / liter")
227
+ alkalinity = caco3_basis_mass_concentration_to_equivalent_concentration(
228
+ alkalinity_as_caco3
229
+ )
230
+ ```
231
+
232
+ Mass concentration and mass fraction are not treated as interchangeable. A
233
+ conversion such as `mg/L` to `mg/kg` requires explicit solution density:
234
+
235
+ ```python
236
+ from fermunits import Q_, mass_concentration_to_mass_fraction
237
+
238
+ concentration = Q_(100.0, "milligram / liter")
239
+ density = Q_(1.05, "kilogram / liter")
240
+ mass_fraction = mass_concentration_to_mass_fraction(concentration, density)
241
+ ```
242
+
243
+ Likewise, conversion between mass concentration and amount concentration
244
+ requires an explicit molar mass supplied as a Pint quantity:
245
+
246
+ ```python
247
+ from fermunits import Q_, mass_concentration_to_amount_concentration
248
+
249
+ sodium_chloride = Q_(58.44, "milligram / liter")
250
+ molar_mass = Q_(58.44, "gram / mole")
251
+ amount_concentration = mass_concentration_to_amount_concentration(
252
+ sodium_chloride,
253
+ molar_mass,
254
+ )
255
+ ```
256
+
257
+ FermUnits preserves Pint's generic `ppm` unit, but canonical chemistry data
258
+ should use an explicit ratio such as `mg/kg` or `microgram / kilogram` when the intended basis
259
+ is mass fraction. FermUnits does not define a generic `ppb` alias.
260
+
261
+ ## Design principles
262
+
263
+ * Pint remains the physical-unit engine.
264
+ * FermUnits adds fermentation-industry definitions and domain-specific APIs.
265
+ * Ambiguous names such as bare `barrel` are not defined.
266
+ * Existing Pint meanings are preserved when they are legitimate.
267
+ * Domain-qualified names distinguish conflicting industry meanings.
268
+ * Empirical scales and calculations are kept separate from physical units.
269
+ * Approximate and provisional formulas are labeled clearly.
270
+ * Input validation rejects nonfinite or physically invalid values.
271
+ * Every domain definition and calculation should have a documented source and
272
+ tests.
273
+ * Restricted authoritative methods are recorded for later verification rather
274
+ than treated as though their details were directly confirmed.
275
+ * Unsupported formulas are rejected rather than implemented merely because
276
+ they appeared in the initial project inventory.
277
+
278
+ ## Current brewing scope
279
+
280
+ Implemented physical units include:
281
+
282
+ * modern British brewery cask units;
283
+ * modern US beer barrel;
284
+ * Imperial beer barrel;
285
+ * pin cask;
286
+ * firkin;
287
+ * kilderkin;
288
+ * domain-qualified wine and brewing hogsheads;
289
+ * domain-qualified brewing puncheon and butt.
290
+
291
+ Implemented brewing calculations include:
292
+
293
+ * specific gravity and gravity points;
294
+ * provisional specific gravity and degrees Plato conversion;
295
+ * explicit wort refractometer correction with a caller-supplied factor;
296
+ * SRM and EBC color-index conversion;
297
+ * approximate Lovibond and SRM conversion;
298
+ * analytical bitterness units from 275 nm absorbance;
299
+ * provisional Lintner and Windisch-Kolbach conversion;
300
+ * dissolved CO2 conversion between volumes and grams per liter.
301
+
302
+ Not yet implemented:
303
+
304
+ * hydrometer temperature correction;
305
+ * generic Brix, Plato, and Balling scale conversion;
306
+ * recipe-estimation formulas such as Tinseth or Rager bitterness;
307
+ * calculations that require unverified assumptions or inaccessible source
308
+ details.
309
+
310
+ Additional wine, distilling, sake, cider, biofuel, and fermentation-process
311
+ definitions will be added after their regional, historical, legal, or technical
312
+ meanings are documented.
313
+
314
+ ## Source verification
315
+
316
+ Detailed source-status notes and unresolved questions are tracked in:
317
+
318
+ ```text
319
+ docs/asbc-verification.md
320
+ ```
321
+
322
+ Items remain on that checklist until the original ASBC or EBC method, table,
323
+ or publication can be reviewed directly.
324
+
325
+ ## License
326
+
327
+ FermUnits is distributed under the MIT License. See [`LICENSE`](LICENSE).
328
+