pharmapy-org 0.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.
- PharmaPy/CakePhysics.py +117 -0
- PharmaPy/Calibration.py +300 -0
- PharmaPy/CheckModule.py +70 -0
- PharmaPy/Classifier.py +275 -0
- PharmaPy/Commons.py +718 -0
- PharmaPy/Connections.py +581 -0
- PharmaPy/Containers.py +1006 -0
- PharmaPy/Crystallizers.py +2229 -0
- PharmaPy/Crystallizers_Refactored.py +215 -0
- PharmaPy/DataClasses.py +529 -0
- PharmaPy/Distillation.py +905 -0
- PharmaPy/Distributions.py +142 -0
- PharmaPy/Drying_Model.py +734 -0
- PharmaPy/DynamicExtraction.py +600 -0
- PharmaPy/Errors.py +23 -0
- PharmaPy/Evaporators.py +1972 -0
- PharmaPy/Extractors.py +305 -0
- PharmaPy/Gaussians.py +138 -0
- PharmaPy/IntegratorBackends.py +1885 -0
- PharmaPy/Interpolation.py +305 -0
- PharmaPy/Kinetics.py +1078 -0
- PharmaPy/LevMarq.py +176 -0
- PharmaPy/Mechanisms.py +2034 -0
- PharmaPy/MetaModeler.py +273 -0
- PharmaPy/MixedPhases.py +648 -0
- PharmaPy/MixedPhases_Refactored.py +366 -0
- PharmaPy/MultiPhaseVessel.py +3204 -0
- PharmaPy/NameAnalysis.py +368 -0
- PharmaPy/ParamEstim.py +1382 -0
- PharmaPy/Phases.py +1227 -0
- PharmaPy/Phases_Refactored.py +1206 -0
- PharmaPy/Plotting.py +386 -0
- PharmaPy/ProcessControl.py +64 -0
- PharmaPy/ProcessControl_Refactored.py +491 -0
- PharmaPy/Reactors.py +2071 -0
- PharmaPy/Reactors_Refactored.py +113 -0
- PharmaPy/Results.py +345 -0
- PharmaPy/SimExec.py +719 -0
- PharmaPy/SolidLiquidSep.py +1299 -0
- PharmaPy/SolidLiquidSep_Refactored.py +871 -0
- PharmaPy/StatsModule.py +508 -0
- PharmaPy/Streams.py +287 -0
- PharmaPy/Streams_Refactored.py +582 -0
- PharmaPy/ThermoModule.py +967 -0
- PharmaPy/ThreePhaseSettler.py +236 -0
- PharmaPy/Utilities.py +71 -0
- PharmaPy/__init__.py +0 -0
- PharmaPy/_assimulo.py +203 -0
- PharmaPy/animate_profiles.py +255 -0
- PharmaPy/data/evaporator/props_nitrogen.json +18 -0
- PharmaPy/data/minimum_modeling_objects.json +7 -0
- PharmaPy/data/thermodynamics/unifac_interaction_params.csv +757 -0
- PharmaPy/data/thermodynamics/unifac_rk_qk.csv +126 -0
- PharmaPy/general_interpolation.py +29 -0
- PharmaPy/jac_module.py +184 -0
- pharmapy_org-0.1.0.dist-info/METADATA +64 -0
- pharmapy_org-0.1.0.dist-info/RECORD +60 -0
- pharmapy_org-0.1.0.dist-info/WHEEL +5 -0
- pharmapy_org-0.1.0.dist-info/licenses/LICENSE.md +41 -0
- pharmapy_org-0.1.0.dist-info/top_level.txt +1 -0
PharmaPy/CakePhysics.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Cake physics correlations shared by the solid-liquid separation units.
|
|
4
|
+
|
|
5
|
+
These four functions carry no state and belong to no class. They were defined
|
|
6
|
+
at the top of ``SolidLiquidSep`` and imported from there by ``Drying_Model``,
|
|
7
|
+
which made a legacy module part of another module's public API. Keeping them
|
|
8
|
+
here lets the legacy units, the refactored ones and the dryer draw on one copy
|
|
9
|
+
instead of drifting apart.
|
|
10
|
+
|
|
11
|
+
``SolidLiquidSep`` re-exports every name below, so the original import path
|
|
12
|
+
still works.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
eps = np.finfo(float).eps * 1.1
|
|
18
|
+
grav = 9.8 # m/s**2
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def high_resolution_fvm(f, boundary_cond, limiter_type='Van Leer'):
|
|
22
|
+
|
|
23
|
+
# Ghost cells -1, 0 and N + 1 (see LeVeque 2002, Chapter 9)
|
|
24
|
+
f_extrap = 2*f[-1] - f[-2]
|
|
25
|
+
f_aug = np.concatenate(([boundary_cond]*2, f, [f_extrap]))
|
|
26
|
+
|
|
27
|
+
f_diff = np.diff(f_aug, axis=0)
|
|
28
|
+
|
|
29
|
+
theta = (f_diff[:-1]) / (f_diff[1:] + eps)
|
|
30
|
+
|
|
31
|
+
if limiter_type == 'Van Leer':
|
|
32
|
+
limiter = (np.abs(theta) + theta) / (1 + np.abs(theta))
|
|
33
|
+
else: # TODO: include more limiters
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
fluxes = f_aug[1:-1] + 0.5 * f_diff[1:] * limiter
|
|
37
|
+
|
|
38
|
+
return fluxes
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def upwind_fvm(f, boundary_cond):
|
|
42
|
+
f_aug = np.concatenate(([boundary_cond], f))
|
|
43
|
+
|
|
44
|
+
return f_aug
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_alpha(solid_phase, porosity, sphericity, rho_sol, csd=None):
|
|
48
|
+
# if csd is None:
|
|
49
|
+
# csd = solid_phase.distrib
|
|
50
|
+
|
|
51
|
+
# x_grid = solid_phase.x_distrib
|
|
52
|
+
|
|
53
|
+
# alpha_x = 180 * (1 - porosity) / \
|
|
54
|
+
# (porosity**3 * (x_grid*1e-6)**2 * rho_sol * sphericity**2)
|
|
55
|
+
|
|
56
|
+
# numerator = trapezoidal_rule(x_grid, csd * alpha_x)
|
|
57
|
+
# denominator = solid_phase.moments[0]
|
|
58
|
+
|
|
59
|
+
# alpha = numerator / (denominator + eps)
|
|
60
|
+
csd = solid_phase.distrib
|
|
61
|
+
rho_sol = solid_phase.getDensity()
|
|
62
|
+
x_grid = solid_phase.x_distrib * 1e-6
|
|
63
|
+
|
|
64
|
+
kv = 0.524 # converting number based CSD to volume based:
|
|
65
|
+
|
|
66
|
+
del_x_dist = np.diff(x_grid)
|
|
67
|
+
node_x_dist = (x_grid[:-1] + x_grid[1:]) / 2
|
|
68
|
+
node_CSD = (csd[:-1] + csd[1:]) / 2
|
|
69
|
+
|
|
70
|
+
# Volume of crystals in each bin
|
|
71
|
+
vol_cry = node_CSD * del_x_dist * (kv * node_x_dist**3)
|
|
72
|
+
frac_vol_cry = vol_cry / (np.sum(vol_cry) + eps)
|
|
73
|
+
|
|
74
|
+
csd = vol_cry
|
|
75
|
+
|
|
76
|
+
# Calculate irreducible saturation in weighted csd (volume based)
|
|
77
|
+
vol_frac = vol_cry/ np.sum(vol_cry)
|
|
78
|
+
x_grid = node_x_dist
|
|
79
|
+
alpha_x = 180 * (1 - porosity) / porosity**3 / x_grid**2 / rho_sol
|
|
80
|
+
alpha = np.sum(alpha_x * vol_frac)
|
|
81
|
+
|
|
82
|
+
return alpha
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_sat_inf(x_vec, csd, deltaP, porosity, height, mu_zero, props):
|
|
86
|
+
surf_tens, rho_liq = props
|
|
87
|
+
|
|
88
|
+
kv = 0.524 # converting number based CSD to volume based:
|
|
89
|
+
|
|
90
|
+
del_x_dist = np.diff(x_vec)
|
|
91
|
+
node_x_dist = (x_vec[:-1] + x_vec[1:]) / 2
|
|
92
|
+
node_CSD = (csd[:-1] + csd[1:]) / 2
|
|
93
|
+
|
|
94
|
+
x_vec = node_x_dist
|
|
95
|
+
if isinstance(surf_tens, float) or isinstance(rho_liq, float):
|
|
96
|
+
capillary_number = porosity**3 * x_vec**2 * \
|
|
97
|
+
(rho_liq*grav*height + deltaP) / (1 - porosity)**2 / height / surf_tens
|
|
98
|
+
else:
|
|
99
|
+
capillary_number = np.outer(
|
|
100
|
+
porosity**3 * x_vec**2,
|
|
101
|
+
(rho_liq*grav*height + deltaP)/(1 - porosity)**2 / height / surf_tens
|
|
102
|
+
)
|
|
103
|
+
# Volume of crystals in each bin
|
|
104
|
+
vol_cry = node_CSD * del_x_dist * (kv * node_x_dist**3)
|
|
105
|
+
frac_vol_cry = vol_cry / (np.sum(vol_cry) + eps)
|
|
106
|
+
|
|
107
|
+
csd = vol_cry
|
|
108
|
+
|
|
109
|
+
s_inf = 0.155 * (1 + 0.031*capillary_number**(-0.49))
|
|
110
|
+
s_inf = np.where(s_inf > 1, 1, s_inf)
|
|
111
|
+
|
|
112
|
+
# Calculate irreducible saturation in weighted csd (volume based)
|
|
113
|
+
vol_frac = vol_cry/ np.sum(vol_cry)
|
|
114
|
+
|
|
115
|
+
s_inf = np.sum(vol_frac *s_inf)
|
|
116
|
+
|
|
117
|
+
return s_inf
|
PharmaPy/Calibration.py
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Tue Nov 3 11:29:05 2020
|
|
5
|
+
|
|
6
|
+
@author: dcasasor
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import itertools
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
from matplotlib.ticker import AutoMinorLocator
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PCR_calibration:
|
|
16
|
+
def __init__(self, data, num_comp=None, standardize=True, snv=False,
|
|
17
|
+
y_name=None, y_suffixes=None):
|
|
18
|
+
|
|
19
|
+
self.data = data
|
|
20
|
+
self.standardize = standardize
|
|
21
|
+
self.snv = snv
|
|
22
|
+
|
|
23
|
+
data_mean = data.mean(axis=0)
|
|
24
|
+
data_std = data.std(axis=0)
|
|
25
|
+
|
|
26
|
+
if snv:
|
|
27
|
+
self.data_centered = self.__center_data(data)
|
|
28
|
+
else:
|
|
29
|
+
self.data_centered = self.__center_data(data, data_mean, data_std)
|
|
30
|
+
# self.data_centered = self.__center_data(data, None, None)
|
|
31
|
+
|
|
32
|
+
self.data_mean = data_mean
|
|
33
|
+
self.data_std = data_std
|
|
34
|
+
|
|
35
|
+
(self.projections, self.explained_variance,
|
|
36
|
+
self.svd_dict) = self.__get_projections()
|
|
37
|
+
|
|
38
|
+
if num_comp is None:
|
|
39
|
+
self.num_comp = len(self.svd_dict['sv'])
|
|
40
|
+
else:
|
|
41
|
+
self.num_comp = num_comp
|
|
42
|
+
|
|
43
|
+
if y_name is None:
|
|
44
|
+
y_name = 'y_'
|
|
45
|
+
|
|
46
|
+
self.y_name = y_name
|
|
47
|
+
self.y_suffixes = y_suffixes
|
|
48
|
+
|
|
49
|
+
def __center_data(self, data=None, mean=None, std=None):
|
|
50
|
+
if mean is None and std is None:
|
|
51
|
+
mean = data.mean(axis=0)
|
|
52
|
+
std = data.std(axis=0)
|
|
53
|
+
|
|
54
|
+
data_centered = data - mean
|
|
55
|
+
|
|
56
|
+
if self.snv or self.standardize:
|
|
57
|
+
data_centered *= 1 / std
|
|
58
|
+
|
|
59
|
+
return data_centered
|
|
60
|
+
|
|
61
|
+
def __get_projections(self, data=None, n_comp=None):
|
|
62
|
+
|
|
63
|
+
if data is None:
|
|
64
|
+
data = self.data_centered
|
|
65
|
+
|
|
66
|
+
# Perform SVD
|
|
67
|
+
u_m, sv, v_nt = np.linalg.svd(data)
|
|
68
|
+
v_n = v_nt.T
|
|
69
|
+
|
|
70
|
+
# Percent of explained variance
|
|
71
|
+
explained_var = sv**2 / (sv**2).sum() * 100
|
|
72
|
+
|
|
73
|
+
# Store SVD in a dict
|
|
74
|
+
v_trunc = v_nt[:len(sv)].T
|
|
75
|
+
|
|
76
|
+
svd_dict = {'U': u_m, 'sv': sv, 'V': v_n, 'V_trunc': v_trunc}
|
|
77
|
+
|
|
78
|
+
# Projections
|
|
79
|
+
projections = np.dot(data, v_n)
|
|
80
|
+
|
|
81
|
+
if n_comp is not None:
|
|
82
|
+
projections = projections[:, :n_comp]
|
|
83
|
+
|
|
84
|
+
return projections, explained_var, svd_dict
|
|
85
|
+
|
|
86
|
+
def plot_projections(self, fig_size=None, num_comp=None):
|
|
87
|
+
|
|
88
|
+
if num_comp is None:
|
|
89
|
+
num_comp = self.num_comp
|
|
90
|
+
|
|
91
|
+
comb = itertools.combinations(range(num_comp), 2)
|
|
92
|
+
|
|
93
|
+
combs = [item for item in comb]
|
|
94
|
+
num_plots = len(combs)
|
|
95
|
+
|
|
96
|
+
if num_plots == 1:
|
|
97
|
+
fig, axes = plt.subplots(figsize=fig_size)
|
|
98
|
+
axes = np.atleast_1d(axes)
|
|
99
|
+
else:
|
|
100
|
+
ncols = 2
|
|
101
|
+
nrows = num_plots // ncols + num_plots % ncols
|
|
102
|
+
|
|
103
|
+
fig, axes = plt.subplots(nrows, ncols, figsize=fig_size)
|
|
104
|
+
|
|
105
|
+
axes_flat = axes.flatten()
|
|
106
|
+
num_axes = len(axes_flat)
|
|
107
|
+
for ind in range(num_plots):
|
|
108
|
+
axis = axes_flat[ind]
|
|
109
|
+
pc_one, pc_two = combs[ind]
|
|
110
|
+
|
|
111
|
+
my_map = plt.get_cmap('Reds')
|
|
112
|
+
data_plot = self.projections[:, combs[ind]].T
|
|
113
|
+
axis.scatter(data_plot[0], data_plot[1],
|
|
114
|
+
# 'o', mfc='None',
|
|
115
|
+
s=30/(num_axes/2), c=range(data_plot.shape[1]),
|
|
116
|
+
cmap=my_map,
|
|
117
|
+
marker='o', edgecolor='k')
|
|
118
|
+
|
|
119
|
+
axis.text(0.5, -0.08, 'PC%i' % (pc_one + 1),
|
|
120
|
+
transform=axis.transAxes, ha='center')
|
|
121
|
+
|
|
122
|
+
axis.text(-0.08, 0.5, 'PC%i' % (pc_two + 1), rotation=90,
|
|
123
|
+
transform=axis.transAxes, va='center')
|
|
124
|
+
|
|
125
|
+
axis.spines['bottom'].set_position('zero')
|
|
126
|
+
axis.spines['left'].set_position('zero')
|
|
127
|
+
|
|
128
|
+
axis.spines['top'].set_visible(False)
|
|
129
|
+
axis.spines['right'].set_visible(False)
|
|
130
|
+
|
|
131
|
+
if num_plots < num_axes:
|
|
132
|
+
fig.delaxes(axes_flat[-1])
|
|
133
|
+
|
|
134
|
+
fig.tight_layout()
|
|
135
|
+
|
|
136
|
+
return fig, axes
|
|
137
|
+
|
|
138
|
+
def get_regression(self, y_data, num_comp=None, update_instance=True):
|
|
139
|
+
|
|
140
|
+
if self.y_suffixes is None:
|
|
141
|
+
self.y_suffixes = ['%i' % num for num in range(1, len(y_data))]
|
|
142
|
+
|
|
143
|
+
self.y_labels = [r'$' + self.y_name + ('{%s}' % suffix) + '$'
|
|
144
|
+
for suffix in self.y_suffixes]
|
|
145
|
+
|
|
146
|
+
if num_comp is None:
|
|
147
|
+
num_comp = self.num_comp
|
|
148
|
+
|
|
149
|
+
# scores = self.projections[:, :num_comp]
|
|
150
|
+
scores = self.projections[:, :num_comp]
|
|
151
|
+
|
|
152
|
+
scores_inv = np.linalg.pinv(scores) # pseudoinverse of scores
|
|
153
|
+
|
|
154
|
+
# Regression coefficients w.r.t. principal components
|
|
155
|
+
if y_data.ndim == 1:
|
|
156
|
+
y_data = y_data[..., np.newaxis]
|
|
157
|
+
|
|
158
|
+
y_means = y_data.mean(axis=0)
|
|
159
|
+
y_center = y_data - y_means
|
|
160
|
+
q_coeff = np.dot(scores_inv, y_center)
|
|
161
|
+
|
|
162
|
+
regression_coeff = q_coeff
|
|
163
|
+
|
|
164
|
+
self.y_data = y_data
|
|
165
|
+
self.y_center = y_center
|
|
166
|
+
self.y_means = y_means
|
|
167
|
+
|
|
168
|
+
if update_instance:
|
|
169
|
+
self.regression_coeff = regression_coeff
|
|
170
|
+
self.num_comp = num_comp
|
|
171
|
+
|
|
172
|
+
y_pred = self.predict(self.data)
|
|
173
|
+
residuals = y_data - y_pred
|
|
174
|
+
self.residuals = residuals
|
|
175
|
+
|
|
176
|
+
# # Regression coefficients w.r.t. original X
|
|
177
|
+
# regression_coeff = np.dot(self.svd_dict['V'][:, :num_comp],
|
|
178
|
+
# q_coeff)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# if update_instance:
|
|
182
|
+
# self.regression_coeff = regression_coeff
|
|
183
|
+
|
|
184
|
+
# y_pred = self.predict(self.data)
|
|
185
|
+
# residuals = y_data - y_pred
|
|
186
|
+
# self.residuals = residuals
|
|
187
|
+
|
|
188
|
+
return regression_coeff
|
|
189
|
+
|
|
190
|
+
def predict(self, inputs, num_comp=None, regression_coeff=None,
|
|
191
|
+
full_output=False):
|
|
192
|
+
inputs = np.atleast_2d(inputs)
|
|
193
|
+
|
|
194
|
+
if self.snv:
|
|
195
|
+
inputs_centered = self.__center_data(inputs)
|
|
196
|
+
else:
|
|
197
|
+
inputs_centered = self.__center_data(inputs)
|
|
198
|
+
|
|
199
|
+
if regression_coeff is None:
|
|
200
|
+
coeff = self.regression_coeff
|
|
201
|
+
else:
|
|
202
|
+
coeff = regression_coeff
|
|
203
|
+
|
|
204
|
+
if num_comp is None:
|
|
205
|
+
num_comp = self.num_comp
|
|
206
|
+
|
|
207
|
+
p_matrix = self.svd_dict['V'][:, :num_comp]
|
|
208
|
+
new_projections = np.dot(inputs_centered, p_matrix)
|
|
209
|
+
|
|
210
|
+
resid_x = inputs_centered[0] - np.dot(new_projections[0],
|
|
211
|
+
p_matrix.T)
|
|
212
|
+
|
|
213
|
+
SPE_x = np.dot(resid_x, resid_x)
|
|
214
|
+
print(SPE_x)
|
|
215
|
+
|
|
216
|
+
# new_projections, _, di = self.__get_projections(inputs_centered,
|
|
217
|
+
# num_comp)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
response = np.dot(new_projections, coeff) + self.y_means
|
|
221
|
+
|
|
222
|
+
num_data = self.y_data.size
|
|
223
|
+
|
|
224
|
+
if full_output:
|
|
225
|
+
resid = response - self.y_data
|
|
226
|
+
mse = 1 / num_data * np.dot(resid.T, resid)
|
|
227
|
+
info_out = {'x_projected': new_projections, 'y_pred': response,
|
|
228
|
+
'MSE': mse}
|
|
229
|
+
|
|
230
|
+
return info_out
|
|
231
|
+
else:
|
|
232
|
+
|
|
233
|
+
return response
|
|
234
|
+
|
|
235
|
+
def evaluate_mse(self, num_comp=None):
|
|
236
|
+
|
|
237
|
+
if num_comp is None:
|
|
238
|
+
pc_counter = range(len(self.svd_dict['sv']))
|
|
239
|
+
else:
|
|
240
|
+
pc_counter = range(num_comp)
|
|
241
|
+
|
|
242
|
+
mse = []
|
|
243
|
+
residuals = []
|
|
244
|
+
|
|
245
|
+
n_data = np.prod(self.y_data.shape)
|
|
246
|
+
for n_component in pc_counter:
|
|
247
|
+
coeff = self.get_regression(self.y_data, n_component + 1,
|
|
248
|
+
update_instance=False)
|
|
249
|
+
|
|
250
|
+
pred = self.predict(self.data, regression_coeff=coeff,
|
|
251
|
+
num_comp=n_component + 1)
|
|
252
|
+
|
|
253
|
+
resid = self.y_data - pred
|
|
254
|
+
|
|
255
|
+
mse_val = 1 / n_data * (resid**2).sum()
|
|
256
|
+
|
|
257
|
+
mse.append(mse_val)
|
|
258
|
+
residuals.append(resid)
|
|
259
|
+
|
|
260
|
+
return mse, residuals
|
|
261
|
+
|
|
262
|
+
def plot_parity(self, figsize=None):
|
|
263
|
+
if figsize is None:
|
|
264
|
+
figsize = (4, 3.5)
|
|
265
|
+
fig, axis = plt.subplots(figsize=figsize)
|
|
266
|
+
|
|
267
|
+
markers = ['o', 's', 'd', '*']
|
|
268
|
+
|
|
269
|
+
y_pred = self.y_data - self.residuals
|
|
270
|
+
|
|
271
|
+
minim = (np.minimum(self.y_data, y_pred)).min()
|
|
272
|
+
maxim = (np.maximum(self.y_data, y_pred)).max()
|
|
273
|
+
|
|
274
|
+
range_vals = maxim - minim
|
|
275
|
+
|
|
276
|
+
left_bottom = [minim - range_vals*0.01]*2
|
|
277
|
+
right_top = [maxim + range_vals*0.01]*2
|
|
278
|
+
|
|
279
|
+
axis.plot(*zip(left_bottom, right_top), '--k', alpha=0.5)
|
|
280
|
+
|
|
281
|
+
for ind in range(self.y_data.shape[1]):
|
|
282
|
+
axis.plot(self.y_data[:, ind], y_pred[:, ind],
|
|
283
|
+
marker=markers[ind], mfc='None', ls='',
|
|
284
|
+
label=self.y_labels[ind])
|
|
285
|
+
|
|
286
|
+
axis.legend()
|
|
287
|
+
|
|
288
|
+
axis.set_xlabel('$%s{data}$' % self.y_name)
|
|
289
|
+
axis.set_ylabel('$%s{model}$' % self.y_name)
|
|
290
|
+
|
|
291
|
+
axis.xaxis.set_minor_locator(AutoMinorLocator(2))
|
|
292
|
+
axis.yaxis.set_minor_locator(AutoMinorLocator(2))
|
|
293
|
+
|
|
294
|
+
axis.text(1, 1.04, 'num_components = %i' % self.num_comp,
|
|
295
|
+
transform=axis.transAxes, ha='right')
|
|
296
|
+
|
|
297
|
+
return fig, axis
|
|
298
|
+
|
|
299
|
+
def cross_validation(self, num_groups=10):
|
|
300
|
+
perm = np.random.permutation(self.data.shape[0])
|
PharmaPy/CheckModule.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Tue Mar 7 14:49:31 2023
|
|
4
|
+
|
|
5
|
+
@author: dcasasor
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import pathlib
|
|
10
|
+
|
|
11
|
+
from PharmaPy.Errors import PharmaPySpecificationError
|
|
12
|
+
import warnings
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
root = pathlib.Path(__file__).parent / 'data'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def check_modeling_objects(uo, instance_name=None):
|
|
19
|
+
with open(root / 'minimum_modeling_objects.json') as fi:
|
|
20
|
+
checks = json.load(fi)
|
|
21
|
+
|
|
22
|
+
class_name = uo.__class__.__name__
|
|
23
|
+
|
|
24
|
+
if instance_name is None:
|
|
25
|
+
instance_name = '<InstanceName>'
|
|
26
|
+
instance_descr = "a " + class_name + ' instance'
|
|
27
|
+
else:
|
|
28
|
+
instance_descr = "the '%s' %s instance" % (instance_name, class_name)
|
|
29
|
+
|
|
30
|
+
if class_name in checks['special']:
|
|
31
|
+
modeling_objs = checks['special'][class_name]
|
|
32
|
+
else:
|
|
33
|
+
modeling_objs = checks[uo.oper_mode]
|
|
34
|
+
|
|
35
|
+
module_name = uo.__module__.split('.')[-1]
|
|
36
|
+
|
|
37
|
+
cond_kin = (module_name in checks['has_kinetics']['modules'] or
|
|
38
|
+
class_name in checks['has_kinetics']['classes']) and \
|
|
39
|
+
'Kinetics' not in modeling_objs
|
|
40
|
+
|
|
41
|
+
cond_utility = (module_name in checks['has_utility']['modules'] or
|
|
42
|
+
class_name in checks['has_utility']['classes']) and \
|
|
43
|
+
'Utility' not in modeling_objs
|
|
44
|
+
|
|
45
|
+
if cond_kin:
|
|
46
|
+
modeling_objs.append('Kinetics')
|
|
47
|
+
|
|
48
|
+
if cond_utility:
|
|
49
|
+
modeling_objs.append('Utility')
|
|
50
|
+
|
|
51
|
+
missing_obj = []
|
|
52
|
+
for obj in modeling_objs:
|
|
53
|
+
if not hasattr(uo, obj) or getattr(uo, obj) is None:
|
|
54
|
+
missing_obj.append(obj)
|
|
55
|
+
|
|
56
|
+
if len(missing_obj) > 0:
|
|
57
|
+
intro = "The following PharmaPy modeling objects were " \
|
|
58
|
+
"not detected in %s:\n" % instance_descr
|
|
59
|
+
|
|
60
|
+
obj_enum = '\t' + ',\n\t'.join(missing_obj) + '.\n\n'
|
|
61
|
+
|
|
62
|
+
recommend = "Please create the missing modeling objects listed above" \
|
|
63
|
+
" and then aggregate them one by one to the corresponding unit " \
|
|
64
|
+
"operation instance, e.g. %s.%s = <%sClass>(...)" % (
|
|
65
|
+
instance_name, missing_obj[0], missing_obj[0])
|
|
66
|
+
|
|
67
|
+
message = intro + obj_enum + recommend
|
|
68
|
+
|
|
69
|
+
# raise PharmaPySpecificationError(message)
|
|
70
|
+
warnings.warn(message)
|