yaeos 4.0.0__cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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.
yaeos/envelopes.py ADDED
@@ -0,0 +1,510 @@
1
+ """Envelopes.
2
+
3
+ This module contains the classes that wrap the data structures used to
4
+ represent different kinds of phase envelopes.
5
+ """
6
+
7
+ from IPython.display import display
8
+
9
+ import matplotlib.pyplot as plt
10
+
11
+ import numpy as np
12
+
13
+ import pandas as pd
14
+
15
+
16
+ class PTEnvelope:
17
+ """PTEnvelope.
18
+ This class represents a pressure-temperature envelope.
19
+ Attributes
20
+ ----------
21
+ global_composition : np.ndarray
22
+ The global composition of the system. Shape is (n_components,).
23
+ main_phases_compositions : np.ndarray
24
+ The compositions of the main phases along the envelope.
25
+ Shape is (n_points, n_phases, n_components).
26
+ reference_phase_compositions : np.ndarray
27
+ The compositions of the reference phase along the envelope.
28
+ Shape is (n_points, n_components).
29
+ main_phases_molar_fractions : np.ndarray
30
+ The molar fractions of the main phases along the envelope.
31
+ Shape is (n_points, n_phases).
32
+ pressures : np.ndarray
33
+ The pressures along the envelope. [bar]
34
+ temperatures : np.ndarray
35
+ The temperatures along the envelope. [K]
36
+ iterations : np.ndarray
37
+ The number of iterations taken to compute the envelope at each point.
38
+ Shape is (n_points,).
39
+ specified_variable : np.ndarray
40
+ The specified variable used to compute the envelope at each point.
41
+ Shape is (n_points,).
42
+ reference_phase_kinds : np.ndarray
43
+ The kinds of the reference phase at each point.
44
+ Shape is (n_points,).
45
+ main_phases_kinds : np.ndarray
46
+ The kinds of the main phases at each point.
47
+ Shape is (n_points, n_phases).
48
+ cp : list
49
+ A list of lists containing the indices of the critical points for each
50
+ phase. Each sublist corresponds to a phase and contains the indices of
51
+ the critical points in the `temperatures` and `pressures` arrays.
52
+ df : pd.DataFrame
53
+ A DataFrame containing the data of the envelope. The columns are:
54
+ - 'T': Temperatures along the envelope.
55
+ - 'P': Pressures along the envelope.
56
+ - 'x_i^j': Compositions of the main phases, where `i` is the component index and `j` is the phase index.
57
+ - 'w_i': Compositions of the reference phase, where `i` is the component index.
58
+ - 'beta^j': Molar fractions of the main phases, where `j` is the phase index.
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ global_composition,
64
+ main_phases_compositions,
65
+ reference_phase_compositions,
66
+ reference_phase_kinds,
67
+ main_phases_kinds,
68
+ main_phases_molar_fractions,
69
+ pressures,
70
+ temperatures,
71
+ iterations,
72
+ specified_variable,
73
+ critical_pressures,
74
+ critical_temperatures,
75
+ ):
76
+
77
+ msk = ~np.isnan(pressures)
78
+ msk_cp = ~np.isnan(critical_pressures)
79
+ self.number_of_components = len(global_composition)
80
+ self.number_of_phases = main_phases_compositions.shape[1]
81
+ self.global_composition = global_composition
82
+ self.main_phases_compositions = main_phases_compositions[msk, :, :]
83
+ self.reference_phase_compositions = reference_phase_compositions[
84
+ msk, :
85
+ ]
86
+ self.main_phases_molar_fractions = main_phases_molar_fractions[msk]
87
+ self.pressures = pressures[msk]
88
+ self.temperatures = temperatures[msk]
89
+ self.iterations = iterations[msk]
90
+ self.specified_variable = specified_variable[msk]
91
+ self.reference_phase_kinds = reference_phase_kinds[msk]
92
+ self.main_phases_kinds = main_phases_kinds[msk]
93
+ self.critical_pressures = critical_pressures[msk_cp]
94
+ self.critical_temperatures = critical_temperatures[msk_cp]
95
+
96
+ df = pd.DataFrame()
97
+
98
+ df["T"] = self.temperatures
99
+ df["P"] = self.pressures
100
+
101
+ for i in range(self.number_of_components):
102
+ for j in range(self.number_of_phases):
103
+ df[f"x_{i+1}^{j+1}"] = self.main_phases_compositions[:, j, i]
104
+ df[f"w_{i+1}"] = self.reference_phase_compositions[:, i]
105
+
106
+ for i in range(self.number_of_phases):
107
+ df[f"beta^{i+1}"] = self.main_phases_molar_fractions[:, i]
108
+
109
+ self.df = df
110
+
111
+ def plot(self, **plot_kwargs):
112
+ if "ax" in plot_kwargs:
113
+ ax = plot_kwargs["ax"]
114
+ del plot_kwargs["ax"]
115
+ else:
116
+ ax = plt.gca()
117
+ ax.plot(self.temperatures, self.pressures, **plot_kwargs)
118
+ ax.set_xlabel("Temperature [K]")
119
+ ax.set_ylabel("Pressure [bar]")
120
+ ax.scatter(
121
+ self.critical_temperatures, self.critical_pressures, color="black"
122
+ )
123
+
124
+ def __getitem__(self, key):
125
+ if "key" in self.__dict__:
126
+ return self.__dict__["key"]
127
+ elif isinstance(key, np.ndarray) or isinstance(key, list):
128
+ return PTEnvelope(
129
+ global_composition=self.global_composition,
130
+ main_phases_compositions=self.main_phases_compositions[key],
131
+ reference_phase_compositions=self.reference_phase_compositions[
132
+ key
133
+ ],
134
+ main_phases_molar_fractions=self.main_phases_molar_fractions[
135
+ key
136
+ ],
137
+ main_phases_kinds=self.main_phases_kinds[key],
138
+ reference_phase_kinds=self.reference_phase_kinds[key],
139
+ pressures=self.pressures[key],
140
+ temperatures=self.temperatures[key],
141
+ iterations=self.iterations[key],
142
+ specified_variable=self.specified_variable[key],
143
+ critical_pressures=self.critical_pressures,
144
+ critical_temperatures=self.critical_temperatures,
145
+ )
146
+ elif key == "T":
147
+ return self.temperatures
148
+ elif key == "Tc":
149
+ return self.critical_temperatures
150
+ elif key == "Pc":
151
+ return self.critical_pressures
152
+ elif key == "P":
153
+ return self.pressures
154
+ elif key == "z":
155
+ return self.global_composition
156
+ elif key == "x":
157
+ return self.main_phases_compositions
158
+ elif key == "w":
159
+ return self.reference_phase_compositions
160
+
161
+ def __repr__(self):
162
+ display(self.df)
163
+ return ""
164
+
165
+ def __len__(self):
166
+ return len(self.temperatures)
167
+
168
+
169
+ class PXEnvelope:
170
+ """PXEnvelope.
171
+
172
+ This class represents a pressure-composition envelope.
173
+
174
+ Attributes
175
+ ----------
176
+ temperature : float
177
+ The temperature at which the envelope is defined. [K]
178
+ global_composition_0 : np.ndarray
179
+ The global composition at the point where :math:`\alpha = 0`.
180
+ global_composition_i : np.ndarray
181
+ The global composition at the point where :math:`\alpha = 1`.
182
+ main_phases_compositions : np.ndarray
183
+ The compositions of the main phases along the envelope.
184
+ Shape is (n_points, n_phases, n_components).
185
+ reference_phase_compositions : np.ndarray
186
+ The compositions of the reference phase along the envelope.
187
+ Shape is (n_points, n_components).
188
+ main_phases_molar_fractions : np.ndarray
189
+ The molar fractions of the main phases along the envelope.
190
+ Shape is (n_points, n_phases).
191
+ pressures : np.ndarray
192
+ The pressures along the envelope. [bar]
193
+ alphas : np.ndarray
194
+ The molar fraction of the `global_composition_i`, :math:`\alpha`.
195
+ Shape is (n_points,).
196
+ critical_pressures : np.ndarray
197
+ The critical pressures of the envelope. Shape is (n_critical_points,).
198
+ critical_alphas : np.ndarray
199
+ The molar fractions of the `global_composition_i` at the critical
200
+ points, :math:`\alpha`. Shape is (n_critical_points,).
201
+ iterations : np.ndarray
202
+ The number of iterations taken to compute the envelope at each point.
203
+ Shape is (n_points,).
204
+ specified_variable : np.ndarray
205
+ The specified variable used to compute the envelope at each point.
206
+ Shape is (n_points,).
207
+ df : pd.DataFrame
208
+ A DataFrame containing the data of the envelope. The columns are:
209
+ - 'alpha': Molar fraction of the `global_composition_i`.
210
+ - 'P': Pressures along the envelope.
211
+ - 'x_i^j': Compositions of the main phases, where `i`
212
+ is the component index and `j` is the phase index.
213
+ - 'w_i': Compositions of the reference phase, where `i` is the
214
+ component index.
215
+ - 'beta^j': Molar fractions of the main phases, where `j`
216
+ is the phase index.
217
+ """
218
+
219
+ def __init__(
220
+ self,
221
+ temperature,
222
+ global_composition_0,
223
+ global_composition_i,
224
+ main_phases_compositions,
225
+ reference_phase_compositions,
226
+ main_phases_molar_fractions,
227
+ pressures,
228
+ alphas,
229
+ iterations,
230
+ specified_variable,
231
+ critical_pressures,
232
+ critical_alphas,
233
+ main_phases_kinds,
234
+ reference_phase_kinds,
235
+ ):
236
+
237
+ msk = ~np.isnan(pressures)
238
+ msk_cp = ~np.isnan(critical_pressures)
239
+
240
+ self.temperature = temperature
241
+ self.number_of_components = len(global_composition_0)
242
+ self.number_of_phases = main_phases_compositions.shape[1]
243
+ self.global_composition_0 = global_composition_0
244
+ self.global_composition_i = global_composition_i
245
+ self.main_phases_compositions = main_phases_compositions[msk, :, :]
246
+ self.reference_phase_compositions = reference_phase_compositions[
247
+ msk, :
248
+ ]
249
+ self.main_phases_molar_fractions = main_phases_molar_fractions[msk]
250
+ self.pressures = pressures[msk]
251
+ self.alphas = alphas[msk]
252
+ self.iterations = iterations[msk]
253
+ self.specified_variable = specified_variable[msk]
254
+ self.main_phases_kinds = main_phases_kinds[msk]
255
+ self.reference_phase_kinds = reference_phase_kinds[msk]
256
+ self.critical_pressures = critical_pressures[msk_cp]
257
+ self.critical_alphas = critical_alphas[msk_cp]
258
+
259
+ df = pd.DataFrame()
260
+
261
+ df["alpha"] = self.alphas
262
+ df["P"] = self.pressures
263
+
264
+ for i in range(self.number_of_components):
265
+ for j in range(self.number_of_phases):
266
+ df[f"x_{i+1}^{j+1}"] = self.main_phases_compositions[:, j, i]
267
+ df[f"w_{i+1}"] = self.reference_phase_compositions[:, i]
268
+
269
+ for i in range(self.number_of_phases):
270
+ df[f"beta^{i+1}"] = self.main_phases_molar_fractions[:, i]
271
+
272
+ self.df = df
273
+
274
+ def plot(self, **plot_kwargs):
275
+ """Plot the envelope.
276
+
277
+ Plot the envelope in a matplotlib axis. Using the natural variables of
278
+ the envelope (:math:`alpha` and pressure) If the `ax` keyword argument
279
+ is provided, it will be used as the axis to plot on. Otherwise, the
280
+ current axis will be used.
281
+
282
+ Parameters
283
+ ----------
284
+ plot_kwargs : dict
285
+ Keyword arguments to pass to the `plot` method of the axis.
286
+ This can include line style, color, etc.
287
+ """
288
+ if "ax" in plot_kwargs:
289
+ ax = plot_kwargs["ax"]
290
+ del plot_kwargs["ax"]
291
+ else:
292
+ ax = plt.gca()
293
+ ax.plot(self.alphas, self.alphas, **plot_kwargs)
294
+ ax.set_xlabel(r"$\alpha$")
295
+ ax.set_ylabel("Pressure [bar]")
296
+ for cp in self.cp:
297
+ ax.scatter(self.pressures[cp], self.alphas[cp], color="black")
298
+
299
+ def __getitem__(self, key):
300
+ if "key" in self.__dict__:
301
+ return self.__dict__["key"]
302
+ elif isinstance(key, np.ndarray) or isinstance(key, list):
303
+ return PXEnvelope(
304
+ global_composition_0=self.global_composition_0,
305
+ global_composition_i=self.global_composition_i,
306
+ temperature=self.temperature,
307
+ main_phases_compositions=self.main_phases_compositions[key],
308
+ reference_phase_compositions=self.reference_phase_compositions[
309
+ key
310
+ ],
311
+ main_phases_molar_fractions=self.main_phases_molar_fractions[
312
+ key
313
+ ],
314
+ pressures=self.pressures[key],
315
+ alphas=self.alphas[key],
316
+ iterations=self.iterations[key],
317
+ specified_variable=self.specified_variable[key],
318
+ critical_pressures=self.critical_pressures,
319
+ critical_alphas=self.critical_alphas,
320
+ )
321
+ elif key == "alpha" or key == "a":
322
+ return self.alphas
323
+ elif key == "P":
324
+ return self.pressures
325
+ elif key == "z":
326
+ return self.global_composition
327
+ elif key == "x":
328
+ return self.main_phases_compositions
329
+ elif key == "w":
330
+ return self.reference_phase_compositions
331
+
332
+ def __repr__(self):
333
+ display(self.df)
334
+ return ""
335
+
336
+ def __len__(self):
337
+ return len(self.alphas)
338
+
339
+
340
+ class TXEnvelope:
341
+ """TXEnvelope.
342
+
343
+ This class represents a temperature-composition envelope.
344
+
345
+ Attributes
346
+ ----------
347
+ pressure : float
348
+ The pressure at which the envelope is defined. [bar]
349
+ global_composition_0 : np.ndarray
350
+ The global composition at the point where :math:`\alpha = 0`.
351
+ global_composition_i : np.ndarray
352
+ The global composition at the point where :math:`\alpha = 1`.
353
+ main_phases_compositions : np.ndarray
354
+ The compositions of the main phases along the envelope.
355
+ Shape is (n_points, n_phases, n_components).
356
+ reference_phase_compositions : np.ndarray
357
+ The compositions of the reference phase along the envelope.
358
+ Shape is (n_points, n_components).
359
+ main_phases_molar_fractions : np.ndarray
360
+ The molar fractions of the main phases along the envelope.
361
+ Shape is (n_points, n_phases).
362
+ temperatures : np.ndarray
363
+ The temperatures along the envelope. [K]
364
+ alphas : np.ndarray
365
+ The molar fraction of the `global_composition_i`, :math:`\alpha`.
366
+ Shape is (n_points,).
367
+ iterations : np.ndarray
368
+ The number of iterations taken to compute the envelope at each point.
369
+ Shape is (n_points,).
370
+ specified_variable : np.ndarray
371
+ The specified variable used to compute the envelope at each point.
372
+ Shape is (n_points,).
373
+ temperature : float
374
+ The temperatures along envelope. [K]
375
+ critical_temperatures : np.ndarray
376
+ The critical temperatures of the envelope. Shape is (n_critical_points,).
377
+ critical_alphas : np.ndarray
378
+ The molar fractions of the `global_composition_i` at the critical
379
+ points, :math:`\alpha`. Shape is (n_critical_points,).
380
+ df : pd.DataFrame
381
+ A DataFrame containing the data of the envelope. The columns are:
382
+ - 'alpha': Molar fraction of the `global_composition_i`.
383
+ - 'T': Temperatures along the envelope.
384
+ - 'x_i^j': Compositions of the main phases, where `i`
385
+ is the component index and `j` is the phase index.
386
+ - 'w_i': Compositions of the reference phase, where `i` is the
387
+ component index.
388
+ - 'beta^j': Molar fractions of the main phases, where `j`
389
+ is the phase index.
390
+ """
391
+
392
+ def __init__(
393
+ self,
394
+ pressure,
395
+ global_composition_0,
396
+ global_composition_i,
397
+ main_phases_compositions,
398
+ reference_phase_compositions,
399
+ main_phases_molar_fractions,
400
+ temperatures,
401
+ alphas,
402
+ iterations,
403
+ specified_variable,
404
+ critical_temperatures,
405
+ critical_alphas,
406
+ main_phases_kinds,
407
+ reference_phase_kinds,
408
+ ):
409
+
410
+ msk = ~np.isnan(temperatures)
411
+ msk_cp = ~np.isnan(critical_temperatures)
412
+
413
+ self.temperature = pressure
414
+ self.number_of_components = len(global_composition_0)
415
+ self.number_of_phases = main_phases_compositions.shape[1]
416
+ self.global_composition_0 = global_composition_0
417
+ self.global_composition_i = global_composition_i
418
+ self.main_phases_compositions = main_phases_compositions[msk, :, :]
419
+ self.reference_phase_compositions = reference_phase_compositions[
420
+ msk, :
421
+ ]
422
+ self.main_phases_molar_fractions = main_phases_molar_fractions[msk]
423
+ self.temperatures = temperatures[msk]
424
+ self.alphas = alphas[msk]
425
+ self.iterations = iterations[msk]
426
+ self.specified_variable = specified_variable[msk]
427
+ self.critical_temperatures = critical_temperatures[msk_cp]
428
+ self.critical_alphas = critical_alphas[msk_cp]
429
+ self.main_phases_kinds = main_phases_kinds[msk]
430
+ self.reference_phase_kinds = reference_phase_kinds[msk]
431
+
432
+ df = pd.DataFrame()
433
+
434
+ df["alpha"] = self.alphas
435
+ df["T"] = self.temperatures
436
+
437
+ for i in range(self.number_of_components):
438
+ for j in range(self.number_of_phases):
439
+ df[f"x_{i+1}^{j+1}"] = self.main_phases_compositions[:, j, i]
440
+ df[f"w_{i+1}"] = self.reference_phase_compositions[:, i]
441
+
442
+ for i in range(self.number_of_phases):
443
+ df[f"beta^{i+1}"] = self.main_phases_molar_fractions[:, i]
444
+
445
+ self.df = df
446
+
447
+ def plot(self, **plot_kwargs):
448
+ """Plot the envelope.
449
+
450
+ Plot the envelope in a matplotlib axis. Using the natural variables of
451
+ the envelope (:math:`alpha` and temperature) If the `ax` keyword
452
+ argument is provided, it will be used as the axis to plot on.
453
+ Otherwise, the current axis will be used.
454
+
455
+ Parameters
456
+ ----------
457
+ plot_kwargs : dict
458
+ Keyword arguments to pass to the `plot` method of the axis.
459
+ This can include line style, color, etc.
460
+ """
461
+ if "ax" in plot_kwargs:
462
+ ax = plot_kwargs["ax"]
463
+ del plot_kwargs["ax"]
464
+ else:
465
+ ax = plt.gca()
466
+ ax.plot(self.alphas, self.temperatures, **plot_kwargs)
467
+ ax.set_xlabel(r"$\alpha$")
468
+ ax.set_ylabel("Temperature [K]")
469
+ for cp in self.cp:
470
+ ax.scatter(self.alphas[cp], self.temperatures[cp], color="black")
471
+
472
+ def __getitem__(self, key):
473
+ if "key" in self.__dict__:
474
+ return self.__dict__["key"]
475
+ elif isinstance(key, np.ndarray) or isinstance(key, list):
476
+ return TXEnvelope(
477
+ global_composition_0=self.global_composition_0,
478
+ global_composition_i=self.global_composition_i,
479
+ pressure=self.pressure,
480
+ main_phases_compositions=self.main_phases_compositions[key],
481
+ reference_phase_compositions=self.reference_phase_compositions[
482
+ key
483
+ ],
484
+ main_phases_molar_fractions=self.main_phases_molar_fractions[
485
+ key
486
+ ],
487
+ temperatures=self.temperatures[key],
488
+ alphas=self.alphas[key],
489
+ iterations=self.iterations[key],
490
+ specified_variable=self.specified_variable[key],
491
+ critical_temperatures=self.critical_temperatures,
492
+ critical_alphas=self.critical_alphas,
493
+ )
494
+ elif key == "alpha" or key == "a":
495
+ return self.alphas
496
+ elif key == "T":
497
+ return self.temperatures
498
+ elif key == "z":
499
+ return self.global_composition
500
+ elif key == "x":
501
+ return self.main_phases_compositions
502
+ elif key == "w":
503
+ return self.reference_phase_compositions
504
+
505
+ def __repr__(self):
506
+ display(self.df)
507
+ return ""
508
+
509
+ def __len__(self):
510
+ return len(self.alphas)
@@ -0,0 +1,12 @@
1
+ """yaeos fitting module.
2
+
3
+ This module provides classes and functions for fitting binary interaction
4
+ parameters to experimental data.
5
+ """
6
+
7
+ from yaeos.fitting.core import BinaryFitter
8
+ from yaeos.fitting.model_setters import fit_kij_lij
9
+ from yaeos.fitting.solvers import solve_pt
10
+
11
+
12
+ __all__ = ["BinaryFitter", "fit_kij_lij", "solve_pt"]