AeroViz 0.1.7__py3-none-any.whl → 0.1.8__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.

Potentially problematic release.


This version of AeroViz might be problematic. Click here for more details.

@@ -0,0 +1,567 @@
1
+ # -*- coding: utf-8 -*-
2
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html
3
+ import warnings
4
+
5
+ import numpy as np
6
+ from scipy.special import jv, yv
7
+
8
+
9
+ def coerceDType(d):
10
+ if type(d) is not np.ndarray:
11
+ return np.array(d)
12
+ else:
13
+ return d
14
+
15
+
16
+ def MieQ(m, wavelength, diameter, nMedium=1.0, asDict=False, asCrossSection=False):
17
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#MieQ
18
+ nMedium = nMedium.real
19
+ m /= nMedium
20
+ wavelength /= nMedium
21
+ x = np.pi * diameter / wavelength
22
+ if x == 0:
23
+ return 0, 0, 0, 1.5, 0, 0, 0
24
+ elif x <= 0.05:
25
+ return RayleighMieQ(m, wavelength, diameter, nMedium, asDict)
26
+ elif x > 0.05:
27
+ nmax = np.round(2 + x + 4 * (x ** (1 / 3)))
28
+ n = np.arange(1, nmax + 1)
29
+ n1 = 2 * n + 1
30
+ n2 = n * (n + 2) / (n + 1)
31
+ n3 = n1 / (n * (n + 1))
32
+ x2 = x ** 2
33
+
34
+ an, bn = Mie_ab(m, x)
35
+
36
+ qext = (2 / x2) * np.sum(n1 * (an.real + bn.real))
37
+ qsca = (2 / x2) * np.sum(n1 * (an.real ** 2 + an.imag ** 2 + bn.real ** 2 + bn.imag ** 2))
38
+ qabs = qext - qsca
39
+
40
+ g1 = [an.real[1:int(nmax)],
41
+ an.imag[1:int(nmax)],
42
+ bn.real[1:int(nmax)],
43
+ bn.imag[1:int(nmax)]]
44
+ g1 = [np.append(x, 0.0) for x in g1]
45
+ g = (4 / (qsca * x2)) * np.sum(
46
+ (n2 * (an.real * g1[0] + an.imag * g1[1] + bn.real * g1[2] + bn.imag * g1[3])) + (
47
+ n3 * (an.real * bn.real + an.imag * bn.imag)))
48
+
49
+ qpr = qext - qsca * g
50
+ qback = (1 / x2) * (np.abs(np.sum(n1 * ((-1) ** n) * (an - bn))) ** 2)
51
+ qratio = qback / qsca
52
+ if asCrossSection:
53
+ css = np.pi * (diameter / 2) ** 2
54
+ cext = css * qext
55
+ csca = css * qsca
56
+ cabs = css * qabs
57
+ cpr = css * qpr
58
+ cback = css * qback
59
+ cratio = css * qratio
60
+ if asDict:
61
+ return dict(Cext=cext, Csca=csca, Cabs=cabs, g=g, Cpr=cpr, Cback=cback, Cratio=cratio)
62
+ else:
63
+ return cext, csca, cabs, g, cpr, cback, cratio
64
+ else:
65
+ if asDict:
66
+ return dict(Qext=qext, Qsca=qsca, Qabs=qabs, g=g, Qpr=qpr, Qback=qback, Qratio=qratio)
67
+ else:
68
+ return qext, qsca, qabs, g, qpr, qback, qratio
69
+
70
+
71
+ def Mie_ab(m, x):
72
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#Mie_ab
73
+ mx = m * x
74
+ nmax = np.round(2 + x + 4 * (x ** (1 / 3)))
75
+ nmx = np.round(max(nmax, np.abs(mx)) + 16)
76
+ n = np.arange(1, nmax + 1) #
77
+ nu = n + 0.5 #
78
+
79
+ sx = np.sqrt(0.5 * np.pi * x)
80
+
81
+ px = sx * jv(nu, x) #
82
+ p1x = np.append(np.sin(x), px[0:int(nmax) - 1]) #
83
+
84
+ chx = -sx * yv(nu, x) #
85
+ ch1x = np.append(np.cos(x), chx[0:int(nmax) - 1]) #
86
+
87
+ gsx = px - (0 + 1j) * chx #
88
+ gs1x = p1x - (0 + 1j) * ch1x #
89
+
90
+ # B&H Equation 4.89
91
+ Dn = np.zeros(int(nmx), dtype=complex)
92
+ for i in range(int(nmx) - 1, 1, -1):
93
+ Dn[i - 1] = (i / mx) - (1 / (Dn[i] + i / mx))
94
+
95
+ D = Dn[1:int(nmax) + 1] # Dn(mx), drop terms beyond nMax
96
+ da = D / m + n / x
97
+ db = m * D + n / x
98
+
99
+ an = (da * px - p1x) / (da * gsx - gs1x)
100
+ bn = (db * px - p1x) / (db * gsx - gs1x)
101
+
102
+ return an, bn
103
+
104
+
105
+ def Mie_cd(m, x):
106
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#Mie_cd
107
+ mx = m * x
108
+ nmax = np.round(2 + x + 4 * (x ** (1 / 3)))
109
+ nmx = np.round(max(nmax, np.abs(mx)) + 16)
110
+ n = np.arange(1, int(nmax) + 1)
111
+ nu = n + 0.5
112
+
113
+ cnx = np.zeros(int(nmx), dtype=complex)
114
+
115
+ for j in np.arange(nmx, 1, -1):
116
+ cnx[int(j) - 2] = j - mx * mx / (cnx[int(j) - 1] + j)
117
+
118
+ cnn = np.array([cnx[b] for b in range(0, len(n))])
119
+
120
+ jnx = np.sqrt(np.pi / (2 * x)) * jv(nu, x)
121
+ jnmx = np.sqrt((2 * mx) / np.pi) / jv(nu, mx)
122
+
123
+ yx = np.sqrt(np.pi / (2 * x)) * yv(nu, x)
124
+ hx = jnx + (1.0j) * yx
125
+
126
+ b1x = np.append(np.sin(x) / x, jnx[0:int(nmax) - 1])
127
+ y1x = np.append(-np.cos(x) / x, yx[0:int(nmax) - 1])
128
+
129
+ hn1x = b1x + (1.0j) * y1x
130
+ ax = x * b1x - n * jnx
131
+ ahx = x * hn1x - n * hx
132
+
133
+ numerator = jnx * ahx - hx * ax
134
+ c_denominator = ahx - hx * cnn
135
+ d_denominator = m * m * ahx - hx * cnn
136
+
137
+ cn = jnmx * numerator / c_denominator
138
+ dn = jnmx * m * numerator / d_denominator
139
+
140
+ return cn, dn
141
+
142
+
143
+ def RayleighMieQ(m, wavelength, diameter, nMedium=1.0, asDict=False, asCrossSection=False):
144
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#RayleighMieQ
145
+ nMedium = nMedium.real
146
+ m /= nMedium
147
+ wavelength /= nMedium
148
+ x = np.pi * diameter / wavelength
149
+ if x == 0:
150
+ return 0, 0, 0, 1.5, 0, 0, 0
151
+ elif x > 0:
152
+ LL = (m ** 2 - 1) / (m ** 2 + 2) # Lorentz-Lorenz term
153
+ LLabsSq = np.abs(LL) ** 2
154
+ qsca = 8 * LLabsSq * (x ** 4) / 3 # B&H eq 5.8
155
+ qabs = 4 * x * LL.imag # B&H eq. 5.11
156
+ qext = qsca + qabs
157
+ qback = 1.5 * qsca # B&H eq. 5.9
158
+ qratio = 1.5
159
+ g = 0
160
+ qpr = qext
161
+ if asCrossSection:
162
+ css = np.pi * (diameter / 2) ** 2
163
+ cext = css * qext
164
+ csca = css * qsca
165
+ cabs = css * qabs
166
+ cpr = css * qpr
167
+ cback = css * qback
168
+ cratio = css * qratio
169
+ if asDict:
170
+ return dict(Cext=cext, Csca=csca, Cabs=cabs, g=g, Cpr=cpr, Cback=cback, Cratio=cratio)
171
+ else:
172
+ return cext, csca, cabs, g, cpr, cback, cratio
173
+ else:
174
+ if asDict:
175
+ return dict(Qext=qext, Qsca=qsca, Qabs=qabs, g=g, Qpr=qpr, Qback=qback, Qratio=qratio)
176
+ else:
177
+ return qext, qsca, qabs, g, qpr, qback, qratio
178
+
179
+
180
+ def AutoMieQ(m, wavelength, diameter, nMedium=1.0, crossover=0.01, asDict=False, asCrossSection=False):
181
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#AutoMieQ
182
+ nMedium = nMedium.real
183
+ m_eff = m / nMedium
184
+ wavelength_eff = wavelength / nMedium
185
+ x_eff = np.pi * diameter / wavelength_eff
186
+ if x_eff == 0:
187
+ return 0, 0, 0, 1.5, 0, 0, 0
188
+ elif x_eff < crossover:
189
+ return RayleighMieQ(m, wavelength, diameter, nMedium, asDict=asDict, asCrossSection=asCrossSection)
190
+ else:
191
+ return MieQ(m, wavelength, diameter, nMedium, asDict=asDict, asCrossSection=asCrossSection)
192
+
193
+
194
+ def LowFrequencyMieQ(m, wavelength, diameter, nMedium=1.0, asDict=False, asCrossSection=False):
195
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#LowFrequencyMieQ
196
+ nMedium = nMedium.real
197
+ m /= nMedium
198
+ wavelength /= nMedium
199
+ x = np.pi * diameter / wavelength
200
+ if x == 0:
201
+ return 0, 0, 0, 1.5, 0, 0, 0
202
+ elif x > 0:
203
+ n = np.arange(1, 3)
204
+ n1 = 2 * n + 1
205
+ n2 = n * (n + 2) / (n + 1)
206
+ n3 = n1 / (n * (n + 1))
207
+ x2 = x ** 2
208
+
209
+ an, bn = LowFrequencyMie_ab(m, x)
210
+
211
+ qext = (2 / x2) * np.sum(n1 * (an.real + bn.real))
212
+ qsca = (2 / x2) * np.sum(n1 * (an.real ** 2 + an.imag ** 2 + bn.real ** 2 + bn.imag ** 2))
213
+ qabs = qext - qsca
214
+
215
+ g1 = [an.real[1:2], an.imag[1:2], bn.real[1:2], bn.imag[1:2]]
216
+ g1 = [np.append(x, 0.0) for x in g1]
217
+ g = (4 / (qsca * x2)) * np.sum(
218
+ (n2 * (an.real * g1[0] + an.imag * g1[1] + bn.real * g1[2] + bn.imag * g1[3])) + (
219
+ n3 * (an.real * bn.real + an.imag * bn.imag)))
220
+
221
+ qpr = qext - qsca * g
222
+ qback = (1 / x2) * (np.abs(np.sum(n1 * ((-1) ** n) * (an - bn))) ** 2)
223
+ qratio = qback / qsca
224
+
225
+ if asCrossSection:
226
+ css = np.pi * (diameter / 2) ** 2
227
+ cext = css * qext
228
+ csca = css * qsca
229
+ cabs = css * qabs
230
+ cpr = css * qpr
231
+ cback = css * qback
232
+ cratio = css * qratio
233
+ if asDict:
234
+ return dict(Cext=cext, Csca=csca, Cabs=cabs, g=g, Cpr=cpr, Cback=cback, Cratio=cratio)
235
+ else:
236
+ return cext, csca, cabs, g, cpr, cback, cratio
237
+ else:
238
+ if asDict:
239
+ return dict(Qext=qext, Qsca=qsca, Qabs=qabs, g=g, Qpr=qpr, Qback=qback, Qratio=qratio)
240
+ else:
241
+ return qext, qsca, qabs, g, qpr, qback, qratio
242
+
243
+
244
+ def LowFrequencyMie_ab(m, x):
245
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#LowFrequencyMie_ab
246
+ # B&H page 131
247
+ m2 = m ** 2
248
+ LL = (m ** 2 - 1) / (m ** 2 + 2)
249
+ x3 = x ** 3
250
+ x5 = x ** 5
251
+ x6 = x ** 6
252
+
253
+ a1 = (-2j * x3 / 3) * LL - (2j * x5 / 5) * LL * (m2 - 2) / (m2 + 2) + (4 * x6 / 9) * (LL ** 2)
254
+ a2 = (-1j * x5 / 15) * (m2 - 1) / (2 * m2 + 3)
255
+ b1 = (-1j * x5 / 45) * (m2 - 1)
256
+ b2 = 0 + 0j
257
+ an = np.append(a1, a2)
258
+ bn = np.append(b1, b2)
259
+ return an, bn
260
+
261
+
262
+ def AutoMie_ab(m, x):
263
+ if x < 0.5:
264
+ return LowFrequencyMie_ab(m, x)
265
+ else:
266
+ return Mie_ab(m, x)
267
+
268
+
269
+ def Mie_SD(m, wavelength, dp, ndp, nMedium=1.0, SMPS=True, interpolate=False, asDict=False):
270
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#Mie_SD
271
+ nMedium = nMedium.real
272
+ m /= nMedium
273
+ wavelength /= nMedium
274
+ dp = coerceDType(dp)
275
+ ndp = coerceDType(ndp)
276
+ _length = np.size(dp)
277
+ Q_ext = np.zeros(_length)
278
+ Q_sca = np.zeros(_length)
279
+ Q_abs = np.zeros(_length)
280
+ Q_pr = np.zeros(_length)
281
+ Q_back = np.zeros(_length)
282
+ Q_ratio = np.zeros(_length)
283
+ g = np.zeros(_length)
284
+
285
+ # scaling of 1e-6 to cast in units of inverse megameters - see docs
286
+ aSDn = np.pi * ((dp / 2) ** 2) * ndp * (1e-6)
287
+ # _logdp = np.log10(dp)
288
+
289
+ for i in range(_length):
290
+ Q_ext[i], Q_sca[i], Q_abs[i], g[i], Q_pr[i], Q_back[i], Q_ratio[i] = AutoMieQ(m, wavelength, dp[i], nMedium)
291
+
292
+ if SMPS:
293
+ Bext = np.sum(Q_ext * aSDn)
294
+ Bsca = np.sum(Q_sca * aSDn)
295
+ Babs = Bext - Bsca
296
+ Bback = np.sum(Q_back * aSDn)
297
+ Bratio = np.sum(Q_ratio * aSDn)
298
+ bigG = np.sum(g * Q_sca * aSDn) / np.sum(Q_sca * aSDn)
299
+ Bpr = Bext - bigG * Bsca
300
+ else:
301
+ Bext = np.trapz(Q_ext * aSDn, dp)
302
+ Bsca = np.trapz(Q_sca * aSDn, dp)
303
+ Babs = Bext - Bsca
304
+ Bback = np.trapz(Q_back * aSDn, dp)
305
+ Bratio = np.trapz(Q_ratio * aSDn, dp)
306
+ bigG = np.trapz(g * Q_sca * aSDn, dp) / np.trapz(Q_sca * aSDn, dp)
307
+ Bpr = Bext - bigG * Bsca
308
+
309
+ if asDict:
310
+ return dict(Bext=Bext, Bsca=Bsca, Babs=Babs, G=bigG, Bpr=Bpr, Bback=Bback, Bratio=Bratio)
311
+ else:
312
+ return Bext, Bsca, Babs, bigG, Bpr, Bback, Bratio
313
+
314
+
315
+ def ScatteringFunction(m, wavelength, diameter, nMedium=1.0, minAngle=0, maxAngle=180, angularResolution=0.5,
316
+ space='theta', angleMeasure='radians', normalization=None):
317
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#ScatteringFunction
318
+ nMedium = nMedium.real
319
+ m /= nMedium
320
+ wavelength /= nMedium
321
+ x = np.pi * diameter / wavelength
322
+
323
+ _steps = int(1 + (maxAngle - minAngle) / angularResolution) # default 361
324
+
325
+ if angleMeasure in ['radians', 'RADIANS', 'rad', 'RAD']:
326
+ adjust = np.pi / 180
327
+ elif angleMeasure in ['gradians', 'GRADIANS', 'grad', 'GRAD']:
328
+ adjust = 1 / 200
329
+ else:
330
+ adjust = 1
331
+
332
+ if space in ['q', 'qspace', 'QSPACE', 'qSpace']:
333
+ # _steps *= 10
334
+ _steps += 1
335
+ if minAngle == 0:
336
+ minAngle = 1e-5
337
+ # measure = np.logspace(np.log10(minAngle),np.log10(maxAngle),_steps)*np.pi/180
338
+ measure = np.linspace(minAngle, maxAngle, _steps) * np.pi / 180
339
+ _q = True
340
+ else:
341
+ measure = np.linspace(minAngle, maxAngle, _steps) * adjust
342
+ _q = False
343
+ if x == 0:
344
+ return measure, 0, 0, 0
345
+ _measure = np.linspace(minAngle, maxAngle, _steps) * np.pi / 180
346
+ SL = np.zeros(_steps)
347
+ SR = np.zeros(_steps)
348
+ SU = np.zeros(_steps)
349
+ for j in range(_steps):
350
+ u = np.cos(_measure[j])
351
+ S1, S2 = MieS1S2(m, x, u)
352
+ SL[j] = (np.sum(np.conjugate(S1) * S1)).real
353
+ SR[j] = (np.sum(np.conjugate(S2) * S2)).real
354
+ SU[j] = (SR[j] + SL[j]) / 2
355
+ if normalization in ['m', 'M', 'max', 'MAX']:
356
+ SL /= np.max(SL)
357
+ SR /= np.max(SR)
358
+ SU /= np.max(SU)
359
+ elif normalization in ['t', 'T', 'total', 'TOTAL']:
360
+ SL /= np.trapz(SL, measure)
361
+ SR /= np.trapz(SR, measure)
362
+ SU /= np.trapz(SU, measure)
363
+ if _q:
364
+ measure = (4 * np.pi / wavelength) * np.sin(measure / 2) * (diameter / 2)
365
+ return measure, SL, SR, SU
366
+
367
+
368
+ def SF_SD(m, wavelength, dp, ndp, nMedium=1.0, minAngle=0, maxAngle=180, angularResolution=0.5, space='theta',
369
+ angleMeasure='radians', normalization=None):
370
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#SF_SD
371
+ nMedium = nMedium.real
372
+ m /= nMedium
373
+ wavelength /= nMedium
374
+
375
+ _steps = int(1 + (maxAngle - minAngle) / angularResolution)
376
+ ndp = coerceDType(ndp)
377
+ dp = coerceDType(dp)
378
+ SL = np.zeros(_steps)
379
+ SR = np.zeros(_steps)
380
+ SU = np.zeros(_steps)
381
+ kwargs = {'minAngle': minAngle,
382
+ 'maxAngle': maxAngle,
383
+ 'angularResolution': angularResolution,
384
+ 'space': space,
385
+ 'normalization': None}
386
+ for n, d in zip(ndp, dp):
387
+ measure, l, r, u = ScatteringFunction(m, wavelength, d, **kwargs)
388
+ SL += l * n
389
+ SR += r * n
390
+ SU += u * n
391
+ if normalization in ['n', 'N', 'number', 'particles']:
392
+ _n = np.trapz(ndp, dp)
393
+ SL /= _n
394
+ SR /= _n
395
+ SU /= _n
396
+ elif normalization in ['m', 'M', 'max', 'MAX']:
397
+ SL /= np.max(SL)
398
+ SR /= np.max(SR)
399
+ SU /= np.max(SU)
400
+ elif normalization in ['t', 'T', 'total', 'TOTAL']:
401
+ SL /= np.trapz(SL, measure)
402
+ SR /= np.trapz(SR, measure)
403
+ SU /= np.trapz(SU, measure)
404
+ return measure, SL, SR, SU
405
+
406
+
407
+ def MieS1S2(m, x, mu):
408
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#MieS1S2
409
+ nmax = np.round(2 + x + 4 * np.power(x, 1 / 3))
410
+ an, bn = AutoMie_ab(m, x)
411
+ pin, taun = MiePiTau(mu, nmax)
412
+ n = np.arange(1, int(nmax) + 1)
413
+ n2 = (2 * n + 1) / (n * (n + 1))
414
+ S1 = np.sum(n2[0:len(an)] * (an * pin[0:len(an)] + bn * taun[0:len(bn)]))
415
+ S2 = np.sum(n2[0:len(an)] * (an * taun[0:len(an)] + bn * pin[0:len(bn)]))
416
+ return S1, S2
417
+
418
+
419
+ def MiePiTau(mu, nmax):
420
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#MiePiTau
421
+ p = np.zeros(int(nmax))
422
+ t = np.zeros(int(nmax))
423
+ p[0] = 1
424
+ p[1] = 3 * mu
425
+ t[0] = mu
426
+ t[1] = 3.0 * np.cos(2 * np.arccos(mu))
427
+ for n in range(2, int(nmax)):
428
+ p[n] = ((2 * n + 1) * (mu * p[n - 1]) - (n + 1) * p[n - 2]) / n
429
+ t[n] = (n + 1) * mu * p[n] - (n + 2) * p[n - 1]
430
+ return p, t
431
+
432
+
433
+ def MatrixElements(m, wavelength, diameter, mu, nMedium=1.0):
434
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#MatrixElements
435
+ nMedium = nMedium.real
436
+ m /= nMedium
437
+ wavelength /= nMedium
438
+ x = np.pi * diameter / wavelength
439
+ # B&H eqs. 4.77, where mu=cos(theta)
440
+ S1, S2 = MieS1S2(m, x, mu)
441
+ S11 = 0.5 * (np.abs(S2) ** 2 + np.abs(S1) ** 2)
442
+ S12 = 0.5 * (np.abs(S2) ** 2 - np.abs(S1) ** 2)
443
+ S33 = 0.5 * (np.conjugate(S2) * S1 + S2 * np.conjugate(S1))
444
+ S34 = 0.5j * (S1 * np.conjugate(S2) - S2 * np.conjugate(S1))
445
+ return S11, S12, S33, S34
446
+
447
+
448
+ def MieQ_withDiameterRange(m, wavelength, nMedium=1.0, diameterRange=(10, 1000), nd=1000, logD=False):
449
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#MieQ_withDiameterRange
450
+ nMedium = nMedium.real
451
+ m /= nMedium
452
+ wavelength /= nMedium
453
+ if logD:
454
+ diameters = np.logspace(np.log10(diameterRange[0]), np.log10(diameterRange[1]), nd)
455
+ else:
456
+ diameters = np.linspace(diameterRange[0], diameterRange[1], nd)
457
+ _qD = [AutoMieQ(m, wavelength, diameter) for diameter in diameters]
458
+ qext = np.array([q[0] for q in _qD])
459
+ qsca = np.array([q[1] for q in _qD])
460
+ qabs = np.array([q[2] for q in _qD])
461
+ g = np.array([q[3] for q in _qD])
462
+ qpr = np.array([q[4] for q in _qD])
463
+ qback = np.array([q[5] for q in _qD])
464
+ qratio = np.array([q[6] for q in _qD])
465
+ return diameters, qext, qsca, qabs, g, qpr, qback, qratio
466
+
467
+
468
+ def MieQ_withWavelengthRange(m, diameter, nMedium=1.0, wavelengthRange=(100, 1600), nw=1000, logW=False):
469
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#MieQ_withWavelengthRange
470
+ nMedium = nMedium.real
471
+ _m = m / nMedium
472
+ _wavelengthRange = tuple([x / nMedium for x in wavelengthRange])
473
+ if type(_m) == complex and len(_wavelengthRange) == 2:
474
+ if logW:
475
+ wavelengths = np.logspace(np.log10(_wavelengthRange[0]), np.log10(_wavelengthRange[1]), nw)
476
+ else:
477
+ wavelengths = np.linspace(_wavelengthRange[0], _wavelengthRange[1], nw)
478
+ _qD = [AutoMieQ(_m, wavelength, diameter) for wavelength in wavelengths]
479
+ elif type(_m) in [np.ndarray, list, tuple] and len(_wavelengthRange) == len(_m):
480
+ wavelengths = _wavelengthRange
481
+ _qD = [MieQ(emm, wavelength, diameter) for emm, wavelength in zip(_m, wavelengths)]
482
+ else:
483
+ warnings.warn("Error: the size of the input data is mismatched. Please examine your inputs and try again.")
484
+ return
485
+
486
+ qext = np.array([q[0] for q in _qD])
487
+ qsca = np.array([q[1] for q in _qD])
488
+ qabs = np.array([q[2] for q in _qD])
489
+ g = np.array([q[3] for q in _qD])
490
+ qpr = np.array([q[4] for q in _qD])
491
+ qback = np.array([q[5] for q in _qD])
492
+ qratio = np.array([q[6] for q in _qD])
493
+ return wavelengths, qext, qsca, qabs, g, qpr, qback, qratio
494
+
495
+
496
+ def MieQ_withSizeParameterRange(m, nMedium=1.0, xRange=(1, 10), nx=1000, logX=False):
497
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#MieQ_withSizeParameterRange
498
+ nMedium = nMedium.real
499
+ m /= nMedium
500
+ _xRange = tuple([x * nMedium for x in xRange]) # I think
501
+ if logX:
502
+ xValues = list(np.logspace(np.log10(_xRange[0]), np.log10(_xRange[1]), nx))
503
+ else:
504
+ xValues = list(np.linspace(_xRange[0], _xRange[1], nx))
505
+ dValues = [1000 * x / np.pi for x in xValues]
506
+ _qD = [AutoMieQ(m, 1000, d) for d in dValues]
507
+ qext = np.array([q[0] for q in _qD])
508
+ qsca = np.array([q[1] for q in _qD])
509
+ qabs = np.array([q[2] for q in _qD])
510
+ g = np.array([q[3] for q in _qD])
511
+ qpr = np.array([q[4] for q in _qD])
512
+ qback = np.array([q[5] for q in _qD])
513
+ qratio = np.array([q[6] for q in _qD])
514
+ return xValues, qext, qsca, qabs, g, qpr, qback, qratio
515
+
516
+
517
+ def Mie_Lognormal(m, wavelength, geoStdDev, geoMean, numberOfParticles, nMedium=1.0, numberOfBins=10000, lower=1,
518
+ upper=1000, gamma=[1], returnDistribution=False, decomposeMultimodal=False, asDict=False):
519
+ # http://pymiescatt.readthedocs.io/en/latest/forward.html#Mie_Lognormal
520
+ nMedium = nMedium.real
521
+ m /= nMedium
522
+ wavelength /= nMedium
523
+ ithPart = lambda gammai, dp, dpgi, sigmagi: (gammai / (np.sqrt(2 * np.pi) * np.log(sigmagi) * dp)) * np.exp(
524
+ -(np.log(dp) - np.log(dpgi)) ** 2 / (2 * np.log(sigmagi) ** 2))
525
+ dp = np.logspace(np.log10(lower), np.log10(upper), numberOfBins)
526
+ if all([type(x) in [list, tuple, np.ndarray] for x in [geoStdDev, geoMean]]):
527
+ # multimodal
528
+ if len(gamma) == 1 and (len(geoStdDev) == len(geoMean) > 1):
529
+ # gamma is distributed equally among modes
530
+ gamma = [1 for x in geoStdDev]
531
+ gamma = [float(x / np.sum(gamma)) for x in gamma]
532
+ ndpi = [numberOfParticles * ithPart(g, dp, dpg, sg) for g, dpg, sg in zip(gamma, geoMean, geoStdDev)]
533
+ ndp = np.sum(ndpi, axis=0)
534
+ elif len(gamma) == len(geoStdDev) == len(geoMean):
535
+ # gamma is fully specified for each mode
536
+ gamma = [float(x / np.sum(gamma)) for x in gamma]
537
+ ndpi = [numberOfParticles * ithPart(g, dp, dpg, sg) for g, dpg, sg in zip(gamma, geoMean, geoStdDev)]
538
+ ndp = np.sum(ndpi, axis=0)
539
+ else:
540
+ # user fucked up
541
+ warnings.warn("Not enough parameters to fully specify each mode.")
542
+ return None
543
+ else:
544
+ # unimodal
545
+ decomposeMultimodal = False
546
+ ndp = numberOfParticles * ithPart(1, dp, geoMean, geoStdDev)
547
+ if ndp[-1] > np.max(ndp) / 100 or ndp[0] > np.max(ndp) / 100:
548
+ warnings.warn(
549
+ "Warning: distribution may not be compact on the specified interval. Consider using a higher upper bound.")
550
+ Bext, Bsca, Babs, bigG, Bpr, Bback, Bratio = Mie_SD(m, wavelength, dp, ndp, SMPS=False)
551
+ if returnDistribution:
552
+ if decomposeMultimodal:
553
+ if asDict == True:
554
+ return dict(Bext=Bext, Bsca=Bsca, Babs=Babs, bigG=bigG, Bpr=Bpr, Bback=Bback,
555
+ Bratio=Bratio), dp, ndp, ndpi
556
+ else:
557
+ return Bext, Bsca, Babs, bigG, Bpr, Bback, Bratio, dp, ndp, ndpi
558
+ else:
559
+ if asDict == True:
560
+ return dict(Bext=Bext, Bsca=Bsca, Babs=Babs, bigG=bigG, Bpr=Bpr, Bback=Bback, Bratio=Bratio), dp, ndp
561
+ else:
562
+ return Bext, Bsca, Babs, bigG, Bpr, Bback, Bratio, dp, ndp
563
+ else:
564
+ if asDict == True:
565
+ return dict(Bext=Bext, Bsca=Bsca, Babs=Babs, bigG=bigG, Bpr=Bpr, Bback=Bback, Bratio=Bratio)
566
+ else:
567
+ return Bext, Bsca, Babs, bigG, Bpr, Bback, Bratio