Robert Elder commited on
Commit ·
053a1c9
1
Parent(s): d33329a
ChemID update: check for valid smiles before doing other work. quantity module updates: added effect of swelling on solvent volume; added check for non-physical amount of swelling; updated error templates to be more informative
Browse files- ChemID.py +15 -11
- quantity_module/functions.py +12 -9
- quantity_module/quantity.py +17 -8
- quantity_module/templates/quantity_error.html +23 -0
ChemID.py
CHANGED
|
@@ -22,6 +22,8 @@ with open('data/salt_list.txt', 'r') as fp:
|
|
| 22 |
lines = fp.readlines()
|
| 23 |
SALT_SET = {line.strip() for line in lines}
|
| 24 |
|
|
|
|
|
|
|
| 25 |
## not sure if this will be possible on pythonanywhere; use this flag to disable related code blocks
|
| 26 |
try_dsstox = True
|
| 27 |
if try_dsstox:
|
|
@@ -136,17 +138,6 @@ def ResolveChemical(chemName, IDtype, debug=False, get_properties=['logp','rho',
|
|
| 136 |
elif IDtype == 'SMILES':
|
| 137 |
smiles = chemName
|
| 138 |
|
| 139 |
-
name = smiles2name(smiles)
|
| 140 |
-
if name:
|
| 141 |
-
cas = name2cas(name)
|
| 142 |
-
if 'rho' in get_properties:
|
| 143 |
-
rho, rho_origin = string2density(name)
|
| 144 |
-
if 'rho' in get_properties and pd.isna(rho) and cas:
|
| 145 |
-
rho, rho_origin = string2density(cas)
|
| 146 |
-
|
| 147 |
-
if 'mp' in get_properties:
|
| 148 |
-
mp, mp_origin = mol2mp(cas, name, smiles)
|
| 149 |
-
|
| 150 |
try:
|
| 151 |
mol = Chem.MolFromSmiles(smiles)
|
| 152 |
except:
|
|
@@ -159,6 +150,19 @@ def ResolveChemical(chemName, IDtype, debug=False, get_properties=['logp','rho',
|
|
| 159 |
im64 = Imageto64(im)
|
| 160 |
else:
|
| 161 |
error = 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
elif IDtype == 'common':
|
| 163 |
name = chemName
|
| 164 |
|
|
|
|
| 22 |
lines = fp.readlines()
|
| 23 |
SALT_SET = {line.strip() for line in lines}
|
| 24 |
|
| 25 |
+
ERROR_CODES = {0:None, 1:'Structure could not be determined from the identifier', 2:'Invalid SMILES code', 3:'Invalid CAS number', 4:'Invalid identifier type selected'}
|
| 26 |
+
|
| 27 |
## not sure if this will be possible on pythonanywhere; use this flag to disable related code blocks
|
| 28 |
try_dsstox = True
|
| 29 |
if try_dsstox:
|
|
|
|
| 138 |
elif IDtype == 'SMILES':
|
| 139 |
smiles = chemName
|
| 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
try:
|
| 142 |
mol = Chem.MolFromSmiles(smiles)
|
| 143 |
except:
|
|
|
|
| 150 |
im64 = Imageto64(im)
|
| 151 |
else:
|
| 152 |
error = 2
|
| 153 |
+
|
| 154 |
+
# if SMILES is not valid, skip the other stuff
|
| 155 |
+
if not error:
|
| 156 |
+
name = smiles2name(smiles)
|
| 157 |
+
if name:
|
| 158 |
+
cas = name2cas(name)
|
| 159 |
+
if 'rho' in get_properties:
|
| 160 |
+
rho, rho_origin = string2density(name)
|
| 161 |
+
if 'rho' in get_properties and pd.isna(rho) and cas:
|
| 162 |
+
rho, rho_origin = string2density(cas)
|
| 163 |
+
|
| 164 |
+
if 'mp' in get_properties:
|
| 165 |
+
mp, mp_origin = mol2mp(cas, name, smiles)
|
| 166 |
elif IDtype == 'common':
|
| 167 |
name = chemName
|
| 168 |
|
quantity_module/functions.py
CHANGED
|
@@ -76,6 +76,8 @@ for solv in df_visc['Solvent_Name']:
|
|
| 76 |
df_visc['MW'] = mws
|
| 77 |
## selected solvent MWs
|
| 78 |
Solvent_MWs = {solv:df_visc.loc[df_visc['Solvent_Name']==solv,'MW'].iloc[0] for solv in solvents}
|
|
|
|
|
|
|
| 79 |
# linear relation to estimate Vabc when it fails for a molecule
|
| 80 |
Vabc = df_desc['Vabc']
|
| 81 |
Vmcg = df_desc['VMcGowan']
|
|
@@ -259,10 +261,11 @@ def get_D_dists(w,T,Polymer_Tg,Solvent_Name,Solvent_MW,Solute_MW,CHRIS_category,
|
|
| 259 |
else:
|
| 260 |
return D_dist_noswell, D_dist_swell
|
| 261 |
|
| 262 |
-
def PlaneSheetFiniteBathMass(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime,nterms):
|
| 263 |
### works with scalar- or vector-valued M0 and D
|
| 264 |
L = PolymerVolume/SurfaceArea #effective length scale of the component
|
| 265 |
-
alpha = SolventVolume/PolymerVolume/K
|
|
|
|
| 266 |
Minfty = M0/(1.+1./(alpha))
|
| 267 |
eps = 1e-12
|
| 268 |
f = lambda x: np.tan(x)+alpha*x
|
|
@@ -276,10 +279,10 @@ def PlaneSheetFiniteBathMass(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,Extr
|
|
| 276 |
result = Minfty*result
|
| 277 |
return result
|
| 278 |
|
| 279 |
-
def PlaneSheetFiniteBathMassApprox(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime):
|
| 280 |
### works with scalar- or vector-valued M0 and D
|
| 281 |
L = PolymerVolume/SurfaceArea #effective length scale of the component
|
| 282 |
-
alpha = SolventVolume
|
| 283 |
Minfty = M0/(1.+1./(alpha))
|
| 284 |
T = D*ExtractionTime/L**2.
|
| 285 |
# if exp will blow up, use asymptotic expansion instead
|
|
@@ -297,15 +300,15 @@ def PlaneSheetFiniteBathMassApprox(M0,D,K,PolymerVolume,SurfaceArea,SolventVolum
|
|
| 297 |
result = Minfty*result
|
| 298 |
return result
|
| 299 |
|
| 300 |
-
def PlaneSheetAnalytical(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime,nterms=5):
|
| 301 |
L = PolymerVolume/SurfaceArea #effective length scale of the component
|
| 302 |
T = D*ExtractionTime/L**2.
|
| 303 |
-
result = (T>0.05) * PlaneSheetFiniteBathMass(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime,nterms) + \
|
| 304 |
-
(T<=0.05) * PlaneSheetFiniteBathMassApprox(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime)
|
| 305 |
return result
|
| 306 |
|
| 307 |
-
def get_M_dist(D_dist, M_expt, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime, K_expt=10):
|
| 308 |
-
M_M0 = PlaneSheetAnalytical(1.0, D_dist, K_expt, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime, nterms=5) # much faster and indistinguishable distribution from Mfunc_film
|
| 309 |
M0_pred = M_expt/M_M0
|
| 310 |
return M0_pred
|
| 311 |
|
|
|
|
| 76 |
df_visc['MW'] = mws
|
| 77 |
## selected solvent MWs
|
| 78 |
Solvent_MWs = {solv:df_visc.loc[df_visc['Solvent_Name']==solv,'MW'].iloc[0] for solv in solvents}
|
| 79 |
+
#Solvent_Densities = {solv:string2density(solv)[0] for solv in solvents}
|
| 80 |
+
Solvent_Densities = {'ethanol': 0.79, 'isopropanol': 0.787515, 'acetonitrile': 0.78467, 'toluene': 0.86925, 'dichloromethane': 1.326875, 'hexane': 0.6602}
|
| 81 |
# linear relation to estimate Vabc when it fails for a molecule
|
| 82 |
Vabc = df_desc['Vabc']
|
| 83 |
Vmcg = df_desc['VMcGowan']
|
|
|
|
| 261 |
else:
|
| 262 |
return D_dist_noswell, D_dist_swell
|
| 263 |
|
| 264 |
+
def PlaneSheetFiniteBathMass(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime,nterms,Qv=1):
|
| 265 |
### works with scalar- or vector-valued M0 and D
|
| 266 |
L = PolymerVolume/SurfaceArea #effective length scale of the component
|
| 267 |
+
#alpha = SolventVolume/PolymerVolume/K
|
| 268 |
+
alpha = (SolventVolume-PolymerVolume*(Qv-1))/(Qv*PolymerVolume*K)
|
| 269 |
Minfty = M0/(1.+1./(alpha))
|
| 270 |
eps = 1e-12
|
| 271 |
f = lambda x: np.tan(x)+alpha*x
|
|
|
|
| 279 |
result = Minfty*result
|
| 280 |
return result
|
| 281 |
|
| 282 |
+
def PlaneSheetFiniteBathMassApprox(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime,Qv=1):
|
| 283 |
### works with scalar- or vector-valued M0 and D
|
| 284 |
L = PolymerVolume/SurfaceArea #effective length scale of the component
|
| 285 |
+
alpha = (SolventVolume-PolymerVolume*(Qv-1))/(Qv*PolymerVolume*K)
|
| 286 |
Minfty = M0/(1.+1./(alpha))
|
| 287 |
T = D*ExtractionTime/L**2.
|
| 288 |
# if exp will blow up, use asymptotic expansion instead
|
|
|
|
| 300 |
result = Minfty*result
|
| 301 |
return result
|
| 302 |
|
| 303 |
+
def PlaneSheetAnalytical(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime,nterms=5,Qv=1):
|
| 304 |
L = PolymerVolume/SurfaceArea #effective length scale of the component
|
| 305 |
T = D*ExtractionTime/L**2.
|
| 306 |
+
result = (T>0.05) * PlaneSheetFiniteBathMass(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime,nterms,Qv=Qv) + \
|
| 307 |
+
(T<=0.05) * PlaneSheetFiniteBathMassApprox(M0,D,K,PolymerVolume,SurfaceArea,SolventVolume,ExtractionTime,Qv=Qv)
|
| 308 |
return result
|
| 309 |
|
| 310 |
+
def get_M_dist(D_dist, M_expt, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime, K_expt=10, Qv=1):
|
| 311 |
+
M_M0 = PlaneSheetAnalytical(1.0, D_dist, K_expt, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime, nterms=5, Qv=Qv) # much faster and indistinguishable distribution from Mfunc_film
|
| 312 |
M0_pred = M_expt/M_M0
|
| 313 |
return M0_pred
|
| 314 |
|
quantity_module/quantity.py
CHANGED
|
@@ -42,7 +42,7 @@ def exp_post():
|
|
| 42 |
CHRIS_category = None
|
| 43 |
|
| 44 |
if T<Polymer_Tg:
|
| 45 |
-
return render_template('
|
| 46 |
|
| 47 |
chemName = request.form['chemName']
|
| 48 |
IDtype = request.form['IDtype']
|
|
@@ -56,7 +56,7 @@ def exp_post():
|
|
| 56 |
|
| 57 |
if error > 0:
|
| 58 |
# TODO output more useful info
|
| 59 |
-
return render_template('
|
| 60 |
|
| 61 |
#MW = SigFigs(MW, 6)
|
| 62 |
if 'logp' not in get_properties:
|
|
@@ -102,8 +102,17 @@ def exp_post():
|
|
| 102 |
Extraction_Time = float(request.form['time'])
|
| 103 |
K_expt = float(request.form['K'])
|
| 104 |
Solvent_MW = Solvent_MWs[Solvent_Name]
|
|
|
|
| 105 |
Solute_MW = MW
|
| 106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
if units == 'mg':
|
| 108 |
mass_units = mass*1e3
|
| 109 |
elif units == 'µg':
|
|
@@ -129,11 +138,11 @@ def exp_post():
|
|
| 129 |
# outside training domain, default to Wilke-Chang
|
| 130 |
method = 'qrf/wc'
|
| 131 |
D_dist_noswell, D_dist_swell = get_D_dists(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, 'G2', rng, return_DCs=False, N=N_sample)
|
| 132 |
-
M0_pred = get_M_dist(D_dist_swell, M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt=K_expt)
|
| 133 |
else:
|
| 134 |
method = 'qrf'
|
| 135 |
D_dist_noswell, D_dist_swell = get_D_dists(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, None, rng, return_DCs=False, N=N_sample, input_Ds=diff)
|
| 136 |
-
M0_pred = get_M_dist(D_dist_swell, M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt=K_expt)
|
| 137 |
else:
|
| 138 |
## use categories
|
| 139 |
CHRIS_category = categories[pIndex]
|
|
@@ -143,7 +152,7 @@ def exp_post():
|
|
| 143 |
CHRIS_flag = 'wc'
|
| 144 |
CHRIS_category = 'G2'
|
| 145 |
D_dist_noswell, D_dist_swell = get_D_dists(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, CHRIS_category, rng, return_DCs=False, N=N_sample)
|
| 146 |
-
M0_pred = get_M_dist(D_dist_swell, M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt=K_expt)
|
| 147 |
if CHRIS_flag is None:
|
| 148 |
method = 'category'
|
| 149 |
else:
|
|
@@ -154,11 +163,11 @@ def exp_post():
|
|
| 154 |
print(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, CHRIS_category)
|
| 155 |
print(np.nanquantile(D_dist_noswell, [0.05,0.5,0.95]))
|
| 156 |
print(np.nanquantile(D_dist_swell, [0.05,0.5,0.95]))
|
| 157 |
-
print('M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt, method')
|
| 158 |
-
print(M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt, method)
|
| 159 |
print(np.nanquantile(M0_pred, [0.05,0.5,0.95]))
|
| 160 |
V1,V2 = get_D_dists(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, 'G2', rng, return_DCs=False, N=N_sample)
|
| 161 |
-
V3 = get_M_dist(V2, M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt=K_expt)
|
| 162 |
print(np.nanquantile(V2, [0.05,0.5,0.95]))
|
| 163 |
print(np.nanquantile(V3, [0.05,0.5,0.95]))
|
| 164 |
|
|
|
|
| 42 |
CHRIS_category = None
|
| 43 |
|
| 44 |
if T<Polymer_Tg:
|
| 45 |
+
return render_template('quantity_error.html', message='Unfortunately, the CHRIS-Total Quantity module can only be used for polymers with glass transition temperatures lower than the extraction temperature.')
|
| 46 |
|
| 47 |
chemName = request.form['chemName']
|
| 48 |
IDtype = request.form['IDtype']
|
|
|
|
| 56 |
|
| 57 |
if error > 0:
|
| 58 |
# TODO output more useful info
|
| 59 |
+
return render_template('quantity_error.html', message=f'Uh-oh! Something went wrong. We were unable to match your chemical. Please return to the previous page and try a different identifier. <br><br>Error code: {error} <br>Error message: {ERROR_CODES[error]} ')
|
| 60 |
|
| 61 |
#MW = SigFigs(MW, 6)
|
| 62 |
if 'logp' not in get_properties:
|
|
|
|
| 102 |
Extraction_Time = float(request.form['time'])
|
| 103 |
K_expt = float(request.form['K'])
|
| 104 |
Solvent_MW = Solvent_MWs[Solvent_Name]
|
| 105 |
+
Solvent_Density = Solvent_Densities[Solvent_Name]
|
| 106 |
Solute_MW = MW
|
| 107 |
|
| 108 |
+
## extent of swelling, V_swelled/V_unswelled
|
| 109 |
+
Swelling_volfrac = Polymer_Density / (Solvent_Density * (1/Swelling_wtfrac-1) + Polymer_Density)
|
| 110 |
+
Qv = 1+Swelling_volfrac/(1-Swelling_volfrac)
|
| 111 |
+
# TODO check that swelling is physically possible
|
| 112 |
+
if Solvent_Volume < Polymer_Volume*(Qv-1):
|
| 113 |
+
return render_template('quantity_error.html', message=f'Based on the information provided, the amount of swelling appears physically impossible. Please check the values you entered.')
|
| 114 |
+
#return render_template('quantity_error.html', message=f'Based on the swelling weight percent ({Swelling_percent}%), the polymer density ({Polymer_Density} g/cm<sup>3</sup>), and the solvent density ({Solvent_Density} g/cm<sup>3</sup>), the polymer volume ({Polymer_Volume} cm<sup>3</sup>), ')
|
| 115 |
+
|
| 116 |
if units == 'mg':
|
| 117 |
mass_units = mass*1e3
|
| 118 |
elif units == 'µg':
|
|
|
|
| 138 |
# outside training domain, default to Wilke-Chang
|
| 139 |
method = 'qrf/wc'
|
| 140 |
D_dist_noswell, D_dist_swell = get_D_dists(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, 'G2', rng, return_DCs=False, N=N_sample)
|
| 141 |
+
M0_pred = get_M_dist(D_dist_swell, M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt=K_expt, Qv=Qv)
|
| 142 |
else:
|
| 143 |
method = 'qrf'
|
| 144 |
D_dist_noswell, D_dist_swell = get_D_dists(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, None, rng, return_DCs=False, N=N_sample, input_Ds=diff)
|
| 145 |
+
M0_pred = get_M_dist(D_dist_swell, M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt=K_expt, Qv=Qv)
|
| 146 |
else:
|
| 147 |
## use categories
|
| 148 |
CHRIS_category = categories[pIndex]
|
|
|
|
| 152 |
CHRIS_flag = 'wc'
|
| 153 |
CHRIS_category = 'G2'
|
| 154 |
D_dist_noswell, D_dist_swell = get_D_dists(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, CHRIS_category, rng, return_DCs=False, N=N_sample)
|
| 155 |
+
M0_pred = get_M_dist(D_dist_swell, M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt=K_expt, Qv=Qv)
|
| 156 |
if CHRIS_flag is None:
|
| 157 |
method = 'category'
|
| 158 |
else:
|
|
|
|
| 163 |
print(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, CHRIS_category)
|
| 164 |
print(np.nanquantile(D_dist_noswell, [0.05,0.5,0.95]))
|
| 165 |
print(np.nanquantile(D_dist_swell, [0.05,0.5,0.95]))
|
| 166 |
+
print('M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt, method, Qv, Swelling_volfrac')
|
| 167 |
+
print(M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt, method, Qv, Swelling_volfrac)
|
| 168 |
print(np.nanquantile(M0_pred, [0.05,0.5,0.95]))
|
| 169 |
V1,V2 = get_D_dists(Swelling_wtfrac, T, Polymer_Tg, Solvent_Name, Solvent_MW, Solute_MW, 'G2', rng, return_DCs=False, N=N_sample)
|
| 170 |
+
V3 = get_M_dist(V2, M_expt, Polymer_Volume, Surface_Area, Solvent_Volume, Extraction_Time*3600, K_expt=K_expt, Qv=Qv)
|
| 171 |
print(np.nanquantile(V2, [0.05,0.5,0.95]))
|
| 172 |
print(np.nanquantile(V3, [0.05,0.5,0.95]))
|
| 173 |
|
quantity_module/templates/quantity_error.html
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>CHRIS-Error</title>
|
| 6 |
+
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles.css') }}">
|
| 7 |
+
|
| 8 |
+
</head>
|
| 9 |
+
|
| 10 |
+
<img src="{{ url_for('static',filename='images/FDAlogo.png') }}" style="float: left;" height="100"/>
|
| 11 |
+
<img src="{{ url_for('static',filename='images/FDAgraphic.png') }}" style="float: right;" height="100"/>
|
| 12 |
+
<br clear="all" />
|
| 13 |
+
|
| 14 |
+
<body>
|
| 15 |
+
|
| 16 |
+
<div style="font-size:5rem;text-align:center"> 🤦 </div>
|
| 17 |
+
|
| 18 |
+
<p style="font-size:2rem;text-align:center">
|
| 19 |
+
{{ message|safe }}
|
| 20 |
+
</p>
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
</body>
|