binmod1d 1.0.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.
- binmod1d/__init__.py +0 -0
- binmod1d/analytical_solutions.py +619 -0
- binmod1d/bin_integrals.py +2869 -0
- binmod1d/collection_kernels.py +510 -0
- binmod1d/distribution.py +1241 -0
- binmod1d/habits.py +461 -0
- binmod1d/interaction.py +6029 -0
- binmod1d/plotting_functions.py +130 -0
- binmod1d/radar.py +151 -0
- binmod1d/spectral_model.py +4662 -0
- binmod1d-1.0.0.dist-info/METADATA +64 -0
- binmod1d-1.0.0.dist-info/RECORD +14 -0
- binmod1d-1.0.0.dist-info/WHEEL +5 -0
- binmod1d-1.0.0.dist-info/top_level.txt +1 -0
binmod1d/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
These are analytical solutions to the SCE, SBE, and SCE-SBE steady-state solution
|
|
4
|
+
following Scott (1964) and Feingold et al. (1988). For more information, see:
|
|
5
|
+
|
|
6
|
+
Scott, W. T.: Analytical studies of cloud droplet coalesence I, J. Atmos. Sci., 25, 54–65, https://doi.org/10.1175/1520-
|
|
7
|
+
0469(1968)025<0054:ASOCDC>2.0.CO;2, 1968.
|
|
8
|
+
|
|
9
|
+
Feingold, G., Tzivion, S., and Levin, Z.: Evolution of raindrop spectra. Part I: Solution to the Stochastic Collection/Breakup equation using
|
|
10
|
+
the method of moments, J. Atmos. Sci., 45, 3387–3399, https://doi.org/10.1175/1520-0469(1988)045<3387:EORSPI>2.0.CO;2, 1988.
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
import numpy as np
|
|
14
|
+
import mpmath
|
|
15
|
+
from scipy.special import gamma, gammaln, hyp2f1
|
|
16
|
+
from scipy.special import i0e, i1e # Import scaled Bessel functions
|
|
17
|
+
|
|
18
|
+
def Scott_moments(r,t,nu,E,B,kernel_type='Golovin'):
|
|
19
|
+
'''
|
|
20
|
+
Calculate moments for Scott's analytical solutions to the SCE for Golovin, Product, and Constant kernels.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
r : TYPE
|
|
25
|
+
DESCRIPTION.
|
|
26
|
+
t : TYPE
|
|
27
|
+
DESCRIPTION.
|
|
28
|
+
nu : TYPE
|
|
29
|
+
DESCRIPTION.
|
|
30
|
+
E : TYPE
|
|
31
|
+
DESCRIPTION.
|
|
32
|
+
B : TYPE
|
|
33
|
+
DESCRIPTION.
|
|
34
|
+
kernel_type : TYPE, optional
|
|
35
|
+
DESCRIPTION. The default is 'Golovin'.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
M_rt : TYPE
|
|
40
|
+
DESCRIPTION.
|
|
41
|
+
|
|
42
|
+
'''
|
|
43
|
+
|
|
44
|
+
T = E*t
|
|
45
|
+
|
|
46
|
+
if kernel_type=='Golovin':
|
|
47
|
+
|
|
48
|
+
#tau = 1 - np.exp(-0.00153*t) # normalized time
|
|
49
|
+
|
|
50
|
+
tau = 1 - np.exp(-T) # normalized time
|
|
51
|
+
|
|
52
|
+
gam_series = np.zeros_like(tau)
|
|
53
|
+
M_rt = np.zeros_like(tau)
|
|
54
|
+
|
|
55
|
+
for tt in range(len(t)):
|
|
56
|
+
gam_series[tt] = mpmath.nsum(lambda k: tau[tt]**(k) *nu**(nu*(k+1))/\
|
|
57
|
+
((tau[tt]+nu)**(r+nu+k*(nu+1))* mpmath.factorial(k+1))*\
|
|
58
|
+
(mpmath.gamma(nu+r+k*(nu+1))/\
|
|
59
|
+
mpmath.gamma(nu*(k+1))),[0,np.inf],method='direct')
|
|
60
|
+
|
|
61
|
+
M_rt[tt] = (1-tau[tt]) *gam_series
|
|
62
|
+
|
|
63
|
+
elif kernel_type == 'Product':
|
|
64
|
+
|
|
65
|
+
#T = (7/40)*0.00959*t # ELD NOTE: Apparently 7/40 factor is necessary to get Scott's solutions in his paper.
|
|
66
|
+
|
|
67
|
+
#T = (7/40)*0.00959*t
|
|
68
|
+
|
|
69
|
+
#T = 0.*t
|
|
70
|
+
|
|
71
|
+
#T = 0.0016*t # This is approximately the correct factor.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
#T = 0.043*t
|
|
75
|
+
|
|
76
|
+
#T = (7/48)*E*t
|
|
77
|
+
|
|
78
|
+
M_rt = np.zeros_like(tau)
|
|
79
|
+
|
|
80
|
+
for tt in range(len(t)):
|
|
81
|
+
M_rt[tt] = mpmath.nsum(lambda k: (T[tt]**k)*nu**((k+1)*(nu+1))*\
|
|
82
|
+
(T[tt]+nu)**(-(nu+r+k*(nu+2)))*mpmath.gamma(r+nu+k*(nu+2))/\
|
|
83
|
+
(mpmath.factorial(k+1)*mpmath.gamma((k+1)*(nu+1))),[0,np.inf],method='direct')
|
|
84
|
+
|
|
85
|
+
elif kernel_type == 'Constant':
|
|
86
|
+
|
|
87
|
+
#T = 0.0429*t
|
|
88
|
+
|
|
89
|
+
#T = E*t
|
|
90
|
+
|
|
91
|
+
# T = 0.0016
|
|
92
|
+
|
|
93
|
+
M_rt = np.zeros_like(tau)
|
|
94
|
+
|
|
95
|
+
for tt in range(len(t)):
|
|
96
|
+
M_rt[tt] = (4./(T[tt]+2)**2)*nu**(-r)*\
|
|
97
|
+
mpmath.nsum(lambda k: (mpmath.gamma(r+nu*(k+1))*((T[tt]/(T[tt]+2))**k)/\
|
|
98
|
+
mpmath.gamma(nu*(k+1))),[0,np.inf],method='direct')
|
|
99
|
+
|
|
100
|
+
return M_rt
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def Feingold_moments(r,t,nu,B,gam,kernel_type='SBE'):
|
|
104
|
+
'''
|
|
105
|
+
Calculate moments for Feingold's analytical solutions to the SBE/SCE-SBE Steady state solution for Constant kernel.
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
r : TYPE
|
|
111
|
+
DESCRIPTION.
|
|
112
|
+
t : TYPE
|
|
113
|
+
DESCRIPTION.
|
|
114
|
+
nu : TYPE
|
|
115
|
+
DESCRIPTION.
|
|
116
|
+
B : TYPE
|
|
117
|
+
DESCRIPTION.
|
|
118
|
+
gam : TYPE
|
|
119
|
+
DESCRIPTION.
|
|
120
|
+
kernel_type : TYPE, optional
|
|
121
|
+
DESCRIPTION. The default is 'SBE'.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
M_rt : TYPE
|
|
126
|
+
DESCRIPTION.
|
|
127
|
+
|
|
128
|
+
'''
|
|
129
|
+
|
|
130
|
+
M_rt = np.zeros_like(t)
|
|
131
|
+
|
|
132
|
+
if kernel_type=='SBE':
|
|
133
|
+
|
|
134
|
+
M0r = nu**(-r)*gamma(nu+r)/gamma(nu)
|
|
135
|
+
|
|
136
|
+
for tt in range(len(t)):
|
|
137
|
+
den = (1+(1/gam)*(np.exp(B*gam*t[tt])-1))
|
|
138
|
+
M_rt[tt] = (M0r+gam*(np.exp(B*gam*t[tt])-1)*gam**(-1-r)*gamma(1+r))/den
|
|
139
|
+
|
|
140
|
+
elif kernel_type=='SCE/SBE':
|
|
141
|
+
|
|
142
|
+
#E = 1 - t
|
|
143
|
+
|
|
144
|
+
E = 1. - B
|
|
145
|
+
|
|
146
|
+
eta = 0.5*E*gam*(2-E)
|
|
147
|
+
|
|
148
|
+
M_rt = 0.5*(1-E)*gam**2*gamma(r+1)*(gam-eta)**(-r-2)*\
|
|
149
|
+
(2*(gam-eta)*hyp2f1(0.5*(r+1),0.5*(r+2),1,(eta/(gam-eta))**2)-\
|
|
150
|
+
eta*(r+1)*np.sign(gam-eta)*hyp2f1(0.5*(r+2), 0.5*(r+3),2,(eta/(gam-eta))**2))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
return M_rt
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def Scott_dists(xbins, Eagg, nu, t, kernel_type='Golovin', max_k=100):
|
|
157
|
+
'''
|
|
158
|
+
Calculates Scott's analytical number distribution function solution to
|
|
159
|
+
the SCE for Golovin, Product, and Constant kernels.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
xbins : TYPE
|
|
164
|
+
DESCRIPTION.
|
|
165
|
+
Eagg : TYPE
|
|
166
|
+
DESCRIPTION.
|
|
167
|
+
nu : TYPE
|
|
168
|
+
DESCRIPTION.
|
|
169
|
+
t : TYPE
|
|
170
|
+
DESCRIPTION.
|
|
171
|
+
kernel_type : TYPE, optional
|
|
172
|
+
DESCRIPTION. The default is 'Golovin'.
|
|
173
|
+
max_k : TYPE, optional
|
|
174
|
+
DESCRIPTION. The default is 100.
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
npbins : TYPE
|
|
179
|
+
DESCRIPTION.
|
|
180
|
+
|
|
181
|
+
'''
|
|
182
|
+
|
|
183
|
+
bins = len(xbins)
|
|
184
|
+
Tlen = len(t)
|
|
185
|
+
npbins = np.zeros([bins, Tlen])
|
|
186
|
+
|
|
187
|
+
# 1. Initial State (t=0)
|
|
188
|
+
# Handle the singularity at x=0 for nu < 1
|
|
189
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
190
|
+
npbins[:, 0] = (1./gamma(nu)) * (nu**nu) * xbins**(nu-1) * np.exp(-nu*xbins)
|
|
191
|
+
np.nan_to_num(npbins[:, 0], copy=False) # Fix any 0^negative_power issues
|
|
192
|
+
|
|
193
|
+
T = Eagg * t
|
|
194
|
+
|
|
195
|
+
# --- THRESHOLD SELECTION ---
|
|
196
|
+
# The series expansions for Product and Constant kernels are numerically
|
|
197
|
+
# explosive for x > 10. We rely on the Saddle Point method there.
|
|
198
|
+
if kernel_type == 'Constant':
|
|
199
|
+
xt = 10.0
|
|
200
|
+
elif kernel_type == 'Product':
|
|
201
|
+
xt = 10.0 # Switch to saddle point earlier to avoid series overflow
|
|
202
|
+
else:
|
|
203
|
+
xt = 50.0 # Golovin is more stable
|
|
204
|
+
|
|
205
|
+
mask_sm = xbins < xt
|
|
206
|
+
mask_lg = ~mask_sm
|
|
207
|
+
|
|
208
|
+
X_sm = xbins[mask_sm, None]
|
|
209
|
+
X_lg = xbins[mask_lg, None]
|
|
210
|
+
T_vec = T[None, 1:]
|
|
211
|
+
k = np.arange(max_k).reshape(-1, 1, 1)
|
|
212
|
+
|
|
213
|
+
eps = 1e-20
|
|
214
|
+
|
|
215
|
+
if kernel_type == 'Golovin':
|
|
216
|
+
tau = 1 - np.exp(-T_vec)
|
|
217
|
+
if np.any(mask_sm):
|
|
218
|
+
# Log Prefactor
|
|
219
|
+
ln_pre = np.log(tau + eps) + (nu - 1) * np.log(X_sm + eps) - (nu + tau) * X_sm
|
|
220
|
+
|
|
221
|
+
# Log Terms
|
|
222
|
+
ln_coeffs = (nu * (k+1)) * np.log(nu) - gammaln(k+2) - gammaln(nu * (k+1))
|
|
223
|
+
ln_powers = k * np.log(tau + eps) + k * (nu + 1) * np.log(X_sm + eps)
|
|
224
|
+
log_terms = ln_coeffs + ln_powers
|
|
225
|
+
|
|
226
|
+
# Combined Log-Sum-Exp
|
|
227
|
+
max_log = np.max(log_terms, axis=0)
|
|
228
|
+
sum_exp = np.sum(np.exp(log_terms - max_log), axis=0)
|
|
229
|
+
|
|
230
|
+
# Result: (1-tau)/tau * exp(pre + max_log) * sum_exp
|
|
231
|
+
# Note: (1-tau)/tau cancels the first tau term cleanly
|
|
232
|
+
term = ((1 - tau) / (tau + eps)) * np.exp(ln_pre + max_log) * sum_exp
|
|
233
|
+
npbins[mask_sm, 1:] = term
|
|
234
|
+
|
|
235
|
+
elif kernel_type == 'Product':
|
|
236
|
+
if np.any(mask_sm):
|
|
237
|
+
# 1. Log Prefactor: -x(T+nu)
|
|
238
|
+
ln_pre = -X_sm * (T_vec + nu)
|
|
239
|
+
|
|
240
|
+
# 2. Log Terms
|
|
241
|
+
# ln_coeffs = (nu+1)(k+1)ln(nu) - ln(k+1)! - ln(gamma((nu+1)(k+1)))
|
|
242
|
+
ln_coeffs = ((nu + 1) * (k + 1)) * np.log(nu) - gammaln(k + 2) - gammaln((nu + 1) * (k + 1))
|
|
243
|
+
# ln_powers = ((nu-1) + k(nu+2))ln(x) + k*ln(T)
|
|
244
|
+
ln_powers = ((nu - 1) + k * (nu + 2)) * np.log(X_sm + eps) + k * np.log(T_vec + eps)
|
|
245
|
+
|
|
246
|
+
log_terms = ln_coeffs + ln_powers
|
|
247
|
+
|
|
248
|
+
# 3. Combined Log-Sum-Exp (Prevents Overflow)
|
|
249
|
+
max_log = np.max(log_terms, axis=0)
|
|
250
|
+
sum_exp = np.sum(np.exp(log_terms - max_log), axis=0)
|
|
251
|
+
|
|
252
|
+
# Direct combination avoids intermediate infinity
|
|
253
|
+
npbins[mask_sm, 1:] = np.exp(ln_pre + max_log) * sum_exp
|
|
254
|
+
|
|
255
|
+
elif kernel_type == 'Constant':
|
|
256
|
+
if np.any(mask_sm):
|
|
257
|
+
# Log Prefactor
|
|
258
|
+
ln_pre = np.log(4.0) - (nu * X_sm) - np.log(X_sm + eps) - 2.0 * np.log(T_vec + 2.0)
|
|
259
|
+
|
|
260
|
+
# Log Terms
|
|
261
|
+
ln_coeffs = -gammaln(nu * (k + 1))
|
|
262
|
+
ln_x_part = (nu * (k + 1)) * np.log((X_sm + eps) * nu)
|
|
263
|
+
ln_t_part = k * (np.log(T_vec + eps) - np.log(T_vec + 2.0))
|
|
264
|
+
|
|
265
|
+
log_terms = ln_coeffs + ln_x_part + ln_t_part
|
|
266
|
+
|
|
267
|
+
# Combined Log-Sum-Exp
|
|
268
|
+
max_log = np.max(log_terms, axis=0)
|
|
269
|
+
sum_exp = np.sum(np.exp(log_terms - max_log), axis=0)
|
|
270
|
+
|
|
271
|
+
npbins[mask_sm, 1:] = np.exp(ln_pre + max_log) * sum_exp
|
|
272
|
+
|
|
273
|
+
# --- Saddle-point Logic (Large x) ---
|
|
274
|
+
if np.any(mask_lg):
|
|
275
|
+
if kernel_type == 'Golovin':
|
|
276
|
+
tau = 1 - np.exp(-T_vec)
|
|
277
|
+
num = (1 - tau) * np.exp(-(tau + nu) * X_lg + (nu + 1) * X_lg * tau**(1./(nu + 1)))
|
|
278
|
+
den = (X_lg**1.5 * tau**((2.*(nu-1)+3)/(2.*(nu-1)+4)) * (2.*np.pi*(nu+1)/nu)**0.5)
|
|
279
|
+
corr = (1 - ((2.*(nu-1)+3)*(nu+2)) / (24.*X_lg * tau**(1/(nu+1)) * nu*(nu+1)))
|
|
280
|
+
npbins[mask_lg, 1:] = (num / den) * corr
|
|
281
|
+
|
|
282
|
+
elif kernel_type == 'Product':
|
|
283
|
+
phi = X_lg * (nu + 2) * T_vec**(1./(nu+2)) * (nu/(nu+1))**((nu+1)/(nu+2))
|
|
284
|
+
num = (nu+1) * np.exp(-X_lg * (T_vec + nu) + phi)
|
|
285
|
+
den = (X_lg**2.5 * (T_vec*(nu+1)/nu)**((2.*nu+3)/(2.*nu+4)) * (2.*np.pi*nu*(nu+2))**0.5)
|
|
286
|
+
corr = (1 - ((nu+3)*(2.*nu+3)) / (24.*X_lg * T_vec**(1./(nu+2)) * nu**((nu+1)/(nu+2)) * (nu+1)**(1./(nu+2)) * (nu+2)))
|
|
287
|
+
npbins[mask_lg, 1:] = (num / den) * corr
|
|
288
|
+
|
|
289
|
+
elif kernel_type == 'Constant':
|
|
290
|
+
R = T_vec / (T_vec + 2)
|
|
291
|
+
ys = np.ones((X_lg.shape[0], T_vec.shape[1]))
|
|
292
|
+
# Vectorized Newton-Raphson
|
|
293
|
+
for _ in range(5):
|
|
294
|
+
f = R - ys**(nu-1) * (ys - 1.0/X_lg)
|
|
295
|
+
df = -( (nu-1) * ys**(nu-2) * (ys - 1.0/X_lg) + ys**(nu-1) )
|
|
296
|
+
ys -= f/df
|
|
297
|
+
|
|
298
|
+
term1 = 0.9221 * 4. * nu * np.exp((ys - 1) * nu * X_lg)
|
|
299
|
+
term2 = (T_vec + 2)**2 * (ys**(nu-1))
|
|
300
|
+
term3 = (2. * np.pi * nu * (nu - (nu - 1) / (ys * X_lg)))**0.5
|
|
301
|
+
npbins[mask_lg, 1:] = term1 / (term2 * term3)
|
|
302
|
+
|
|
303
|
+
return npbins
|
|
304
|
+
|
|
305
|
+
def Feingold_dists(xbins, t, nu, E, B, gam, kernel_type='SBE'):
|
|
306
|
+
'''
|
|
307
|
+
Calculates Feingold's analytical number distribution function solution to
|
|
308
|
+
the SBE/SCE-SBE steady-state solution for Constant kernel.
|
|
309
|
+
|
|
310
|
+
Parameters
|
|
311
|
+
----------
|
|
312
|
+
xbins : TYPE
|
|
313
|
+
DESCRIPTION.
|
|
314
|
+
t : TYPE
|
|
315
|
+
DESCRIPTION.
|
|
316
|
+
nu : TYPE
|
|
317
|
+
DESCRIPTION.
|
|
318
|
+
E : TYPE
|
|
319
|
+
DESCRIPTION.
|
|
320
|
+
B : TYPE
|
|
321
|
+
DESCRIPTION.
|
|
322
|
+
gam : TYPE
|
|
323
|
+
DESCRIPTION.
|
|
324
|
+
kernel_type : TYPE, optional
|
|
325
|
+
DESCRIPTION. The default is 'SBE'.
|
|
326
|
+
|
|
327
|
+
Returns
|
|
328
|
+
-------
|
|
329
|
+
npbins : TYPE
|
|
330
|
+
DESCRIPTION.
|
|
331
|
+
|
|
332
|
+
'''
|
|
333
|
+
|
|
334
|
+
if kernel_type == 'SBE':
|
|
335
|
+
#n0_xt = (1./gamma(nu))*(nu**nu)*xbins[:,None]**(nu-1)*np.exp(-nu*xbins[:,None]) # initial dist.
|
|
336
|
+
#npbins = (n0_xt+gam*(np.exp(B*gam*t[None,:])-1)*np.exp(-gam*xbins[:,None]))/(1+(1./gam)*(np.exp(B*gam*t[None,:])-1))
|
|
337
|
+
|
|
338
|
+
x_col = xbins[:, None]
|
|
339
|
+
t_row = t[None, :]
|
|
340
|
+
|
|
341
|
+
# 1. Initial distribution (t=0)
|
|
342
|
+
# Using eps to protect against 0^(nu-1) singularity if xbins starts at 0 and nu < 1
|
|
343
|
+
eps = 1e-20
|
|
344
|
+
n0_xt = (1./gamma(nu)) * (nu**nu) * (x_col + eps)**(nu-1) * np.exp(-nu * x_col)
|
|
345
|
+
|
|
346
|
+
# 2. Exponent parameter P
|
|
347
|
+
P = B * gam * t_row
|
|
348
|
+
|
|
349
|
+
# 3. Stable decaying exponentials
|
|
350
|
+
exp_neg_P = np.exp(-P)
|
|
351
|
+
one_minus_exp = 1.0 - exp_neg_P
|
|
352
|
+
|
|
353
|
+
# 4. Numerically stable fraction
|
|
354
|
+
num = gam * n0_xt * exp_neg_P + (gam**2) * one_minus_exp * np.exp(-gam * x_col)
|
|
355
|
+
den = gam * exp_neg_P + one_minus_exp
|
|
356
|
+
|
|
357
|
+
npbins = num / den
|
|
358
|
+
|
|
359
|
+
elif kernel_type == 'SCE/SBE':
|
|
360
|
+
# E_new logic (kept as provided)
|
|
361
|
+
E_new = 1000. * E
|
|
362
|
+
#E_new = E
|
|
363
|
+
eta = 0.5 * E_new * gam * (2. - E_new)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# Implementing using the scaled versions:
|
|
367
|
+
arg = eta * xbins
|
|
368
|
+
|
|
369
|
+
decay_factor = np.exp(-(gam - 2.0 * eta) * xbins)
|
|
370
|
+
bessel_diff = i0e(arg) - i1e(arg)
|
|
371
|
+
|
|
372
|
+
npbins = (1 - E_new) * gam**2 * bessel_diff * decay_factor
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
return npbins
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
# def Scott_dists_working(xbins, Eagg, nu, t, kernel_type='Golovin', max_k=40):
|
|
380
|
+
# """
|
|
381
|
+
# Fully vectorized version of Scott distributions.
|
|
382
|
+
# Replaces mpmath and scalar loops with NumPy tensor operations.
|
|
383
|
+
|
|
384
|
+
# ELD NOTE: I used Google Gemini to optimize the old Scott_dists() function
|
|
385
|
+
# (see below; kept for posterity).
|
|
386
|
+
|
|
387
|
+
# """
|
|
388
|
+
# bins = len(xbins)
|
|
389
|
+
# Tlen = len(t)
|
|
390
|
+
# npbins = np.zeros([bins, Tlen])
|
|
391
|
+
|
|
392
|
+
# # 1. Initial State (t=0)
|
|
393
|
+
# npbins[:, 0] = (1./gamma(nu)) * (nu**nu) * xbins**(nu-1) * np.exp(-nu*xbins)
|
|
394
|
+
|
|
395
|
+
# # Time and Bins setup
|
|
396
|
+
# T = Eagg * t
|
|
397
|
+
# xt = 50
|
|
398
|
+
# mask_sm = xbins < xt
|
|
399
|
+
# mask_lg = ~mask_sm
|
|
400
|
+
|
|
401
|
+
# # Prepare 2D grid broadcast shapes
|
|
402
|
+
# X_sm = xbins[mask_sm, None] # (N_sm, 1)
|
|
403
|
+
# X_lg = xbins[mask_lg, None] # (N_lg, 1)
|
|
404
|
+
# T_vec = T[None, 1:] # (1, Tlen-1)
|
|
405
|
+
# k = np.arange(max_k).reshape(-1, 1, 1) # (K, 1, 1) for series summation
|
|
406
|
+
|
|
407
|
+
# if kernel_type == 'Golovin':
|
|
408
|
+
# tau = 1 - np.exp(-T_vec)
|
|
409
|
+
|
|
410
|
+
# # Series Approximation (Small x)
|
|
411
|
+
# if np.any(mask_sm):
|
|
412
|
+
# log_coeffs = (nu * (k+1)) * np.log(nu) - gammaln(k+2) - gammaln(nu * (k+1))
|
|
413
|
+
# term_powers = (tau**k) * (X_sm**(k * (nu+1)))
|
|
414
|
+
# series_sum = np.sum(np.exp(log_coeffs) * term_powers, axis=0)
|
|
415
|
+
|
|
416
|
+
# prefactor = (1 - tau) * X_sm**(nu-1) * np.exp(-(nu + tau) * X_sm)
|
|
417
|
+
# npbins[mask_sm, 1:] = prefactor * series_sum
|
|
418
|
+
|
|
419
|
+
# # Saddle-point Approximation (Large x)
|
|
420
|
+
# if np.any(mask_lg):
|
|
421
|
+
# num = (1 - tau) * np.exp(-(tau + nu) * X_lg + (nu + 1) * X_lg * tau**(1./(nu + 1)))
|
|
422
|
+
# den = (X_lg**1.5 * tau**((2.*(nu-1)+3)/(2.*(nu-1)+4)) * (2.*np.pi*(nu+1)/nu)**0.5)
|
|
423
|
+
# corr = (1 - ((2.*(nu-1)+3)*(nu+2)) / (24.*X_lg * tau**(1/(nu+1)) * nu*(nu+1)))
|
|
424
|
+
# npbins[mask_lg, 1:] = (num / den) * corr
|
|
425
|
+
|
|
426
|
+
# elif kernel_type == 'Product':
|
|
427
|
+
# # Series Approximation (Small x)
|
|
428
|
+
# if np.any(mask_sm):
|
|
429
|
+
# log_coeffs = ((nu+1)*(k+1)) * np.log(nu) - gammaln(k+2) - gammaln((nu+1)*(k+1))
|
|
430
|
+
# term_powers = (X_sm**((nu-1) + k*(nu+2))) * (T_vec**k)
|
|
431
|
+
# series_sum = np.sum(np.exp(log_coeffs) * term_powers, axis=0)
|
|
432
|
+
|
|
433
|
+
# npbins[mask_sm, 1:] = np.exp(-X_sm * (T_vec + nu)) * series_sum
|
|
434
|
+
|
|
435
|
+
# # Saddle-point Approximation (Large x)
|
|
436
|
+
# if np.any(mask_lg):
|
|
437
|
+
# phi = X_lg * (nu + 2) * T_vec**(1./(nu+2)) * (nu/(nu+1))**((nu+1)/(nu+2))
|
|
438
|
+
# num = (nu+1) * np.exp(-X_lg * (T_vec + nu) + phi)
|
|
439
|
+
# den = (X_lg**2.5 * (T_vec*(nu+1)/nu)**((2.*nu+3)/(2.*nu+4)) * (2.*np.pi*nu*(nu+2))**0.5)
|
|
440
|
+
# corr = (1 - ((nu+3)*(2.*nu+3)) / (24.*X_lg * T_vec**(1./(nu+2)) * nu**((nu+1)/(nu+2)) * (nu+1)**(1./(nu+2)) * (nu+2)))
|
|
441
|
+
# npbins[mask_lg, 1:] = (num / den) * corr
|
|
442
|
+
|
|
443
|
+
# elif kernel_type == 'Constant':
|
|
444
|
+
# # Series Approximation (Small x)
|
|
445
|
+
# if np.any(mask_sm):
|
|
446
|
+
# log_pre = np.log(4.0) - (nu * X_sm) - np.log(X_sm) - 2.0 * np.log(T_vec + 2.0)
|
|
447
|
+
# log_coeffs = -gammaln(nu * (k+1))
|
|
448
|
+
|
|
449
|
+
# #x_term = (X_sm * nu)**(nu * (k+1))
|
|
450
|
+
# #x_term = np.exp((nu*(k+1))*np.log((X_sm * nu)))
|
|
451
|
+
# #t_term = (T_vec / (T_vec + 2))**k
|
|
452
|
+
|
|
453
|
+
# ln_x_part = (nu * (k + 1)) * np.log(X_sm * nu)
|
|
454
|
+
# ln_t_part = k * (np.log(T_vec) - np.log(T_vec + 2.0))
|
|
455
|
+
# log_series_terms = log_coeffs + ln_x_part + ln_t_part # Shape (K, N_sm, T_len-1)
|
|
456
|
+
|
|
457
|
+
# # 3. Log-Sum-Exp Trick to prevent overflow
|
|
458
|
+
# # We find the max log term for each (bin, time) pair
|
|
459
|
+
# max_log = np.max(log_series_terms, axis=0)
|
|
460
|
+
# # Sum exp(log_terms - max_log) is numerically stable (max value is 1)
|
|
461
|
+
# sum_exp = np.sum(np.exp(log_series_terms - max_log), axis=0)
|
|
462
|
+
# # 4. Combine: exp(log_pre + max_log) * sum_exp
|
|
463
|
+
# # This effectively calculates exp(log_pre) * sum(exp(log_series_terms))
|
|
464
|
+
# npbins[mask_sm, 1:] = np.exp(log_pre + max_log) * sum_exp
|
|
465
|
+
|
|
466
|
+
# #series_sum = np.sum(np.exp(log_coeffs) * x_term * t_term, axis=0)
|
|
467
|
+
# #prefactor = (4. * np.exp(-nu * X_sm)) / (X_sm * (T_vec + 2)**2)
|
|
468
|
+
# #npbins[mask_sm, 1:] = prefactor * series_sum
|
|
469
|
+
|
|
470
|
+
# # Vectorized Newton-Raphson & Saddle-point (Large x)
|
|
471
|
+
# if np.any(mask_lg):
|
|
472
|
+
# R = T_vec / (T_vec + 2)
|
|
473
|
+
# ys = np.ones((X_lg.shape[0], T_vec.shape[1]))
|
|
474
|
+
# for _ in range(5): # 5 iterations for convergence
|
|
475
|
+
# f = R - ys**(nu-1) * (ys - 1.0/X_lg)
|
|
476
|
+
# df = -( (nu-1) * ys**(nu-2) * (ys - 1.0/X_lg) + ys**(nu-1) )
|
|
477
|
+
# ys -= f/df
|
|
478
|
+
|
|
479
|
+
# term1 = 0.9221 * 4. * nu * np.exp((ys - 1) * nu * X_lg)
|
|
480
|
+
# term2 = (T_vec + 2)**2 * (ys**(nu-1))
|
|
481
|
+
# term3 = (2. * np.pi * nu * (nu - (nu - 1) / (ys * X_lg)))**0.5
|
|
482
|
+
# npbins[mask_lg, 1:] = term1 / (term2 * term3)
|
|
483
|
+
|
|
484
|
+
# return npbins
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
# def Scott_dists_OLD(xbins,Eagg,nu,t,kernel_type='Golovin'):
|
|
488
|
+
|
|
489
|
+
# xt = 50
|
|
490
|
+
|
|
491
|
+
# #mpmath.dps = 10
|
|
492
|
+
|
|
493
|
+
# bins = len(xbins)
|
|
494
|
+
# Tlen = len(t)
|
|
495
|
+
|
|
496
|
+
# #mpmath.mp.prec = 500
|
|
497
|
+
|
|
498
|
+
# # Distribution functions for each bin
|
|
499
|
+
# npbins = np.zeros([bins, Tlen])
|
|
500
|
+
# #mpbins = np.zeros([bins, Tlen])
|
|
501
|
+
# #zpbins = np.zeros([bins, Tlen])
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
# # Calculate initial grid
|
|
505
|
+
# npbins[:,0] = (1./gamma(nu))*(nu**nu)*xbins**(nu-1)*np.exp(-nu*xbins)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
# #T = 0.00153*t # This seems right
|
|
509
|
+
|
|
510
|
+
# T = Eagg*t # This seems right
|
|
511
|
+
|
|
512
|
+
# if kernel_type=='Golovin':
|
|
513
|
+
|
|
514
|
+
# #tau = 1 - np.exp(-0.00153*t) # normalized time
|
|
515
|
+
|
|
516
|
+
# tau = 1 - np.exp(-T) # normalized time
|
|
517
|
+
|
|
518
|
+
# for xx in range(1,len(xbins)):
|
|
519
|
+
|
|
520
|
+
# if xbins[xx]<xt:
|
|
521
|
+
|
|
522
|
+
# for tt in range(1,len(t)):
|
|
523
|
+
|
|
524
|
+
# npbins[xx,tt] = (1-tau[tt])*xbins[xx]**(nu-1)*np.exp(-(nu+tau[tt])*xbins[xx])*\
|
|
525
|
+
# mpmath.nsum(lambda k: tau[tt]**(k) *nu**(nu*(k+1))/\
|
|
526
|
+
# (mpmath.factorial(k+1)*mpmath.gamma(nu*(k+1))) * xbins[xx]**(k*(nu+1)),[0,np.inf],method='direct')
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
# else: # Saddle-point approximation
|
|
531
|
+
|
|
532
|
+
# npbins[xx,1:] = (1-tau[1:])*np.exp(-(tau[1:]+nu)*\
|
|
533
|
+
# xbins[xx]+(nu+1)*xbins[xx]*tau[1:]**(1./(nu+1)))/(xbins[xx]**(1.5)*\
|
|
534
|
+
# tau[1:]**((2.*(nu-1)+3)/(2.*(nu-1)+4))*(2.*np.pi*(nu+1)/nu)**0.5)*\
|
|
535
|
+
# (1-((2.*(nu-1)+3)*(nu+2))/(24.*xbins[xx]*tau[1:]**(1/(nu+1))*nu*(nu+1)))
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
# elif kernel_type=='Product':
|
|
539
|
+
|
|
540
|
+
# #T = (7/40)*0.00959*t # 7/40 factor apparently is necessary here
|
|
541
|
+
|
|
542
|
+
# #T = 0.00153*t # This seems right
|
|
543
|
+
|
|
544
|
+
# #T = 0.00959*t # 7/40 factor apparently is necessary here
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
# for xx in range(1,len(xbins)):
|
|
548
|
+
|
|
549
|
+
# if xbins[xx]<xt:
|
|
550
|
+
|
|
551
|
+
# for tt in range(1,len(t)):
|
|
552
|
+
|
|
553
|
+
# npbins[xx,tt] = (np.exp(-xbins[xx]*(T[tt]+nu)))*mpmath.nsum(lambda k: ((xbins[xx])**((nu-1)+\
|
|
554
|
+
# k*(nu+2))*nu**((nu+1)*(k+1))/(mpmath.factorial(k+1)*mpmath.gamma((nu+1)*\
|
|
555
|
+
# (k+1))))*(T[tt])**k,[0,np.inf],method='direct')
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
# else: # Saddle-point approximation
|
|
559
|
+
|
|
560
|
+
# npbins[xx,1:] = ((nu+1)*np.exp(-xbins[xx]*(T[1:]+nu)+xbins[xx]*(nu+2)*T[1:]**(1./(nu+2))*(nu/(nu+1))**((nu+1)/(nu+2)))/\
|
|
561
|
+
# (xbins[xx]**(5./2)*(T[1:]*(nu+1)/nu)**((2.*(nu+2)-1)/(2.*(nu+2)))*(2.*np.pi*(nu*(nu+2)))**(1./2.)))*\
|
|
562
|
+
# (1-((nu+3)*(2.*(nu+2)-1))/(24.*xbins[xx]*T[1:]**(1./(nu+2))*nu**((nu+1)/(nu+2))*(nu+1)**(1./(nu+2))*(nu+2)))
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
# elif kernel_type=='Constant':
|
|
566
|
+
|
|
567
|
+
# #T = 0.0429*t
|
|
568
|
+
|
|
569
|
+
# #T = 0.0016*t
|
|
570
|
+
|
|
571
|
+
# for xx in range(1,len(xbins)):
|
|
572
|
+
|
|
573
|
+
# if xbins[xx]<xt:
|
|
574
|
+
|
|
575
|
+
# for tt in range(1,len(t)):
|
|
576
|
+
|
|
577
|
+
# npbins[xx,tt] = ((4.*np.exp(-nu*xbins[xx]))/\
|
|
578
|
+
# (xbins[xx]*(T[tt]+2)**2))*mpmath.nsum(lambda k:\
|
|
579
|
+
# ((xbins[xx]*nu)**(nu*(k+1))/\
|
|
580
|
+
# mpmath.gamma(nu*(k+1)))*(T[tt]/(T[tt]+2))**k,[0,np.inf],method='direct')
|
|
581
|
+
|
|
582
|
+
# else: # Saddle-point approximation
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
# ys = np.zeros_like(T)
|
|
586
|
+
|
|
587
|
+
# for tt in range(1,len(t)):
|
|
588
|
+
|
|
589
|
+
# ys_eq = lambda ys: (T[tt]/(T[tt]+2))-ys**(nu-1)*(ys-1./xbins[xx])
|
|
590
|
+
|
|
591
|
+
# ys[tt] = fsolve(ys_eq,1)[0]
|
|
592
|
+
|
|
593
|
+
# npbins[xx,1:] = 0.9221*4.*(nu)*np.exp((ys[1:]-1)*(nu)*xbins[xx])/((T[1:]+2)**2*(ys[1:]**(nu-1))*(2.*np.pi*nu*(nu-(nu-1)/(ys[1:]*xbins[xx])))**(0.5))
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
# return npbins
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
# def Feingold_dists_ORIG(xbins,t,nu,E,B,gam,kernel_type='SBE'):
|
|
600
|
+
|
|
601
|
+
# if kernel_type=='SBE':
|
|
602
|
+
# n0_xt = (1./gamma(nu))*(nu**nu)*xbins[:,None]**(nu-1)*np.exp(-nu*xbins[:,None]) # initial dist.
|
|
603
|
+
# npbins = (n0_xt+gam*(np.exp(B*gam*t[None,:])-1)*np.exp(-gam*xbins[:,None]))/(1+(1./gam)*(np.exp(B*gam*t[None,:])-1))
|
|
604
|
+
|
|
605
|
+
# elif kernel_type=='SCE/SBE':
|
|
606
|
+
|
|
607
|
+
# # NOTE: only valid for C = K*E and B = K*(1-E)
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
# #E_new = 1.*E
|
|
611
|
+
|
|
612
|
+
# E_new = 1000.*E
|
|
613
|
+
|
|
614
|
+
# eta = 0.5*E_new*gam*(2.-E_new)
|
|
615
|
+
|
|
616
|
+
# npbins = (1-E_new)*gam**2*(i0(eta*xbins)-i1(eta*xbins))*np.exp(-(gam-eta)*xbins)
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
# return npbins
|