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 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/PolymerVolume/K
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('quantity_temperatureError.html')
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('quantity_chemError.html')
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"> &#129318; </div>
17
+
18
+ <p style="font-size:2rem;text-align:center">
19
+ {{ message|safe }}
20
+ </p>
21
+
22
+
23
+ </body>