File size: 6,559 Bytes
db70ee2 8cf84d6 07404f4 db70ee2 73afb55 4cf8bbb db70ee2 abe0ff4 3c8422a 55e7549 0043a04 c0c5fff 10ad681 8cf84d6 73afb55 10ad681 07404f4 10ad681 8cf84d6 10ad681 db70ee2 8cf84d6 10ad681 8cf84d6 10ad681 db70ee2 8cf84d6 db70ee2 8cf84d6 db70ee2 8cf84d6 6c9c7cf 8cf84d6 07404f4 3c8422a abe0ff4 3c8422a 07404f4 ec4a00e c87f597 6c9c7cf ec4a00e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | import math
import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import io
import base64
from scipy import special
from scipy.optimize import bisect
def SigFigs(number, n):
if number == 0: return number
return round(number, n - int(math.floor(math.log10(math.fabs(number)))) - 1)
def HtmlNumber(number,n):
number_str = f'{number:.{n}e}'
base, exponent = number_str.split('e')
html_output = f'{base} × 10<sup>{exponent[0] if exponent[0]=="-" else ""}{int(exponent[1:]):d}</sup>'
return html_output
def Piringer(Mw, Ap, T=310.):
# Semi-empirical model for D(Mw) given polymer property Ap- Toxicol. Sci. 2019, 172 (1), 201–212.
if Mw > 1100.: # if molecule is greater than 1100 g/mol, default to that value as worst case
Mw = 1100.
return 1e4 * np.exp(Ap - 0.1351 * Mw ** (2. / 3.) + 0.003 * Mw - 10454. / T)
def WilkeChang(Mw):
# Semi-empirical model for D(Mw) assuming diffusion in water
Va = 4.21 + 1.58 * Mw # molecular volume as a function of Mw assuming linear alkanes as worst case
T = 310.
MwH2O = 18.
viscosity = 0.6913
alpha = 2.6
return 7.4e-8 * (MwH2O * alpha) ** 0.5 * T / viscosity / Va ** 0.6
def SheetRelease(amount, vol, area, time, D):
# analytical solution(s) to Fick's law for a plane sheet
L = vol / area
D = D * 3600. # convert seconds to hours
tau = D * time / L ** 2. # dimensionless time
if tau <= 0.2:
release = 2. * amount * np.sqrt(tau / np.pi)
else:
release = amount * (1. - (8. / (np.pi ** 2.)) * np.exp(-tau * np.pi ** 2. / 4.))
return release
def SheetRates(amount, vol, area, time, D):
D = D * 86400.
L = vol / area
rates = np.zeros(len(time))
tau = D * time[0] / L ** 2.
if tau < 0.2:
rates[0] = 2. * amount * np.sqrt(tau / np.pi)
else:
rates[0] = amount * (1. - (8. / (np.pi ** 2.)) * np.exp(-tau * np.pi ** 2. / 4.))
for i in range(1, len(time)):
tau = D * time[i] / L ** 2.
if tau < 0.2:
rates[i] = 2. * amount * np.sqrt(tau / np.pi) - np.sum(rates[:i])
else:
rates[i] = amount * (1. - (8. / (np.pi ** 2.)) * np.exp(-tau * np.pi ** 2. / 4.)) - np.sum(rates[:i])
return rates
def RatePlot(tarray, rates):
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(tarray, rates, 'o')
ax.set(
xlabel='time (days)',
ylabel='release rate (mg/day)',
)
plt.tight_layout()
pngImage = io.BytesIO()
FigureCanvas(fig).print_png(pngImage)
pngImageB64String = "data:image/png;base64,"
pngImageB64String += base64.b64encode(pngImage.getvalue()).decode('utf8')
return pngImageB64String
def CdfPlot(vals, units=None):
xlabel = 'Total quantity' if units is None else f'Total quantity ({units})'
fig, ax = plt.subplots(figsize=(6, 4))
ax.ecdf(vals, c='b', lw=3)
q50 = np.nanquantile(vals,0.5)
ax.plot([q50,q50],[0,0.5],'k--',[q50],[0.5],'ko',[ax.get_xlim()[0],q50],[0.5,0.5],'k--',lw=2,ms=10)
ax.set_xscale('log')
ax.set(
xlabel=xlabel,
ylabel='Cumulative probability',
)
plt.tight_layout()
pngImage = io.BytesIO()
FigureCanvas(fig).print_png(pngImage)
pngImageB64String = "data:image/png;base64,"
pngImageB64String += base64.b64encode(pngImage.getvalue()).decode('utf8')
return pngImageB64String
def PlaneSheetFiniteBathMass(M0, D, K, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime, nterms):
L = PolymerVolume / SurfaceArea # effective length scale of the component
alpha = SolventVolume / PolymerVolume / K
Minfty = M0 / (1. + 1. / (alpha))
qn = np.zeros((nterms))
for j in range(nterms):
solved = False
eps = 1e-8
while not solved:
f = lambda x: np.tan(x) + alpha * x
rts = bisect(f, np.pi / 2. + j * np.pi + eps, np.pi * (1. + j) - eps, xtol=1e-6)
# print(rts)
solved = True
qn[j] = rts
result = 1.
for j in range(nterms):
result = result - (2. * alpha * (1. + alpha)) * np.exp(-D * qn[j] ** 2. * ExtractionTime / L ** 2.) / (
1. + alpha + alpha ** 2. * qn[j] ** 2.)
result = Minfty * result
return result
def PlaneSheetFiniteBathMassApprox(M0, D, K, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime):
L = PolymerVolume / SurfaceArea # effective length scale of the component
alpha = SolventVolume / PolymerVolume / K
Minfty = M0 / (1. + 1. / (alpha))
T = D * ExtractionTime / L ** 2.
# if exp will blow up, use asymptotic expansion instead
if (T / alpha ** 2. < 100.):
result = Minfty * (1. + alpha) * (1. - np.exp(T / alpha ** 2.) * special.erfc(np.sqrt(T) / alpha))
else:
result = Minfty * (1. + alpha) * (1. - alpha / (np.sqrt(np.pi) * np.sqrt(T)) + alpha ** 3. / (
2. * np.sqrt(np.pi) * (T) ** 1.5) - 3. * alpha ** 5. / (4. * np.sqrt(np.pi) * (T) ** 2.5))
return result
def PlaneSheetAnalytical(M0, D, K, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime, nterms):
L = PolymerVolume / SurfaceArea # effective length scale of the component
tau = D * ExtractionTime / L ** 2.
if tau > 0.05:
result = PlaneSheetFiniteBathMass(M0, D, K, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime, nterms)
else:
result = PlaneSheetFiniteBathMassApprox(M0, D, K, PolymerVolume, SurfaceArea, SolventVolume, ExtractionTime)
return result
def Piecewise(Mw, params):
mw_cut, func_lo, func_hi, params_lo, params_hi = params
if Mw <= mw_cut:
D = func_lo(Mw,*params_lo)
else:
D = func_hi(Mw,*params_hi)
return D
def PowerLaw(Mw, A, B):
logMw = np.log(Mw)
logD = A+logMw*B
return np.exp(logD)
def weight_func(dists):
"""
Returns the weights, based on the method in OPERA
Parameter f controls the maximum weight, prevents 1/dist -> inf
Set f to a very small number to mimic default sklearn behavior when dist = 0
"""
#f = 1e-15 # OPERA on CompTox?
#f = 5e-2 # OPERA in Matlab code
#f = 1e-3 # test
f = 5e-3
tmp = 1/(f+dists)
tmp = tmp/tmp.sum(axis=1)[..., None]
return tmp
#def func_piringer(Mw,T,Ap):
# if Mw > 1100.: # if molecule is greater than 1100 g/mol, default to that value as worst case
# Mw = 1100.
# D = 1e4*np.exp(Ap-0.1351*Mw**(2/3)+0.003*Mw-10454/T)
# return D
|