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 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