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.
- cosmopharm/__init__.py +2 -2
- cosmopharm/actmodels/actmodel.py +118 -118
- cosmopharm/actmodels/cosmo.py +152 -152
- cosmopharm/components.py +31 -31
- cosmopharm/equilibrium/__init__.py +2 -2
- cosmopharm/equilibrium/lle.py +178 -178
- cosmopharm/equilibrium/sle.py +244 -244
- cosmopharm/utils/__init__.py +3 -3
- cosmopharm/utils/convert.py +58 -58
- cosmopharm/utils/helpers.py +36 -36
- cosmopharm/utils/lle_scanner.py +188 -188
- cosmopharm/utils/spacing.py +246 -246
- {cosmopharm-0.0.26.dist-info → cosmopharm-0.0.27.dist-info}/LICENSE +20 -20
- {cosmopharm-0.0.26.dist-info → cosmopharm-0.0.27.dist-info}/METADATA +150 -150
- cosmopharm-0.0.27.dist-info/RECORD +18 -0
- {cosmopharm-0.0.26.dist-info → cosmopharm-0.0.27.dist-info}/WHEEL +1 -1
- cosmopharm-0.0.26.dist-info/RECORD +0 -18
- {cosmopharm-0.0.26.dist-info → cosmopharm-0.0.27.dist-info}/top_level.txt +0 -0
cosmopharm/utils/helpers.py
CHANGED
@@ -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
|
cosmopharm/utils/lle_scanner.py
CHANGED
@@ -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
|