cosmopharm 0.0.26__py3-none-any.whl → 0.0.27__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.
@@ -1,36 +1,36 @@
1
- import pandas as pd
2
- import numpy as np
3
- from ..components import Component
4
-
5
- def read_params(params_filepath):
6
- params = pd.read_excel(params_filepath)
7
- cols = [col.split('/')[0] for col in params.columns]
8
- params.rename(columns=dict(zip(params.columns, cols)), inplace=True)
9
- return params
10
-
11
- def get_params_for_component(component, parameters):
12
- """Retrieve the parameters for the given component from the dataframe."""
13
- params = parameters[parameters['Component'] == component.name]
14
- if params.empty:
15
- raise ValueError(f"No data found for component {component.name}")
16
- return params.iloc[0]
17
-
18
- def add_parameters(c, params):
19
- """Add properties of a component based on provided parameters."""
20
- KILOJOULE_TO_JOULE = 1e3
21
- c.Mw = params['Mw'] # g/mol
22
- c.T_fus = params['T_fus'] if params['T_fus'] > 0 else np.nan # K
23
- c.H_fus = params['H_fus'] * KILOJOULE_TO_JOULE # J/mol
24
- c.Cp_fus_A = np.nan_to_num(params['Cp_fus_a_fit']) # J/(mol K)
25
- c.Cp_fus_BT = np.nan_to_num(params['Cp_fus_bT_fit']) # J/(mol K²)
26
- c.v_298 = params['v298'] # cm³/mol
27
- c.v_hc = params['v_hc'] # cm³/mol
28
-
29
- def create_components(names, parameters):
30
- components = []
31
- for name in names:
32
- component = Component(name)
33
- params = get_params_for_component(component, parameters)
34
- add_parameters(component, params)
35
- components.append(component)
36
- return components
1
+ import pandas as pd
2
+ import numpy as np
3
+ from ..components import Component
4
+
5
+ def read_params(params_filepath):
6
+ params = pd.read_excel(params_filepath)
7
+ cols = [col.split('/')[0] for col in params.columns]
8
+ params.rename(columns=dict(zip(params.columns, cols)), inplace=True)
9
+ return params
10
+
11
+ def get_params_for_component(component, parameters):
12
+ """Retrieve the parameters for the given component from the dataframe."""
13
+ params = parameters[parameters['Component'] == component.name]
14
+ if params.empty:
15
+ raise ValueError(f"No data found for component {component.name}")
16
+ return params.iloc[0]
17
+
18
+ def add_parameters(c, params):
19
+ """Add properties of a component based on provided parameters."""
20
+ KILOJOULE_TO_JOULE = 1e3
21
+ c.Mw = params['Mw'] # g/mol
22
+ c.T_fus = params['T_fus'] if params['T_fus'] > 0 else np.nan # K
23
+ c.H_fus = params['H_fus'] * KILOJOULE_TO_JOULE # J/mol
24
+ c.Cp_fus_A = np.nan_to_num(params['Cp_fus_a_fit']) # J/(mol K)
25
+ c.Cp_fus_BT = np.nan_to_num(params['Cp_fus_bT_fit']) # J/(mol K²)
26
+ c.v_298 = params['v298'] # cm³/mol
27
+ c.v_hc = params['v_hc'] # cm³/mol
28
+
29
+ def create_components(names, parameters):
30
+ components = []
31
+ for name in names:
32
+ component = Component(name)
33
+ params = get_params_for_component(component, parameters)
34
+ add_parameters(component, params)
35
+ components.append(component)
36
+ return components
@@ -1,188 +1,188 @@
1
- import numpy as np
2
- from scipy.optimize import fsolve, root
3
- from scipy.signal import argrelextrema
4
-
5
- def find_inflection_points(x, y):
6
- """
7
- Determines the inflection points of the function based on second derivative.
8
-
9
- Parameters:
10
- - x, y: data points
11
-
12
- Returns:
13
- - x and y values of turning points and their indices.
14
- """
15
- dy = np.diff(y) / np.diff(x)
16
- dx = (x[:-1] + x[1:]) / 2
17
- d2y = np.diff(dy) / np.diff(dx)
18
- sign_changes = np.diff(np.sign(d2y))
19
- i = np.where(sign_changes)[0] + 2
20
- return i, x[i], y[i]
21
-
22
- def find_common_tangent(f, g, xL_init, xR_init):
23
- """
24
- Determines the common tangent for two functions.
25
-
26
- Parameters:
27
- - f, g: Polynomial functions
28
- - xL_init, xR_init: Initial guesses for intersection points
29
-
30
- Returns:
31
- - Intersection points and y-values.
32
- """
33
- def fobj(x):
34
- xL, xR = x
35
- df = f.deriv()(xL)
36
- dg = g.deriv()(xR)
37
- dy = (g(xR) - f(xL)) / (xR - xL)
38
- return [df - dg, dy - df]
39
- xL, xR = fsolve(fobj, x0=[xL_init, xR_init])
40
- # [xL, xR], info, *_ = fsolve(fobj, x0=[xL_init, xR_init], full_output=True)
41
- # print(info['nfev'])
42
- # kwargs = dict(method='krylov', options={
43
- # 'maxiter': 20, 'xtol': 1e-3})
44
- # res = root(fobj, x0=[xL_init, xR_init], **kwargs)
45
- # [xL, xR] = res.x
46
- # print(res.nit)
47
- return xL, xR, f(xL), g(xR)
48
-
49
- def approximate_between_points(x, y, start, end, deg=5):
50
- segment_x = x[start:end+1]
51
- segment_y = y[start:end+1]
52
- midpoint = (segment_x[0] + segment_x[-1]) / 2
53
- params = np.polyfit(segment_x, segment_y, deg=deg)
54
- func = np.poly1d(params)
55
- return func, segment_x, midpoint
56
-
57
- def get_segment_border_indices(x, start, end, fraction=0.5, min_values=5):
58
- # Calculate the intermediate point using the given fraction
59
- intermediate_x = x[start] + fraction * (x[end] - x[start])
60
- # Find the index in x that is closest to this intermediate point
61
- # closest_index = start + np.argmin(np.abs(x[start:end+1] - intermediate_x))
62
- closest_index = np.argmin(np.abs(x - intermediate_x))
63
- # Ensure that there are at least min_values between start and closest_index
64
- if abs(closest_index - start) < min_values:
65
- closest_index = end
66
- return sorted([start, closest_index])
67
-
68
- def find_binodal(x1, gmix, iL, iR, ax=None):
69
- # Approximate segments between pure component and inflection points
70
- borderL = get_segment_border_indices(x1, start=iL, end=0)
71
- borderR = get_segment_border_indices(x1, start=iR, end=-1)
72
- f, xL_range, mL = approximate_between_points(x1, gmix, *borderL, deg=5)
73
- g, xR_range, mR = approximate_between_points(x1, gmix, *borderR, deg=5)
74
- # Find common tangent
75
- xL, xR, yL, yR = find_common_tangent(f, g, mL, mR)
76
-
77
- # Check if results are outside the fitting range
78
- adjustL = xL < xL_range[0] or xL > xL_range[-1]
79
- adjustR = xR < xR_range[0] or xR > xR_range[-1]
80
-
81
- # If outside, adjust the respective range and recalculate xL and xR
82
- if adjustL or adjustR:
83
- if adjustL:
84
- borderL = get_segment_border_indices(
85
- x1, start=iL, end=0, fraction=1)
86
- f, xL_range, mL = approximate_between_points(
87
- x1, gmix, *borderL, deg=5)
88
- if adjustR:
89
- borderR = get_segment_border_indices(
90
- x1, start=iR, end=-1, fraction=1)
91
- g, xR_range, mR = approximate_between_points(
92
- x1, gmix, *borderR, deg=10)
93
-
94
- # Find common tangent
95
- xL, xR, yL, yR = find_common_tangent(f, g, mL, mR)
96
-
97
- if ax is not None:
98
- ax.plot(xL_range, f(xL_range), 'r', lw=1)
99
- ax.plot(xR_range, g(xR_range), 'r', lw=1)
100
-
101
- return xL, xR, yL, yR
102
-
103
- def find_local_extremum(y, typ='minimum'):
104
- kind = np.less if "min" in typ else np.greater
105
- return argrelextrema(y, kind)[0]
106
-
107
- def approx_binodal(x1, gmix, xS, iL, iR):
108
- x, y = x1, gmix
109
- local_minima = find_local_extremum(y, 'minimum')
110
- min_x = x[local_minima]
111
-
112
- # Initialize return values
113
- xL = xR = yL = yR = None
114
-
115
- if len(min_x) == 0:
116
- pass
117
- # Check the number of local minima found
118
- if len(min_x) == 1:
119
- # Precompute the boolean condition to avoid redundancy
120
- is_greater_than_mid_x = min_x > max(xS)
121
- # Select indices based on the precomputed condition
122
- i = iL if is_greater_than_mid_x else iR
123
- slice_ = slice(None, i+1) if is_greater_than_mid_x else slice(i, None)
124
-
125
- # Slicing the arrays based on the selected indices
126
- x1, y1 = x[slice_], y[slice_]
127
-
128
- # Fit a line to the boundary points
129
- m, n = np.polyfit(x1[[0, -1]], y1[[0, -1]], 1)
130
- f = np.poly1d([m, n])
131
-
132
- # Calculate the difference for finding the local minima
133
- diff = y1 - f(x1)
134
- local_minima = find_local_extremum(diff, 'minimum')
135
-
136
- # Adjust local_minima index based on the original x array
137
- if not is_greater_than_mid_x:
138
- local_minima += i # Adjust index for the slice offset
139
-
140
- # Check if new local minima were found and sort
141
- if local_minima.size > 0:
142
- # Sort to ensure xL < xR
143
- xL, xR = np.sort([min_x[0], x[local_minima][0]])
144
- yL, yR = y[np.where((x == xL) | (x == xR))]
145
-
146
- elif len(min_x) == 2:
147
- xL, xR = min_x
148
- yL, yR = y[local_minima]
149
-
150
- else:
151
- pass
152
-
153
- return xL, xR, yL, yR
154
-
155
-
156
- def estimate_lle_from_gmix(x1, gmix, rough=True, ax=None):
157
- # Initialize return values
158
- xL = xR = yL = yR = None
159
-
160
- # Find inflection points
161
- try:
162
- idx, xS, yS = find_inflection_points(x1, gmix)
163
- iL, iR = idx[:2]
164
- except ValueError:
165
- # print("Warning: No inflection points found for current iteration.")
166
- return xL, xR, yL, yR
167
-
168
- # Find binodal
169
- if rough:
170
- xL, xR, yL, yR = approx_binodal(x1, gmix, xS, iL, iR)
171
- else:
172
- xL, xR, yL, yR = find_binodal(x1, gmix, iL, iR, ax=ax)
173
-
174
- # Round xL and xR if they are not None
175
- xL = round(xL, 8) if xL is not None else None
176
- xR = round(xR, 8) if xR is not None else None
177
-
178
- if ax is not None and None not in (xL, xR, yL, yR):
179
- # Plot common tangent
180
- m = (yR-yL)/(xR-xL)
181
- n = yR - m*xR
182
- t = np.poly1d([m, n])
183
- ylim = ax.get_ylim()
184
- ax.plot([xL, xR], [yL, yR], 'ko', mfc='w', zorder=3)
185
- ax.plot(x1, t(x1), 'k:')
186
- ax.set_ylim(ylim)
187
-
188
- return xL, xR, yL, yR
1
+ import numpy as np
2
+ from scipy.optimize import fsolve, root
3
+ from scipy.signal import argrelextrema
4
+
5
+ def find_inflection_points(x, y):
6
+ """
7
+ Determines the inflection points of the function based on second derivative.
8
+
9
+ Parameters:
10
+ - x, y: data points
11
+
12
+ Returns:
13
+ - x and y values of turning points and their indices.
14
+ """
15
+ dy = np.diff(y) / np.diff(x)
16
+ dx = (x[:-1] + x[1:]) / 2
17
+ d2y = np.diff(dy) / np.diff(dx)
18
+ sign_changes = np.diff(np.sign(d2y))
19
+ i = np.where(sign_changes)[0] + 2
20
+ return i, x[i], y[i]
21
+
22
+ def find_common_tangent(f, g, xL_init, xR_init):
23
+ """
24
+ Determines the common tangent for two functions.
25
+
26
+ Parameters:
27
+ - f, g: Polynomial functions
28
+ - xL_init, xR_init: Initial guesses for intersection points
29
+
30
+ Returns:
31
+ - Intersection points and y-values.
32
+ """
33
+ def fobj(x):
34
+ xL, xR = x
35
+ df = f.deriv()(xL)
36
+ dg = g.deriv()(xR)
37
+ dy = (g(xR) - f(xL)) / (xR - xL)
38
+ return [df - dg, dy - df]
39
+ xL, xR = fsolve(fobj, x0=[xL_init, xR_init])
40
+ # [xL, xR], info, *_ = fsolve(fobj, x0=[xL_init, xR_init], full_output=True)
41
+ # print(info['nfev'])
42
+ # kwargs = dict(method='krylov', options={
43
+ # 'maxiter': 20, 'xtol': 1e-3})
44
+ # res = root(fobj, x0=[xL_init, xR_init], **kwargs)
45
+ # [xL, xR] = res.x
46
+ # print(res.nit)
47
+ return xL, xR, f(xL), g(xR)
48
+
49
+ def approximate_between_points(x, y, start, end, deg=5):
50
+ segment_x = x[start:end+1]
51
+ segment_y = y[start:end+1]
52
+ midpoint = (segment_x[0] + segment_x[-1]) / 2
53
+ params = np.polyfit(segment_x, segment_y, deg=deg)
54
+ func = np.poly1d(params)
55
+ return func, segment_x, midpoint
56
+
57
+ def get_segment_border_indices(x, start, end, fraction=0.5, min_values=5):
58
+ # Calculate the intermediate point using the given fraction
59
+ intermediate_x = x[start] + fraction * (x[end] - x[start])
60
+ # Find the index in x that is closest to this intermediate point
61
+ # closest_index = start + np.argmin(np.abs(x[start:end+1] - intermediate_x))
62
+ closest_index = np.argmin(np.abs(x - intermediate_x))
63
+ # Ensure that there are at least min_values between start and closest_index
64
+ if abs(closest_index - start) < min_values:
65
+ closest_index = end
66
+ return sorted([start, closest_index])
67
+
68
+ def find_binodal(x1, gmix, iL, iR, ax=None):
69
+ # Approximate segments between pure component and inflection points
70
+ borderL = get_segment_border_indices(x1, start=iL, end=0)
71
+ borderR = get_segment_border_indices(x1, start=iR, end=-1)
72
+ f, xL_range, mL = approximate_between_points(x1, gmix, *borderL, deg=5)
73
+ g, xR_range, mR = approximate_between_points(x1, gmix, *borderR, deg=5)
74
+ # Find common tangent
75
+ xL, xR, yL, yR = find_common_tangent(f, g, mL, mR)
76
+
77
+ # Check if results are outside the fitting range
78
+ adjustL = xL < xL_range[0] or xL > xL_range[-1]
79
+ adjustR = xR < xR_range[0] or xR > xR_range[-1]
80
+
81
+ # If outside, adjust the respective range and recalculate xL and xR
82
+ if adjustL or adjustR:
83
+ if adjustL:
84
+ borderL = get_segment_border_indices(
85
+ x1, start=iL, end=0, fraction=1)
86
+ f, xL_range, mL = approximate_between_points(
87
+ x1, gmix, *borderL, deg=5)
88
+ if adjustR:
89
+ borderR = get_segment_border_indices(
90
+ x1, start=iR, end=-1, fraction=1)
91
+ g, xR_range, mR = approximate_between_points(
92
+ x1, gmix, *borderR, deg=10)
93
+
94
+ # Find common tangent
95
+ xL, xR, yL, yR = find_common_tangent(f, g, mL, mR)
96
+
97
+ if ax is not None:
98
+ ax.plot(xL_range, f(xL_range), 'r', lw=1)
99
+ ax.plot(xR_range, g(xR_range), 'r', lw=1)
100
+
101
+ return xL, xR, yL, yR
102
+
103
+ def find_local_extremum(y, typ='minimum'):
104
+ kind = np.less if "min" in typ else np.greater
105
+ return argrelextrema(y, kind)[0]
106
+
107
+ def approx_binodal(x1, gmix, xS, iL, iR):
108
+ x, y = x1, gmix
109
+ local_minima = find_local_extremum(y, 'minimum')
110
+ min_x = x[local_minima]
111
+
112
+ # Initialize return values
113
+ xL = xR = yL = yR = None
114
+
115
+ if len(min_x) == 0:
116
+ pass
117
+ # Check the number of local minima found
118
+ if len(min_x) == 1:
119
+ # Precompute the boolean condition to avoid redundancy
120
+ is_greater_than_mid_x = min_x > max(xS)
121
+ # Select indices based on the precomputed condition
122
+ i = iL if is_greater_than_mid_x else iR
123
+ slice_ = slice(None, i+1) if is_greater_than_mid_x else slice(i, None)
124
+
125
+ # Slicing the arrays based on the selected indices
126
+ x1, y1 = x[slice_], y[slice_]
127
+
128
+ # Fit a line to the boundary points
129
+ m, n = np.polyfit(x1[[0, -1]], y1[[0, -1]], 1)
130
+ f = np.poly1d([m, n])
131
+
132
+ # Calculate the difference for finding the local minima
133
+ diff = y1 - f(x1)
134
+ local_minima = find_local_extremum(diff, 'minimum')
135
+
136
+ # Adjust local_minima index based on the original x array
137
+ if not is_greater_than_mid_x:
138
+ local_minima += i # Adjust index for the slice offset
139
+
140
+ # Check if new local minima were found and sort
141
+ if local_minima.size > 0:
142
+ # Sort to ensure xL < xR
143
+ xL, xR = np.sort([min_x[0], x[local_minima][0]])
144
+ yL, yR = y[np.where((x == xL) | (x == xR))]
145
+
146
+ elif len(min_x) == 2:
147
+ xL, xR = min_x
148
+ yL, yR = y[local_minima]
149
+
150
+ else:
151
+ pass
152
+
153
+ return xL, xR, yL, yR
154
+
155
+
156
+ def estimate_lle_from_gmix(x1, gmix, rough=True, ax=None):
157
+ # Initialize return values
158
+ xL = xR = yL = yR = None
159
+
160
+ # Find inflection points
161
+ try:
162
+ idx, xS, yS = find_inflection_points(x1, gmix)
163
+ iL, iR = idx[:2]
164
+ except ValueError:
165
+ # print("Warning: No inflection points found for current iteration.")
166
+ return xL, xR, yL, yR
167
+
168
+ # Find binodal
169
+ if rough:
170
+ xL, xR, yL, yR = approx_binodal(x1, gmix, xS, iL, iR)
171
+ else:
172
+ xL, xR, yL, yR = find_binodal(x1, gmix, iL, iR, ax=ax)
173
+
174
+ # Round xL and xR if they are not None
175
+ xL = round(xL, 8) if xL is not None else None
176
+ xR = round(xR, 8) if xR is not None else None
177
+
178
+ if ax is not None and None not in (xL, xR, yL, yR):
179
+ # Plot common tangent
180
+ m = (yR-yL)/(xR-xL)
181
+ n = yR - m*xR
182
+ t = np.poly1d([m, n])
183
+ ylim = ax.get_ylim()
184
+ ax.plot([xL, xR], [yL, yR], 'ko', mfc='w', zorder=3)
185
+ ax.plot(x1, t(x1), 'k:')
186
+ ax.set_ylim(ylim)
187
+
188
+ return xL, xR, yL, yR