pyEQL 0.5.2__py3-none-any.whl → 1.1.0__py3-none-any.whl

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 (62) hide show
  1. pyEQL/__init__.py +50 -43
  2. pyEQL/activity_correction.py +481 -707
  3. pyEQL/database/geothermal.dat +5693 -0
  4. pyEQL/database/llnl.dat +19305 -0
  5. pyEQL/database/phreeqc_license.txt +54 -0
  6. pyEQL/database/pyeql_db.json +35902 -0
  7. pyEQL/engines.py +793 -0
  8. pyEQL/equilibrium.py +148 -228
  9. pyEQL/functions.py +121 -416
  10. pyEQL/pint_custom_units.txt +2 -2
  11. pyEQL/presets/Ringers lactate.yaml +20 -0
  12. pyEQL/presets/normal saline.yaml +17 -0
  13. pyEQL/presets/rainwater.yaml +17 -0
  14. pyEQL/presets/seawater.yaml +29 -0
  15. pyEQL/presets/urine.yaml +26 -0
  16. pyEQL/presets/wastewater.yaml +21 -0
  17. pyEQL/salt_ion_match.py +53 -284
  18. pyEQL/solute.py +126 -191
  19. pyEQL/solution.py +2163 -2090
  20. pyEQL/utils.py +165 -0
  21. pyEQL-1.1.0.dist-info/AUTHORS.md +13 -0
  22. {pyEQL-0.5.2.dist-info → pyEQL-1.1.0.dist-info}/COPYING +1 -1
  23. pyEQL-0.5.2.dist-info/LICENSE → pyEQL-1.1.0.dist-info/LICENSE.txt +3 -7
  24. pyEQL-1.1.0.dist-info/METADATA +129 -0
  25. pyEQL-1.1.0.dist-info/RECORD +27 -0
  26. {pyEQL-0.5.2.dist-info → pyEQL-1.1.0.dist-info}/WHEEL +2 -1
  27. pyEQL/chemical_formula.py +0 -1006
  28. pyEQL/database/Erying_viscosity.tsv +0 -18
  29. pyEQL/database/Jones_Dole_B.tsv +0 -32
  30. pyEQL/database/Jones_Dole_B_inorganic_Jenkins.tsv +0 -75
  31. pyEQL/database/LICENSE +0 -4
  32. pyEQL/database/dielectric_parameter.tsv +0 -30
  33. pyEQL/database/diffusion_coefficient.tsv +0 -116
  34. pyEQL/database/hydrated_radius.tsv +0 -35
  35. pyEQL/database/ionic_radius.tsv +0 -35
  36. pyEQL/database/partial_molar_volume.tsv +0 -22
  37. pyEQL/database/pitzer_activity.tsv +0 -169
  38. pyEQL/database/pitzer_volume.tsv +0 -132
  39. pyEQL/database/template.tsv +0 -14
  40. pyEQL/database.py +0 -300
  41. pyEQL/elements.py +0 -4552
  42. pyEQL/logging_system.py +0 -53
  43. pyEQL/parameter.py +0 -435
  44. pyEQL/tests/__init__.py +0 -32
  45. pyEQL/tests/test_activity.py +0 -578
  46. pyEQL/tests/test_bulk_properties.py +0 -86
  47. pyEQL/tests/test_chemical_formula.py +0 -279
  48. pyEQL/tests/test_debye_length.py +0 -79
  49. pyEQL/tests/test_density.py +0 -106
  50. pyEQL/tests/test_dielectric.py +0 -153
  51. pyEQL/tests/test_effective_pitzer.py +0 -276
  52. pyEQL/tests/test_mixed_electrolyte_activity.py +0 -154
  53. pyEQL/tests/test_osmotic_coeff.py +0 -99
  54. pyEQL/tests/test_pyeql_volume_concentration.py +0 -428
  55. pyEQL/tests/test_salt_matching.py +0 -337
  56. pyEQL/tests/test_solute_properties.py +0 -251
  57. pyEQL/water_properties.py +0 -352
  58. pyEQL-0.5.2.dist-info/AUTHORS +0 -7
  59. pyEQL-0.5.2.dist-info/METADATA +0 -72
  60. pyEQL-0.5.2.dist-info/RECORD +0 -47
  61. pyEQL-0.5.2.dist-info/entry_points.txt +0 -3
  62. {pyEQL-0.5.2.dist-info → pyEQL-1.1.0.dist-info}/top_level.txt +0 -0
pyEQL/logging_system.py DELETED
@@ -1,53 +0,0 @@
1
- # logging system
2
- """ Create a logging system using Python's built-in module.
3
-
4
- Each module within pyEQL has its own logger, with a StreamHandler attached to it that
5
- directs formatted messages to standard output. This is intended to facilitate the use
6
- of pyEQL as an interactive console program, at the expense of some flexibility when
7
- using it as a true library in another application.
8
-
9
- The default logging levels are mapped to pyEQL events as follows:
10
-
11
- DEBUG - detailed messages about function execution including methods used, data sources,
12
- temperature adjustments, etc.
13
- INFO - Messages indicating calculation steps, function calls, etc.
14
- WARNING - assumptions or limitations of module output
15
- ERROR - Module could not complete a task due to invalid input or other problem
16
- CRITICAL - not used
17
-
18
- :copyright: 2013-2020 by Ryan S. Kingsbury
19
- :license: LGPL, see LICENSE for more details.
20
-
21
- """
22
- import logging
23
-
24
- # define a log filter to emit only unique log messages
25
-
26
-
27
- class Unique(logging.Filter):
28
- """Messages are allowed through just once.
29
- The 'message' includes substitutions, but is not formatted by the
30
- handler. If it were, then practically all messages would be unique!
31
- """
32
-
33
- def __init__(self, name=""):
34
- logging.Filter.__init__(self, name)
35
- self.reset()
36
-
37
- def reset(self):
38
- """Act as if nothing has happened."""
39
- self.__logged = {}
40
-
41
- def filter(self, rec):
42
- """logging.Filter.filter performs an extra filter on the name."""
43
- return logging.Filter.filter(self, rec) and self.__is_first_time(rec)
44
-
45
- def __is_first_time(self, rec):
46
- """Emit a message only once."""
47
- msg = rec.msg % (rec.args)
48
- if msg in self.__logged:
49
- self.__logged[msg] += 1
50
- return False
51
- else:
52
- self.__logged[msg] = 1
53
- return True
pyEQL/parameter.py DELETED
@@ -1,435 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- This module implements the Parameter() class, which is used to store
4
- values, units, uncertainties, and reference data for various quantities
5
- used throughout pyEQL.
6
-
7
- :copyright: 2013-2020 by Ryan S. Kingsbury
8
- :license: LGPL, see LICENSE for more details.
9
-
10
- """
11
-
12
- # logging system
13
- import logging
14
- import os
15
-
16
- # per the pint documentation, it's important that pint and its associated Unit
17
- # Registry are only imported once.
18
- from pint import UnitRegistry
19
-
20
- # add a filter to emit only unique log messages to the handler
21
- from pyEQL.logging_system import Unique
22
-
23
- logger = logging.getLogger(__name__)
24
- unique = Unique()
25
- logger.addFilter(unique)
26
-
27
- # add a handler for console output, since pyEQL is meant to be used interactively
28
- ch = logging.StreamHandler()
29
-
30
- # create formatter for the log
31
- formatter = logging.Formatter("(%(name)s) - %(levelname)s - %(message)s")
32
-
33
- # add formatter to the handler
34
- ch.setFormatter(formatter)
35
- logger.addHandler(ch)
36
-
37
- # Units handling
38
-
39
- # here we assign the identifier 'unit' to the UnitRegistry
40
- unit = UnitRegistry()
41
-
42
- # use this to enable legacy handling of offset units
43
- # TODO fix this to handle offsets the way pint wants us to since 0.7
44
- unit.autoconvert_offset_to_baseunit = True
45
-
46
- # append custom unit definitions and contexts
47
- directory = os.path.dirname(__file__)
48
- unit.load_definitions(directory + "/pint_custom_units.txt")
49
- # activate the "chemistry" context globally
50
- unit.enable_contexts("chem")
51
- # set the default string formatting for pint quantities
52
- unit.default_format = "P~"
53
-
54
-
55
- def testfunc(val):
56
- list = []
57
- try:
58
- list.append(float(val) * unit(""))
59
- return list
60
- except ValueError:
61
- print("Value Error")
62
- return None
63
-
64
-
65
- class Parameter:
66
- """
67
- Class for storing and retrieving measured parameter values together with their
68
- units, context, and reference information.
69
-
70
- Some pyEQL functions search for specific parameter names, such as:
71
- diffusion_coefficient
72
-
73
-
74
-
75
- """
76
-
77
- def __init__(self, name, magnitude, units="", **kwargs):
78
- """
79
- Parameters
80
- ----------
81
- name : str
82
- A short name (akin to a variable name) for the parameter
83
-
84
- magnitude : float, int, str, tuple or list of floats, ints, or strs
85
- The value of the parameter. In most cases this will only be a single numerical value.
86
- However, in some cases it may be desirable to store a group of parameters (such as coefficients
87
- for an equation) together in a tuple or list.
88
-
89
- Numeric values can be input as strings (or lists of strings) and they will be converted to
90
- floats.
91
-
92
- Non-numeric values are permissible as well. When specifying non-numeric values, the units
93
- argument must either be 'dimensionless' or left blank.
94
-
95
- Lists of non-numeric strings are not permitted.
96
-
97
- units : str, optional
98
- A string representing the units of measure for the parameter value
99
- given in 'magnitude.' See the pint documentation for proper syntax. In general
100
- common abbreviations or unit names can be used, but pythonic math syntax
101
- must be followed. e.g. 'meters ** 2 / second' and 'm **2 /s' are valid but
102
- 'm2/s' is not.
103
-
104
- If a parameter has no units, leave blank or enter 'dimensionless'
105
-
106
- Note that if a parameter DOES have units but they are not specified, all
107
- calculations involving this parameter will return incorrect units.
108
-
109
- Optional Keyword Arguments
110
- --------------------------
111
- reference : str, optional
112
- A string containing reference information documenting the source of
113
- the parameter.
114
- uncertainty : tuple of floats or ints, optional
115
- The uncertainty of the parameter value as a percentage (0-100). If only one value is supplied in
116
- the tuple,
117
- the same uncertainty will be used for positive and negative. If two values are supplied, the first
118
- is the positive uncertainty and the second is the negative.
119
- uncertainty_type: str, optional
120
- A string specifying different ways of measuring uncertainty.
121
- temperature : str, optional
122
- The temperature at which 'magnitude' was measured in degrees Celsius.
123
- Specify the temperature as a string containing the magnitude and
124
- a unit, e.g. '25 degC', '32 degF', '298 kelvin', and '500 degR'
125
- pressure : str, optional
126
- The pressure at which 'magnitude' was measured in Pascals
127
- Specify the pressure as a string containing the magnitude and a
128
- unit. e.g. '101 kPa'.
129
- Typical valid units are 'Pa', 'atm', or 'torr'.
130
- ionic_strength : str, optional
131
- The ionic strength of the solution in which 'magnitude' was measured. Specify
132
- the ionic strength as a string containing the magnitude and a unit. e.g. '2 mol/kg'
133
- description : str, optional
134
- A string contiaining a longer name describing the paramter. For example
135
- 'Diffusion Coefficient' or 'Hydrated Ionic Radius'
136
- comments : str, optional
137
- A string containing additional notes pertaining to the context,
138
- conditions, or assumptions that may restrict the use of 'value'
139
-
140
- Notes
141
- -----
142
- In general, parameter values are assumed to be entered in fundamental
143
- SI units (m, kg, s, etc.). The 'units' field is required to call attention
144
- to this fact and provide a levelof error-checking in calculations involving
145
- the parameter.
146
-
147
- Examples
148
- --------
149
- # TODO fix this example
150
- >>> sodium_diffusion = Parameter('diffusion coefficient',(1.334e-9,),'m2/s','CRC Handbook of Chemistry and
151
- Physics, 92nd Ed., pp. 5-77 to 5-79',(),25,101325,0)
152
- >>> print(sodium_diffusion)
153
- Parameter diffusion coefficient
154
- <BLANKLINE>
155
- -------------------------------------------
156
- Value: (1.334e-09,) m2/s at 25 degrees C.
157
- Notes:
158
- Reference: CRC Handbook of Chemistry and Physics, 92nd Ed., pp. 5-77 to 5-79
159
- <BLANKLINE>
160
- """
161
- self.name = name
162
- use_units = ""
163
- # turn numeric parameters into quantities with associated units
164
- # if units were specified as 'None', convert into something pint will understand
165
- if (
166
- units == "None"
167
- or units == "none"
168
- or units == ""
169
- or units == "dimensionless"
170
- ):
171
- use_units = "dimensionless"
172
- else:
173
- use_units = units
174
-
175
- # see if the input value is a list or tuple. If so, create a list of
176
- # quantities (including units), and convert the list to a tuple
177
- if isinstance(magnitude, (tuple, list)):
178
- # check whether each element is a number
179
- temp_list = []
180
- for item in magnitude:
181
- try:
182
- temp_list.append(float(item) * unit(use_units))
183
- except ValueError:
184
- print("Value Error on %s" % item)
185
- # Throw an error if units are assigned to a non-numeric parameter
186
- if not (use_units == "dimensionless"):
187
- logger.error(
188
- "A non-numeric parameter cannot have units, but units of %s were specified"
189
- % units
190
- )
191
- temp_list.append(item)
192
-
193
- # convert the resulting list into a tuple
194
- self.value = tuple(temp_list)
195
-
196
- # if the input is a single item, try to convert it to a number. If that
197
- # doesn't work, it must be a str and will be passed on as-is
198
- else:
199
- try:
200
- self.value = float(magnitude) * unit(use_units)
201
- except ValueError:
202
- # Throw an error if units are assigned to a non-numeric parameter
203
- if not (use_units == "dimensionless"):
204
- logger.error(
205
- "A non-numeric parameter cannot have units, but units of %s were specified"
206
- % units
207
- )
208
-
209
- self.value = magnitude
210
-
211
- # process optional keyword arguments - reference conditions
212
- self.base_temperature = "Not specified"
213
- self.base_pressure = "Not specified"
214
- self.base_ionic_strength = "Not specified"
215
-
216
- if "temperature" in kwargs and kwargs["temperature"] != "":
217
- self.temperature_set = True
218
- self.base_temperature = unit(kwargs["temperature"])
219
- if "pressure" in kwargs and kwargs["pressure"] != "":
220
- self.pressure_set = True
221
- self.base_pressure = unit(kwargs["pressure"])
222
- if "ionic_strength" in kwargs and kwargs["ionic_strength"] != "":
223
- self.ionic_strength_set = True
224
- self.base_ionic_strength = unit(kwargs["ionic_strength"])
225
-
226
- # process optional keyword arguments - descriptive and reference
227
- self.reference = "Not specified"
228
- self.description = ""
229
- self.comment = ""
230
-
231
- if "reference" in kwargs:
232
- self.reference = kwargs["reference"]
233
- if "description" in kwargs:
234
- self.description = kwargs["description"]
235
- if "comment" in kwargs:
236
- self.comment = kwargs["comment"]
237
-
238
- # process optional keyword arguments - uncertanty
239
- # TODO - uncertainty functions / parameters
240
-
241
- # process optional keyword arguments - temperature adjustment
242
- # TODO - temperature adjustment functions / parameters
243
-
244
- def get_name(self):
245
- """
246
- Return the name of the parameter.
247
-
248
- Parameters
249
- ----------
250
- None
251
-
252
- Returns
253
- -------
254
- str
255
- The name of the parameter
256
- """
257
- return self.name
258
-
259
- def get_value(self, temperature=None, pressure=None, ionic_strength=None):
260
- """
261
- Return the value of a parameter at the specified conditions.
262
-
263
- Parameters
264
- ----------
265
- temperature : str, optional
266
- The temperature at which 'magnitude' was measured in degrees Celsius.
267
- Specify the temperature as a string containing the magnitude and
268
- a unit, e.g. '25 degC', '32 degF', '298 kelvin', and '500 degR'
269
- pressure : str, optional
270
- The pressure at which 'magnitude' was measured in Pascals
271
- Specify the pressure as a string containing the magnitude and a
272
- unit. e.g. '101 kPa'.
273
- Typical valid units are 'Pa', 'atm', or 'torr'.
274
- ionic_strength : str, optional
275
- The ionic strength of the solution in which 'magnitude' was measured. Specify
276
- the ionic strength as a string containing the magnitude and a unit. e.g. '2 mol/kg'
277
-
278
- Returns
279
- -------
280
- Quantity
281
- The value of the parameter at the specified conditions.
282
-
283
- """
284
- # if the user does not specify conditions, return the value at base_temperature,
285
- # base_pressure, and/or base_ionic_strength
286
- if temperature is None:
287
- temperature = self.base_temperature
288
- logger.info(
289
- "Temperature not specified for "
290
- + str(self.name)
291
- + ". Returning value at "
292
- + str(temperature)
293
- + "."
294
- )
295
- else:
296
- temperature = unit(temperature)
297
- if pressure is None:
298
- pressure = self.base_pressure
299
- logger.info(
300
- "Pressure not specified for "
301
- + str(self.name)
302
- + ". Returning value at "
303
- + str(pressure)
304
- + "."
305
- )
306
- else:
307
- pressure = unit(pressure)
308
- if ionic_strength is None:
309
- ionic_strength = self.base_ionic_strength
310
- logger.info(
311
- "Ionic Strength not specified for "
312
- + str(self.name)
313
- + ". Returning value at "
314
- + str(ionic_strength)
315
- + "."
316
- )
317
- else:
318
- ionic_strength = unit(ionic_strength)
319
-
320
- # compare requested conditions with base conditions
321
- if temperature != self.base_temperature:
322
- # TODO- implement temperature correction
323
- logger.warning(
324
- "Requested temperature for "
325
- + str(self.name)
326
- + " ("
327
- + str(temperature)
328
- + ") differs from measurement conditions."
329
- + "Returning value at "
330
- + str(self.base_temperature)
331
- )
332
-
333
- if pressure != self.base_pressure:
334
- # TODO - implement pressure correction
335
- logger.warning(
336
- "Requested pressure for "
337
- + str(self.name)
338
- + " ("
339
- + str(pressure)
340
- + ") differs from measurement conditions."
341
- + "Returning value at "
342
- + str(self.base_pressure)
343
- )
344
-
345
- if ionic_strength != self.base_ionic_strength:
346
- logger.warning(
347
- "Requested ionic strength for "
348
- + str(self.name)
349
- + " ("
350
- + str(ionic_strength)
351
- + ") differs from measurement conditions."
352
- + "Returning value at "
353
- + str(self.base_ionic_strength)
354
- )
355
-
356
- return self.value
357
-
358
- def get_magnitude(self, temperature=None, pressure=None, ionic_strength=None):
359
- """
360
- Return the magnitude of a parameter at the specified conditions.
361
-
362
- Parameters
363
- ----------
364
- temperature : str, optional
365
- The temperature at which 'magnitude' was measured in degrees Celsius.
366
- Specify the temperature as a string containing the magnitude and
367
- a unit, e.g. '25 degC', '32 degF', '298 kelvin', and '500 degR'
368
- pressure : str, optional
369
- The pressure at which 'magnitude' was measured in Pascals
370
- Specify the pressure as a string containing the magnitude and a
371
- unit. e.g. '101 kPa'.
372
- Typical valid units are 'Pa', 'atm', or 'torr'.
373
- ionic_strength : str, optional
374
- The ionic strength of the solution in which 'magnitude' was measured. Specify
375
- the ionic strength as a string containing the magnitude and a unit. e.g. '2 mol/kg'
376
-
377
- Returns
378
- -------
379
- Number
380
- The magnitude of the parameter at the specified conditions.
381
-
382
- """
383
- return self.get_value(temperature, pressure, ionic_strength).magnitude
384
-
385
- def get_units(self):
386
- """
387
- Return the units of a parameter
388
- """
389
- return self.get_value().units
390
-
391
- def get_dimensions(self):
392
- """
393
- Return the dimensions of the parameter.
394
- """
395
- return self.get_value().dimensionality
396
-
397
- def __str__(self):
398
- """
399
- Set the output of the print() statement for a parameter value
400
- """
401
- return (
402
- "\n"
403
- + "----------------------------------------------------------------------------"
404
- + "\n"
405
- + "Parameter "
406
- + str(self.name)
407
- + "\n\n"
408
- + str(self.description)
409
- + "\n"
410
- "Value: "
411
- + str(self.get_value())
412
- + "\n"
413
- + "Conditions (T,P,Ionic Strength): "
414
- + str(self.base_temperature)
415
- + ", "
416
- + str(self.base_pressure)
417
- + ", "
418
- + str(self.base_ionic_strength)
419
- + "\n"
420
- + "Notes: "
421
- + str(self.comment)
422
- + "\n"
423
- + "Reference: "
424
- + str(self.reference)
425
- + "\n"
426
- + "--------------------------------------------------------------------------------------"
427
- + "\n"
428
- )
429
-
430
-
431
- # TODO - turn doctest back on when the nosigint error is gone
432
- # Tests
433
- # if __name__ == "__main__":
434
- # import doctest
435
- # doctest.testmod()
pyEQL/tests/__init__.py DELETED
@@ -1,32 +0,0 @@
1
- import unittest
2
- import os
3
- import logging
4
-
5
-
6
- def testsuite():
7
- """A testsuite that includes all the pyEQL tests.
8
- """
9
- # turn off log warning messages during tests
10
- logging.disable(logging.WARNING)
11
-
12
- # include all files in the directory of this script
13
- suite = unittest.TestLoader().discover(os.path.dirname(__file__))
14
-
15
- return suite
16
-
17
-
18
- def main():
19
- """Runs the testsuite as command line application.
20
- """
21
- try:
22
- unittest.main()
23
- except Exception as e:
24
- print("Error: %s" % e)
25
-
26
-
27
- def run():
28
- """Run all tests.
29
- :return: a :class:`unittest.TestResult` object
30
- """
31
- test_runner = unittest.TextTestRunner()
32
- return test_runner.run(testsuite())