AeroViz 0.1.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.
Potentially problematic release.
This version of AeroViz might be problematic. Click here for more details.
- AeroViz/__init__.py +15 -0
- AeroViz/dataProcess/Chemistry/__init__.py +63 -0
- AeroViz/dataProcess/Chemistry/_calculate.py +27 -0
- AeroViz/dataProcess/Chemistry/_isoropia.py +99 -0
- AeroViz/dataProcess/Chemistry/_mass_volume.py +175 -0
- AeroViz/dataProcess/Chemistry/_ocec.py +184 -0
- AeroViz/dataProcess/Chemistry/_partition.py +29 -0
- AeroViz/dataProcess/Chemistry/_teom.py +16 -0
- AeroViz/dataProcess/Optical/_IMPROVE.py +61 -0
- AeroViz/dataProcess/Optical/__init__.py +62 -0
- AeroViz/dataProcess/Optical/_absorption.py +54 -0
- AeroViz/dataProcess/Optical/_extinction.py +36 -0
- AeroViz/dataProcess/Optical/_mie.py +16 -0
- AeroViz/dataProcess/Optical/_mie_sd.py +143 -0
- AeroViz/dataProcess/Optical/_scattering.py +30 -0
- AeroViz/dataProcess/SizeDistr/__init__.py +61 -0
- AeroViz/dataProcess/SizeDistr/__merge.py +250 -0
- AeroViz/dataProcess/SizeDistr/_merge.py +245 -0
- AeroViz/dataProcess/SizeDistr/_merge_v1.py +254 -0
- AeroViz/dataProcess/SizeDistr/_merge_v2.py +243 -0
- AeroViz/dataProcess/SizeDistr/_merge_v3.py +518 -0
- AeroViz/dataProcess/SizeDistr/_merge_v4.py +424 -0
- AeroViz/dataProcess/SizeDistr/_size_distr.py +93 -0
- AeroViz/dataProcess/VOC/__init__.py +19 -0
- AeroViz/dataProcess/VOC/_potential_par.py +76 -0
- AeroViz/dataProcess/__init__.py +11 -0
- AeroViz/dataProcess/core/__init__.py +92 -0
- AeroViz/plot/__init__.py +7 -0
- AeroViz/plot/distribution/__init__.py +1 -0
- AeroViz/plot/distribution/distribution.py +582 -0
- AeroViz/plot/improve/__init__.py +1 -0
- AeroViz/plot/improve/improve.py +240 -0
- AeroViz/plot/meteorology/__init__.py +1 -0
- AeroViz/plot/meteorology/meteorology.py +317 -0
- AeroViz/plot/optical/__init__.py +2 -0
- AeroViz/plot/optical/aethalometer.py +77 -0
- AeroViz/plot/optical/optical.py +388 -0
- AeroViz/plot/templates/__init__.py +8 -0
- AeroViz/plot/templates/contour.py +47 -0
- AeroViz/plot/templates/corr_matrix.py +108 -0
- AeroViz/plot/templates/diurnal_pattern.py +42 -0
- AeroViz/plot/templates/event_evolution.py +65 -0
- AeroViz/plot/templates/koschmieder.py +156 -0
- AeroViz/plot/templates/metal_heatmap.py +57 -0
- AeroViz/plot/templates/regression.py +256 -0
- AeroViz/plot/templates/scatter.py +130 -0
- AeroViz/plot/templates/templates.py +398 -0
- AeroViz/plot/timeseries/__init__.py +1 -0
- AeroViz/plot/timeseries/timeseries.py +317 -0
- AeroViz/plot/utils/__init__.py +3 -0
- AeroViz/plot/utils/_color.py +71 -0
- AeroViz/plot/utils/_decorator.py +74 -0
- AeroViz/plot/utils/_unit.py +55 -0
- AeroViz/process/__init__.py +31 -0
- AeroViz/process/core/DataProc.py +19 -0
- AeroViz/process/core/SizeDist.py +90 -0
- AeroViz/process/core/__init__.py +4 -0
- AeroViz/process/method/PyMieScatt_update.py +567 -0
- AeroViz/process/method/__init__.py +2 -0
- AeroViz/process/method/mie_theory.py +258 -0
- AeroViz/process/method/prop.py +62 -0
- AeroViz/process/script/AbstractDistCalc.py +143 -0
- AeroViz/process/script/Chemical.py +176 -0
- AeroViz/process/script/IMPACT.py +49 -0
- AeroViz/process/script/IMPROVE.py +161 -0
- AeroViz/process/script/Others.py +65 -0
- AeroViz/process/script/PSD.py +103 -0
- AeroViz/process/script/PSD_dry.py +94 -0
- AeroViz/process/script/__init__.py +5 -0
- AeroViz/process/script/retrieve_RI.py +70 -0
- AeroViz/rawDataReader/__init__.py +68 -0
- AeroViz/rawDataReader/core/__init__.py +397 -0
- AeroViz/rawDataReader/script/AE33.py +31 -0
- AeroViz/rawDataReader/script/AE43.py +34 -0
- AeroViz/rawDataReader/script/APS_3321.py +47 -0
- AeroViz/rawDataReader/script/Aurora.py +38 -0
- AeroViz/rawDataReader/script/BC1054.py +46 -0
- AeroViz/rawDataReader/script/EPA_vertical.py +18 -0
- AeroViz/rawDataReader/script/GRIMM.py +35 -0
- AeroViz/rawDataReader/script/IGAC_TH.py +104 -0
- AeroViz/rawDataReader/script/IGAC_ZM.py +90 -0
- AeroViz/rawDataReader/script/MA350.py +45 -0
- AeroViz/rawDataReader/script/NEPH.py +57 -0
- AeroViz/rawDataReader/script/OCEC_LCRES.py +34 -0
- AeroViz/rawDataReader/script/OCEC_RES.py +28 -0
- AeroViz/rawDataReader/script/SMPS_TH.py +41 -0
- AeroViz/rawDataReader/script/SMPS_aim11.py +51 -0
- AeroViz/rawDataReader/script/SMPS_genr.py +51 -0
- AeroViz/rawDataReader/script/TEOM.py +46 -0
- AeroViz/rawDataReader/script/Table.py +28 -0
- AeroViz/rawDataReader/script/VOC_TH.py +30 -0
- AeroViz/rawDataReader/script/VOC_ZM.py +37 -0
- AeroViz/rawDataReader/script/__init__.py +22 -0
- AeroViz/tools/__init__.py +3 -0
- AeroViz/tools/database.py +94 -0
- AeroViz/tools/dataclassifier.py +117 -0
- AeroViz/tools/datareader.py +66 -0
- AeroViz-0.1.0.dist-info/LICENSE +21 -0
- AeroViz-0.1.0.dist-info/METADATA +117 -0
- AeroViz-0.1.0.dist-info/RECORD +102 -0
- AeroViz-0.1.0.dist-info/WHEEL +5 -0
- AeroViz-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from ..core import _writter, _run_process
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
|
|
5
|
+
'Optical',
|
|
6
|
+
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Optical(_writter):
|
|
11
|
+
|
|
12
|
+
## scatter
|
|
13
|
+
@_run_process('Optical - SAE', 'SAE')
|
|
14
|
+
def SAE(self, df_sca):
|
|
15
|
+
from ._scattering import _SAE
|
|
16
|
+
|
|
17
|
+
out = _SAE(df_sca)
|
|
18
|
+
|
|
19
|
+
return self, out
|
|
20
|
+
|
|
21
|
+
## absorption
|
|
22
|
+
@_run_process('Optical - absCoe', 'absCoe')
|
|
23
|
+
def absCoe(self, df_ae33, abs_band=[550]):
|
|
24
|
+
from ._absorption import _absCoe
|
|
25
|
+
|
|
26
|
+
out = _absCoe(df_ae33, abs_band)
|
|
27
|
+
|
|
28
|
+
return self, out
|
|
29
|
+
|
|
30
|
+
@_run_process('Optical - AAE', 'AAE')
|
|
31
|
+
def AAE(self, df_abs):
|
|
32
|
+
from ._absorption import _AAE
|
|
33
|
+
|
|
34
|
+
out = _AAE(df_abs)
|
|
35
|
+
|
|
36
|
+
return self, out
|
|
37
|
+
|
|
38
|
+
## extinction
|
|
39
|
+
@_run_process('Optical - basic', 'opt_basic')
|
|
40
|
+
def basic(self, df_abs, df_sca, df_ec=None, df_mass=None, df_no2=None):
|
|
41
|
+
from ._extinction import _basic
|
|
42
|
+
|
|
43
|
+
out = _basic(df_abs, df_sca, df_ec, df_mass, df_no2)
|
|
44
|
+
|
|
45
|
+
return self, out
|
|
46
|
+
|
|
47
|
+
@_run_process('Optical - Mie', 'Mie')
|
|
48
|
+
def Mie(self, df_psd, df_m, wave_length=550):
|
|
49
|
+
from ._mie import _mie
|
|
50
|
+
|
|
51
|
+
out = _mie(df_psd, df_m, wave_length)
|
|
52
|
+
|
|
53
|
+
return self, out
|
|
54
|
+
|
|
55
|
+
@_run_process('Optical - IMPROVE', 'IMPROVE')
|
|
56
|
+
def IMPROVE(self, df_mass, df_RH, method='revised'):
|
|
57
|
+
# _fc = __import__(f'_IMPROVE._{method}')
|
|
58
|
+
from ._IMPROVE import _revised
|
|
59
|
+
|
|
60
|
+
out = _revised(df_mass, df_RH)
|
|
61
|
+
|
|
62
|
+
return self, out
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
def _absCoe(df, abs_band):
|
|
2
|
+
import numpy as n
|
|
3
|
+
from scipy.optimize import curve_fit
|
|
4
|
+
|
|
5
|
+
band = n.array([370, 470, 520, 590, 660, 880, 950])
|
|
6
|
+
|
|
7
|
+
df_out = {}
|
|
8
|
+
|
|
9
|
+
def _get_slope(__df):
|
|
10
|
+
func = lambda _x, _sl, _int: _sl * _x + _int
|
|
11
|
+
popt, pcov = curve_fit(func, band, __df.values)
|
|
12
|
+
|
|
13
|
+
return func(n.array(abs_band), *popt)
|
|
14
|
+
|
|
15
|
+
MAE = n.array([18.47, 14.54, 13.14, 11.58, 10.35, 7.77, 7.19]) * 1e-3
|
|
16
|
+
df_abs = (df.copy() * MAE).dropna().copy()
|
|
17
|
+
|
|
18
|
+
df_out = df_abs.apply(_get_slope, axis=1, result_type='expand').reindex(df.index)
|
|
19
|
+
df_out.columns = [f'abs_{_band}' for _band in abs_band]
|
|
20
|
+
|
|
21
|
+
df_out['eBC'] = df['BC6']
|
|
22
|
+
|
|
23
|
+
return df_out
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _AAE(df):
|
|
27
|
+
import numpy as n
|
|
28
|
+
from scipy.optimize import curve_fit
|
|
29
|
+
|
|
30
|
+
def _AAEcalc(_df):
|
|
31
|
+
## parameter
|
|
32
|
+
MAE = n.array([18.47, 14.54, 13.14, 11.58, 10.35, 7.77, 7.19]) * 1e-3
|
|
33
|
+
band = n.array([370, 470, 520, 590, 660, 880, 950])
|
|
34
|
+
_df *= MAE
|
|
35
|
+
|
|
36
|
+
## 7 pts fitting
|
|
37
|
+
## function
|
|
38
|
+
def _get_slope(__df):
|
|
39
|
+
func = lambda _x, _sl, _int: _sl * _x + _int
|
|
40
|
+
popt, pcov = curve_fit(func, n.log(band), n.log(__df))
|
|
41
|
+
|
|
42
|
+
return popt
|
|
43
|
+
|
|
44
|
+
## calculate
|
|
45
|
+
_AAE = _df.apply(_get_slope, axis=1, result_type='expand')
|
|
46
|
+
_AAE.columns = ['slope', 'intercept']
|
|
47
|
+
|
|
48
|
+
return _AAE
|
|
49
|
+
|
|
50
|
+
df_out = _AAEcalc(df[['BC1', 'BC2', 'BC3', 'BC4', 'BC5', 'BC6', 'BC7']].dropna())
|
|
51
|
+
df_out = df_out.mask((-df_out.slope < 0.8) | (-df_out.slope > 2.)).copy()
|
|
52
|
+
|
|
53
|
+
df_out['eBC'] = df['BC6']
|
|
54
|
+
return df_out.reindex(df.index)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from AeroViz.dataProcess.core import _union_index
|
|
2
|
+
from pandas import DataFrame
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _basic(df_abs, df_sca, df_ec, df_mass, df_no2):
|
|
6
|
+
df_abs, df_sca, df_ec, df_mass, df_no2 = _union_index(df_abs, df_sca, df_ec, df_mass, df_no2)
|
|
7
|
+
|
|
8
|
+
df_out = DataFrame()
|
|
9
|
+
|
|
10
|
+
## abs and sca coe
|
|
11
|
+
df_out['abs'] = df_abs.copy()
|
|
12
|
+
df_out['sca'] = df_sca.copy()
|
|
13
|
+
|
|
14
|
+
## extinction coe.
|
|
15
|
+
df_out['ext'] = df_out['abs'] + df_out['sca']
|
|
16
|
+
|
|
17
|
+
## SSA
|
|
18
|
+
df_out['SSA'] = df_out['sca'] / df_out['ext']
|
|
19
|
+
|
|
20
|
+
## MAE, MSE, MEE
|
|
21
|
+
if df_mass is not None:
|
|
22
|
+
df_out['MAE'] = df_out['abs'] / df_mass
|
|
23
|
+
df_out['MSE'] = df_out['sca'] / df_mass
|
|
24
|
+
df_out['MEE'] = df_out['MSE'] + df_out['MAE']
|
|
25
|
+
|
|
26
|
+
## gas absorbtion
|
|
27
|
+
if df_no2 is not None:
|
|
28
|
+
df_out['abs_gas'] = df_no2 * .33
|
|
29
|
+
df_out['sca_gas'] = 10
|
|
30
|
+
df_out['ext_all'] = df_out['ext'] + df_out['abs_gas'] + df_out['sca_gas']
|
|
31
|
+
|
|
32
|
+
## other
|
|
33
|
+
if df_ec is not None:
|
|
34
|
+
df_out['eBC'] = df_ec / 1e3
|
|
35
|
+
|
|
36
|
+
return df_out
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# from PyMieScatt import Mie_SD
|
|
2
|
+
# from PyMieScatt import Mie_SD
|
|
3
|
+
|
|
4
|
+
from ._mie_sd import Mie_SD
|
|
5
|
+
from pandas import date_range, concat, DataFrame, to_numeric
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _mie(_psd_ori, _RI_ori, _wave):
|
|
9
|
+
_ori_idx = _psd_ori.index.copy()
|
|
10
|
+
_cal_idx = _psd_ori.loc[_RI_ori.dropna().index].dropna(how='all').index
|
|
11
|
+
|
|
12
|
+
_psd, _RI = _psd_ori.loc[_cal_idx], _RI_ori.loc[_cal_idx]
|
|
13
|
+
|
|
14
|
+
_out = Mie_SD(_RI.values, 550, _psd)
|
|
15
|
+
|
|
16
|
+
return _out.reindex(_ori_idx)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# http://pymiescatt.readthedocs.io/en/latest/forward.html
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.integrate import trapezoid
|
|
5
|
+
from scipy.special import jv, yv
|
|
6
|
+
import warnings
|
|
7
|
+
from pandas import date_range, concat, DataFrame, to_numeric, to_datetime, Series
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def coerceDType(d):
|
|
11
|
+
if type(d) is not np.ndarray:
|
|
12
|
+
return np.array(d)
|
|
13
|
+
else:
|
|
14
|
+
return d
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def Mie_ab(m, x, nmax, df_n):
|
|
18
|
+
nu = df_n.copy() + 0.5
|
|
19
|
+
n1 = 2 * df_n.copy() + 1
|
|
20
|
+
|
|
21
|
+
sx = np.sqrt(0.5 * np.pi * x)
|
|
22
|
+
px = sx.reshape(-1, 1) * jv(nu, x.reshape(-1, 1))
|
|
23
|
+
chx = -sx.reshape(-1, 1) * yv(nu, x.reshape(-1, 1))
|
|
24
|
+
|
|
25
|
+
p1x = concat([DataFrame(np.sin(x)), px.mask(df_n == nmax.reshape(-1, 1))], axis=1)
|
|
26
|
+
p1x.columns = np.arange(len(p1x.keys()))
|
|
27
|
+
p1x = p1x[df_n.keys()]
|
|
28
|
+
|
|
29
|
+
ch1x = concat([DataFrame(np.cos(x)), chx.mask(df_n == nmax.reshape(-1, 1))], axis=1)
|
|
30
|
+
ch1x.columns = np.arange(len(ch1x.keys()))
|
|
31
|
+
ch1x = ch1x[df_n.keys()]
|
|
32
|
+
|
|
33
|
+
gsx = px - (0 + 1j) * chx
|
|
34
|
+
gs1x = p1x - (0 + 1j) * ch1x
|
|
35
|
+
|
|
36
|
+
mx = m.reshape(-1, 1) * x
|
|
37
|
+
nmx = np.round(np.max(np.hstack([[nmax] * m.size, np.abs(mx)]).reshape(m.size, 2, -1), axis=1) + 16)
|
|
38
|
+
|
|
39
|
+
df_qext = DataFrame(columns=m, index=df_n.index)
|
|
40
|
+
df_qsca = DataFrame(columns=m, index=df_n.index)
|
|
41
|
+
|
|
42
|
+
df_n /= x.reshape(-1, 1)
|
|
43
|
+
for _bin_idx, (_nmx_ary, _mx, _nmax) in enumerate(zip(nmx.T, mx.T, nmax)):
|
|
44
|
+
|
|
45
|
+
df_D = DataFrame(np.nan, index=np.arange(m.size), columns=df_n.keys())
|
|
46
|
+
|
|
47
|
+
Dn_lst = []
|
|
48
|
+
for _nmx, _uni_idx in DataFrame(_nmx_ary).groupby(0).groups.items():
|
|
49
|
+
|
|
50
|
+
_inv_mx = 1 / _mx[_uni_idx]
|
|
51
|
+
|
|
52
|
+
Dn = np.zeros((_uni_idx.size, int(_nmx)), dtype=complex)
|
|
53
|
+
for _idx in range(int(_nmx) - 1, 1, -1):
|
|
54
|
+
Dn[:, _idx - 1] = (_idx * _inv_mx) - (1 / (Dn[:, _idx] + _idx * _inv_mx))
|
|
55
|
+
|
|
56
|
+
Dn_lst.append(Dn[:, 1: int(_nmax) + 1])
|
|
57
|
+
df_D.loc[_uni_idx, 0: int(_nmax) - 1] = Dn[:, 1: int(_nmax) + 1]
|
|
58
|
+
|
|
59
|
+
## other parameter
|
|
60
|
+
_df_n, _px, _p1x, _gsx, _gs1x, _n1 = df_n.loc[_bin_idx], px.loc[_bin_idx], p1x.loc[_bin_idx], gsx.loc[_bin_idx], \
|
|
61
|
+
gs1x.loc[_bin_idx], n1.loc[_bin_idx].values
|
|
62
|
+
|
|
63
|
+
_da = df_D / m.reshape(-1, 1) + _df_n
|
|
64
|
+
_db = df_D * m.reshape(-1, 1) + _df_n
|
|
65
|
+
|
|
66
|
+
_an = (_da * _px - _p1x) / (_da * _gsx - _gs1x)
|
|
67
|
+
_bn = (_db * _px - _p1x) / (_db * _gsx - _gs1x)
|
|
68
|
+
|
|
69
|
+
_real_an, _real_bn = np.real(_an), np.real(_bn)
|
|
70
|
+
_imag_an, _imag_bn = np.imag(_an), np.imag(_bn)
|
|
71
|
+
|
|
72
|
+
_pr_qext = np.nansum(_n1 * (_real_an + _real_bn), axis=1)
|
|
73
|
+
_pr_qsca = np.nansum(_n1 * (_real_an ** 2 + _real_bn ** 2 + _imag_an ** 2 + _imag_bn ** 2), axis=1)
|
|
74
|
+
|
|
75
|
+
df_qext.loc[_bin_idx] = _pr_qext
|
|
76
|
+
df_qsca.loc[_bin_idx] = _pr_qsca
|
|
77
|
+
|
|
78
|
+
return df_qext, df_qsca
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def MieQ(m_ary, wavelength, diameter):
|
|
82
|
+
# http://pymiescatt.readthedocs.io/en/latest/forward.html#MieQ
|
|
83
|
+
|
|
84
|
+
x = np.pi * diameter / wavelength
|
|
85
|
+
|
|
86
|
+
nmax = np.round(2 + x + 4 * (x ** (1 / 3)))
|
|
87
|
+
|
|
88
|
+
df_n = DataFrame([np.arange(1, nmax.max() + 1)] * nmax.size)
|
|
89
|
+
df_n = df_n.mask(df_n > nmax.reshape(-1, 1))
|
|
90
|
+
|
|
91
|
+
n1 = 2 * df_n + 1
|
|
92
|
+
n2 = df_n * (df_n + 2) / (df_n + 1)
|
|
93
|
+
n3 = n1 / (df_n * (df_n + 1))
|
|
94
|
+
x2 = x ** 2
|
|
95
|
+
|
|
96
|
+
_qext, _qsca = Mie_ab(m_ary, x, nmax, df_n)
|
|
97
|
+
|
|
98
|
+
qext = (2 / x2).reshape(-1, 1) * _qext
|
|
99
|
+
qsca = (2 / x2).reshape(-1, 1) * _qsca
|
|
100
|
+
|
|
101
|
+
# return qext.astype(float).values.T, qsca.astype(float).values.T,
|
|
102
|
+
return qext.values.T.astype(float), qsca.values.T.astype(float)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def Mie_SD(m_ary, wavelength, psd, multp_m_in1psd=False, dt_chunk_size=10, q_table=False):
|
|
106
|
+
m_ary = coerceDType(m_ary)
|
|
107
|
+
if type(psd) is not DataFrame:
|
|
108
|
+
psd = DataFrame(psd).T
|
|
109
|
+
|
|
110
|
+
if (len(m_ary) != len(psd)) & ~multp_m_in1psd:
|
|
111
|
+
raise ValueError('"m array" size should be same as "psd" size')
|
|
112
|
+
|
|
113
|
+
dp = psd.keys().values
|
|
114
|
+
ndp = psd.values
|
|
115
|
+
aSDn = np.pi * ((dp / 2) ** 2) * ndp * 1e-6
|
|
116
|
+
|
|
117
|
+
if q_table:
|
|
118
|
+
qext, qsca = q_table
|
|
119
|
+
else:
|
|
120
|
+
qext, qsca = MieQ(m_ary, wavelength, dp)
|
|
121
|
+
|
|
122
|
+
if multp_m_in1psd:
|
|
123
|
+
# print('\tcalculate ext')
|
|
124
|
+
|
|
125
|
+
aSDn_all = np.repeat(aSDn, m_ary.size, axis=0).reshape(len(aSDn), m_ary.size, -1)
|
|
126
|
+
|
|
127
|
+
qext_all = np.repeat(qext[np.newaxis, :, :], len(aSDn), axis=0).reshape(*aSDn_all.shape)
|
|
128
|
+
qsca_all = np.repeat(qsca[np.newaxis, :, :], len(aSDn), axis=0).reshape(*aSDn_all.shape)
|
|
129
|
+
|
|
130
|
+
df_ext = DataFrame(trapezoid(aSDn_all * qext_all), columns=m_ary, index=psd.index).astype(float)
|
|
131
|
+
df_sca = DataFrame(trapezoid(aSDn_all * qsca_all), columns=m_ary, index=psd.index).astype(float)
|
|
132
|
+
df_abs = df_ext - df_sca
|
|
133
|
+
# print('\tdone')
|
|
134
|
+
|
|
135
|
+
return dict(ext=df_ext, sca=df_sca, abs=df_abs)
|
|
136
|
+
|
|
137
|
+
else:
|
|
138
|
+
df_out = DataFrame(index=psd.index)
|
|
139
|
+
df_out['ext'] = trapezoid(qext * aSDn).astype(float)
|
|
140
|
+
df_out['sca'] = trapezoid(qsca * aSDn).astype(float)
|
|
141
|
+
df_out['abs'] = df_out['ext'] - df_out['sca']
|
|
142
|
+
|
|
143
|
+
return df_out
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.optimize import curve_fit
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
'_SAE',
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _SAE(df):
|
|
10
|
+
def _SAEcalc(_df):
|
|
11
|
+
## parameter
|
|
12
|
+
band = np.array([450, 550, 700]) * 1e-3
|
|
13
|
+
|
|
14
|
+
## 3 pts fitting
|
|
15
|
+
## function
|
|
16
|
+
def _get_slope(__df):
|
|
17
|
+
func = lambda _x, _sl, _int: _sl * _x + _int
|
|
18
|
+
popt, pcov = curve_fit(func, np.log(band), np.log(__df))
|
|
19
|
+
|
|
20
|
+
return popt
|
|
21
|
+
|
|
22
|
+
## calculate
|
|
23
|
+
_SAE = _df.apply(_get_slope, axis=1, result_type='expand')
|
|
24
|
+
_SAE.columns = ['slope', 'intercept']
|
|
25
|
+
|
|
26
|
+
return _SAE
|
|
27
|
+
|
|
28
|
+
df_out = _SAEcalc(df[['B', 'G', 'R']].dropna())
|
|
29
|
+
|
|
30
|
+
return df_out.reindex(df.index)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from ..core import _writter, _run_process
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
|
|
5
|
+
'SizeDistr',
|
|
6
|
+
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SizeDistr(_writter):
|
|
11
|
+
|
|
12
|
+
## basic
|
|
13
|
+
@_run_process('SizeDistr - basic', 'distr_basic')
|
|
14
|
+
def basic(self, df, hybrid_bin_start_loc=None, unit='nm', bin_range=(0, 20000), input_type='norm'):
|
|
15
|
+
from ._size_distr import _basic
|
|
16
|
+
|
|
17
|
+
out = _basic(df, hybrid_bin_start_loc, unit, bin_range, input_type)
|
|
18
|
+
|
|
19
|
+
return self, out
|
|
20
|
+
|
|
21
|
+
## merge
|
|
22
|
+
@_run_process('SizeDistr - merge_SMPS_APS_v4', 'distr_merge')
|
|
23
|
+
def merge_SMPS_APS_v4(self, df_smps, df_aps, df_pm25, aps_unit='um',
|
|
24
|
+
smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True,
|
|
25
|
+
times_range=(0.8, 1.25, .05)):
|
|
26
|
+
from ._merge_v4 import merge_SMPS_APS
|
|
27
|
+
|
|
28
|
+
out = merge_SMPS_APS(df_smps, df_aps, df_pm25, aps_unit, smps_overlap_lowbound, aps_fit_highbound, dndsdv_alg,
|
|
29
|
+
times_range)
|
|
30
|
+
|
|
31
|
+
return self, out
|
|
32
|
+
|
|
33
|
+
## merge
|
|
34
|
+
@_run_process('SizeDistr - merge_SMPS_APS_v3', 'distr_merge')
|
|
35
|
+
def merge_SMPS_APS_v3(self, df_smps, df_aps, aps_unit='um',
|
|
36
|
+
smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True):
|
|
37
|
+
from ._merge_v3 import merge_SMPS_APS
|
|
38
|
+
|
|
39
|
+
out = merge_SMPS_APS(df_smps, df_aps, aps_unit, smps_overlap_lowbound, aps_fit_highbound, dndsdv_alg)
|
|
40
|
+
|
|
41
|
+
return self, out
|
|
42
|
+
|
|
43
|
+
## merge
|
|
44
|
+
@_run_process('SizeDistr - merge_SMPS_APS_v2', 'distr_merge')
|
|
45
|
+
def merge_SMPS_APS_v2(self, df_smps, df_aps, aps_unit='um',
|
|
46
|
+
smps_overlap_lowbound=500, aps_fit_highbound=1000):
|
|
47
|
+
from ._merge_v2 import merge_SMPS_APS
|
|
48
|
+
|
|
49
|
+
out = merge_SMPS_APS(df_smps, df_aps, aps_unit, smps_overlap_lowbound, aps_fit_highbound)
|
|
50
|
+
|
|
51
|
+
return self, out
|
|
52
|
+
|
|
53
|
+
## merge
|
|
54
|
+
@_run_process('SizeDistr - merge_SMPS_APS_v1', 'distr_merge')
|
|
55
|
+
def merge_SMPS_APS(self, df_smps, df_aps, aps_unit='um', shift_mode='mobility',
|
|
56
|
+
smps_overlap_lowbound=523, aps_fit_highbound=800):
|
|
57
|
+
from ._merge_v1 import _merge_SMPS_APS
|
|
58
|
+
|
|
59
|
+
out = _merge_SMPS_APS(df_smps, df_aps, aps_unit, shift_mode, smps_overlap_lowbound, aps_fit_highbound)
|
|
60
|
+
|
|
61
|
+
return self, out
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from datetime import datetime as dtm
|
|
2
|
+
from pandas import DataFrame, to_datetime
|
|
3
|
+
# from scipy.interpolate import interp1d
|
|
4
|
+
from scipy.interpolate import UnivariateSpline as unvpline, interp1d
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
__all__ = ['_merge_SMPS_APS']
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def __test_plot(smpsx, smps, apsx, aps, mergex, merge, mergeox, mergeo, _sh):
|
|
11
|
+
from matplotlib.pyplot import subplots, close, show, rcParams
|
|
12
|
+
|
|
13
|
+
## parameter
|
|
14
|
+
# '''
|
|
15
|
+
## plot
|
|
16
|
+
fig, ax = subplots()
|
|
17
|
+
|
|
18
|
+
ax.plot(smpsx, smps, c='#ff794c', label='smps', marker='o', lw=2)
|
|
19
|
+
ax.plot(apsx, aps, c='#4c79ff', label='aps', marker='o', lw=2)
|
|
20
|
+
ax.plot(mergex, merge, c='#79796a', label='merge')
|
|
21
|
+
# ax.plot(mergeox,mergeo,c='#111111',label='mergeo',marker='o',lw=.75)
|
|
22
|
+
|
|
23
|
+
ax.set(xscale='log', yscale='log', )
|
|
24
|
+
|
|
25
|
+
ax.legend(framealpha=0, )
|
|
26
|
+
ax.set_title((_sh ** 2)[0], fontsize=13)
|
|
27
|
+
|
|
28
|
+
show()
|
|
29
|
+
close()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# '''
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Overlap fitting
|
|
36
|
+
## Create a fitting func. by smps data
|
|
37
|
+
## return : shift factor
|
|
38
|
+
def _overlap_fitting(_smps_ori, _aps_ori, _smps_lb, _aps_hb):
|
|
39
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range fitting\033[0m")
|
|
40
|
+
|
|
41
|
+
## overlap fitting
|
|
42
|
+
## parmeter
|
|
43
|
+
_dt_indx = _smps_ori.index
|
|
44
|
+
|
|
45
|
+
## overlap diameter data
|
|
46
|
+
_aps = _aps_ori[_aps_ori.keys()[_aps_ori.keys() < _aps_hb]].copy()
|
|
47
|
+
_smps = _smps_ori[_smps_ori.keys()[_smps_ori.keys() > _smps_lb]].copy()
|
|
48
|
+
|
|
49
|
+
## use SMPS data apply power law fitting
|
|
50
|
+
## y = Ax^B, A = e**coefa, B = coefb, x = logx, y = logy
|
|
51
|
+
## ref : http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
|
52
|
+
## power law fit to SMPS num conc at upper bins to log curve
|
|
53
|
+
|
|
54
|
+
## coefficient A, B
|
|
55
|
+
_smps_qc_cond = ((_smps != 0) & np.isfinite(_smps))
|
|
56
|
+
_smps_qc = _smps.where(_smps_qc_cond)
|
|
57
|
+
|
|
58
|
+
_size = _smps_qc_cond.sum(axis=1)
|
|
59
|
+
_size = _size.where(_size != 0.).copy()
|
|
60
|
+
|
|
61
|
+
_logx, _logy = np.log(_smps_qc.keys()._data.astype(float)), np.log(_smps_qc)
|
|
62
|
+
_x, _y, _xy, _xx = _logx.sum(), _logy.sum(axis=1), (_logx * _logy).sum(axis=1), (_logx ** 2).sum()
|
|
63
|
+
|
|
64
|
+
_coeB = ((_size * _xy - _x * _y) / (_size * _xx - _x ** 2.))
|
|
65
|
+
_coeA = np.exp((_y - _coeB * _x) / _size).values.reshape(-1, 1)
|
|
66
|
+
_coeB = _coeB.values.reshape(-1, 1)
|
|
67
|
+
|
|
68
|
+
## rebuild shift smps data by coe. A, B
|
|
69
|
+
## x_shift = (y_ori/A)**(1/B)
|
|
70
|
+
_aps_shift_x = (_aps / _coeA) ** (1 / _coeB)
|
|
71
|
+
_aps_shift_x = _aps_shift_x.where(np.isfinite(_aps_shift_x))
|
|
72
|
+
|
|
73
|
+
## the least squares of diameter
|
|
74
|
+
## the shift factor which the cklosest to 1
|
|
75
|
+
_shift_factor = (_aps_shift_x.keys()._data.astype(float) / _aps_shift_x)
|
|
76
|
+
_shift_factor.columns = range(len(_aps_shift_x.keys()))
|
|
77
|
+
|
|
78
|
+
_dropna_idx = _shift_factor.dropna(how='all').index.copy()
|
|
79
|
+
|
|
80
|
+
## use the target function to get the similar aps and smps bin
|
|
81
|
+
## S2 = sum( (smps_fit_line(dia) - aps(dia*shift_factor) )**2 )
|
|
82
|
+
## assumption : the same diameter between smps and aps should get the same conc.
|
|
83
|
+
|
|
84
|
+
## be sure they art in log value
|
|
85
|
+
_S2 = DataFrame(index=_aps_shift_x.index)
|
|
86
|
+
_dia_table = DataFrame(np.full(_aps_shift_x.shape, _aps_shift_x.keys()),
|
|
87
|
+
columns=_aps_shift_x.keys(), index=_aps_shift_x.index)
|
|
88
|
+
for _idx, _factor in _shift_factor.items():
|
|
89
|
+
_smps_fit_df = _coeA * (_dia_table / _factor.to_frame().values) ** _coeB
|
|
90
|
+
_S2[_idx] = ((_smps_fit_df - _aps) ** 2).sum(axis=1)
|
|
91
|
+
|
|
92
|
+
_least_squ_idx = _S2.idxmin(axis=1).loc[_dropna_idx]
|
|
93
|
+
|
|
94
|
+
_shift_factor_out = DataFrame(_shift_factor.loc[_dropna_idx].values[range(len(_dropna_idx)), _least_squ_idx.values],
|
|
95
|
+
index=_dropna_idx).reindex(_dt_indx)
|
|
96
|
+
|
|
97
|
+
return _shift_factor_out, (DataFrame(_coeA, index=_dt_indx), DataFrame(_coeB, index=_dt_indx))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
## Remove big shift data ()
|
|
101
|
+
## Return : aps, smps, shift (without big shift data)
|
|
102
|
+
def _shift_data_process(_shift):
|
|
103
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mshift-data quality control\033[0m")
|
|
104
|
+
|
|
105
|
+
_rho = _shift ** 2
|
|
106
|
+
_shift = _shift.mask((~np.isfinite(_shift)) | (_rho > 2) | (_rho < 0.3))
|
|
107
|
+
|
|
108
|
+
_qc_index = _shift.mask((_rho < 0.6) | (_shift.isna())).dropna().index
|
|
109
|
+
|
|
110
|
+
return _qc_index, _shift
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# return _smps.loc[~_big_shift], _aps.loc[~_big_shift], _shift[~_big_shift].reshape(-1,1)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
## Create merge data
|
|
117
|
+
## shift all smps bin and remove the aps bin which smaller than the latest old smps bin
|
|
118
|
+
## Return : merge bins, merge data, density
|
|
119
|
+
def _merge_data(_smps_ori, _aps_ori, _shift_ori, _shift_mode, _smps_lb, _aps_hb, _coe):
|
|
120
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mcreate merge data\033[0m")
|
|
121
|
+
|
|
122
|
+
_ori_idx = _smps_ori.index
|
|
123
|
+
_merge_idx = _smps_ori.loc[_aps_ori.dropna(how='all').index].dropna(how='all').index
|
|
124
|
+
|
|
125
|
+
_uni_idx, _count = np.unique(np.hstack((_smps_ori.dropna(how='all').index, _aps_ori.dropna(how='all').index,
|
|
126
|
+
_shift_ori.dropna(how='all').index)), return_counts=True)
|
|
127
|
+
|
|
128
|
+
_merge_idx = to_datetime(np.unique(_uni_idx[_count == 3]))
|
|
129
|
+
|
|
130
|
+
_smps, _aps, _shift = _smps_ori.loc[_merge_idx], _aps_ori.loc[_merge_idx], _shift_ori.loc[_merge_idx].values
|
|
131
|
+
|
|
132
|
+
## parameter
|
|
133
|
+
_coeA, _coeB = _coe[0].loc[_merge_idx], _coe[1].loc[_merge_idx]
|
|
134
|
+
_smps_key, _aps_key = _smps.keys()._data.astype(float), _aps.keys()._data.astype(float)
|
|
135
|
+
|
|
136
|
+
_test = 1000
|
|
137
|
+
|
|
138
|
+
# _cntr = (_smps_lb+_aps_hb)/2
|
|
139
|
+
_cntr = _test
|
|
140
|
+
_bin_lb = _smps_key[-1]
|
|
141
|
+
|
|
142
|
+
## make shift bins
|
|
143
|
+
_smps_bin = np.full(_smps.shape, _smps_key)
|
|
144
|
+
_aps_bin = np.full(_aps.shape, _aps_key)
|
|
145
|
+
# _std_bin = _smps_key.tolist()+_aps_key[_aps_key>_smps_key[-1]].tolist()
|
|
146
|
+
_std_bin = np.geomspace(_smps_key[0], _aps_key[-1], 230)
|
|
147
|
+
_std_bin_merge = _std_bin[(_std_bin < _cntr) & (_std_bin > _bin_lb)]
|
|
148
|
+
_std_bin_inte1 = _std_bin[_std_bin <= _bin_lb]
|
|
149
|
+
_std_bin_inte2 = _std_bin[_std_bin >= _cntr]
|
|
150
|
+
|
|
151
|
+
if _shift_mode == 'mobility':
|
|
152
|
+
_aps_bin /= _shift
|
|
153
|
+
|
|
154
|
+
elif _shift_mode == 'aerodynamic':
|
|
155
|
+
_smps_bin *= _shift
|
|
156
|
+
|
|
157
|
+
## merge
|
|
158
|
+
_merge_lst = []
|
|
159
|
+
for _bin_smps, _bin_aps, _dt_smps, _dt_aps, _sh in zip(_smps_bin, _aps_bin, _smps.values, _aps.values, _shift):
|
|
160
|
+
## remove
|
|
161
|
+
|
|
162
|
+
## keep complete smps bins and data
|
|
163
|
+
## remove the aps bin data lower than smps bin
|
|
164
|
+
_condi = _bin_aps >= _bin_smps[-1]
|
|
165
|
+
|
|
166
|
+
_merge_bin = np.hstack((_bin_smps, _bin_aps[_condi]))
|
|
167
|
+
_merge_dt = np.hstack((_dt_smps, _dt_aps[_condi]))
|
|
168
|
+
|
|
169
|
+
# _merge_fit_loc = (_merge_bin<_aps_hb)&(_merge_bin>_smps_lb)
|
|
170
|
+
_merge_fit_loc = (_merge_bin < 1500) & (_merge_bin > _smps_lb)
|
|
171
|
+
|
|
172
|
+
## coeA and coeB
|
|
173
|
+
_unvpl_fc = unvpline(np.log(_merge_bin[_merge_fit_loc]), np.log(_merge_dt[_merge_fit_loc]), s=50)
|
|
174
|
+
# _unvpl_fc = unvpline(_merge_bin[_merge_fit_loc],_merge_dt[_merge_fit_loc],s=150)
|
|
175
|
+
# _inte_log_fc = interp1d(n.log10(_merge_bin[_merge_fit_loc]),n.log10(_merge_dt[_merge_fit_loc]),
|
|
176
|
+
# kind='linear',fill_value='extrapolate')
|
|
177
|
+
_inte_fc = interp1d(_merge_bin, _merge_dt, kind='linear', fill_value='extrapolate')
|
|
178
|
+
|
|
179
|
+
__merge = np.exp(_unvpl_fc(np.log(_std_bin_merge)))
|
|
180
|
+
# __merge = _unvpl_fc(_std_bin_merge)
|
|
181
|
+
|
|
182
|
+
_merge_dt_fit = np.hstack((_inte_fc(_std_bin_inte1), __merge, _inte_fc(_std_bin_inte2)))
|
|
183
|
+
# _merge_dt_fit = __merge
|
|
184
|
+
# __test_plot(_bin_smps,_dt_smps,_bin_aps,_dt_aps,_std_bin,_merge_dt_fit,_merge_bin,_merge_dt,_sh)
|
|
185
|
+
|
|
186
|
+
_merge_lst.append(_merge_dt_fit)
|
|
187
|
+
|
|
188
|
+
_df_merge = DataFrame(_merge_lst, columns=_std_bin, index=_merge_idx)
|
|
189
|
+
_df_merge = _df_merge.mask(_df_merge < 0)
|
|
190
|
+
|
|
191
|
+
## process output df
|
|
192
|
+
## average, align with index
|
|
193
|
+
def _out_df(*_df_arg, **_df_kwarg):
|
|
194
|
+
_df = DataFrame(*_df_arg, **_df_kwarg).reindex(_ori_idx)
|
|
195
|
+
_df.index.name = 'time'
|
|
196
|
+
return _df
|
|
197
|
+
|
|
198
|
+
return _out_df(_df_merge), _out_df(_shift_ori ** 2)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
## aps_fit_highbound : the diameter I choose randomly
|
|
202
|
+
def _merge_SMPS_APS(df_smps, df_aps, aps_unit, shift_mode, smps_overlap_lowbound, aps_fit_highbound):
|
|
203
|
+
# print(f'\nMerge data :')
|
|
204
|
+
# print(f' APS fittint higher diameter : {aps_fit_highbound:4d} nm')
|
|
205
|
+
# print(f' SMPS overlap lower diameter : {smps_overlap_lowbound:4d} nm')
|
|
206
|
+
# print(f' Average time : {self.data_freq:>4s}\n')
|
|
207
|
+
|
|
208
|
+
## get data, remove 'total' and 'mode'
|
|
209
|
+
## set to the same units
|
|
210
|
+
smps, aps = df_smps, df_aps
|
|
211
|
+
smps.columns = smps.keys().to_numpy(float)
|
|
212
|
+
aps.columns = aps.keys().to_numpy(float)
|
|
213
|
+
|
|
214
|
+
if aps_unit == 'um':
|
|
215
|
+
aps.columns = aps.keys() * 1e3
|
|
216
|
+
|
|
217
|
+
## shift infomation, calculate by powerlaw fitting
|
|
218
|
+
shift, coe = _overlap_fitting(smps, aps, smps_overlap_lowbound, aps_fit_highbound)
|
|
219
|
+
|
|
220
|
+
## process data by shift infomation, and average data
|
|
221
|
+
qc_cond, shift = _shift_data_process(shift)
|
|
222
|
+
|
|
223
|
+
## merge aps and smps..
|
|
224
|
+
merge_data, density = _merge_data(smps, aps, shift, shift_mode, smps_overlap_lowbound, aps_fit_highbound, coe)
|
|
225
|
+
density.columns = ['density']
|
|
226
|
+
|
|
227
|
+
## add total and mode
|
|
228
|
+
# merge_total = merge_data.sum(axis=1,min_count=1).copy()
|
|
229
|
+
# merge_mode = merge_data.idxmax(axis=1).astype(float).copy()
|
|
230
|
+
|
|
231
|
+
# merge_data['total'] = merge_total
|
|
232
|
+
# merge_data['mode'] = merge_mode
|
|
233
|
+
|
|
234
|
+
## out
|
|
235
|
+
out_dic = {
|
|
236
|
+
'data_all': merge_data,
|
|
237
|
+
'data_qc': merge_data.loc[qc_cond],
|
|
238
|
+
'density_all': density,
|
|
239
|
+
'density_qc': density.loc[qc_cond],
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
## process data
|
|
243
|
+
|
|
244
|
+
for _nam, _df in out_dic.items():
|
|
245
|
+
out_dic[_nam] = _df.reindex(df_aps.index).copy()
|
|
246
|
+
|
|
247
|
+
# merge_data = merge_data.reindex(df_aps.index)
|
|
248
|
+
# density = density.reindex(df_aps.index)
|
|
249
|
+
|
|
250
|
+
return out_dic
|