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,424 @@
|
|
|
1
|
+
# from ContainerHandle.dataProcess.utils import _union_index
|
|
2
|
+
|
|
3
|
+
from datetime import datetime as dtm
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from pandas import DataFrame, concat, DatetimeIndex
|
|
7
|
+
# from scipy.interpolate import interp1d
|
|
8
|
+
from scipy.interpolate import UnivariateSpline as unvpline, interp1d
|
|
9
|
+
|
|
10
|
+
from multiprocessing import Pool, cpu_count
|
|
11
|
+
from functools import partial
|
|
12
|
+
|
|
13
|
+
import warnings
|
|
14
|
+
|
|
15
|
+
warnings.filterwarnings("ignore")
|
|
16
|
+
|
|
17
|
+
__all__ = ['_merge_SMPS_APS']
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _powerlaw_fit(_coeA, _coeB, _aps, _idx, _factor):
|
|
21
|
+
# breakpoint()
|
|
22
|
+
|
|
23
|
+
_smps_fit_df = _coeA * (_aps.keys().values / _factor) ** _coeB
|
|
24
|
+
return DataFrame(((_smps_fit_df.copy() - _aps.copy()) ** 2).sum(axis=1), columns=[_idx])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Calculate S2
|
|
28
|
+
## 1. SMPS and APS power law fitting
|
|
29
|
+
## 2. shift factor from 0.5 ~ 3
|
|
30
|
+
## 3. calculate S2
|
|
31
|
+
## return : S2
|
|
32
|
+
# def _S2_calculate_dN(_smps, _aps):
|
|
33
|
+
def _powerlaw_fit_dN(_smps, _aps, _alg_type):
|
|
34
|
+
print(f"\t\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range fitting : {_alg_type}\033[0m")
|
|
35
|
+
|
|
36
|
+
## overlap fitting
|
|
37
|
+
## parmeter
|
|
38
|
+
_dt_indx = _smps.index
|
|
39
|
+
|
|
40
|
+
## use SMPS data apply power law fitting
|
|
41
|
+
## y = Ax^B, A = e**coefa, B = coefb, x = logx, y = logy
|
|
42
|
+
## ref : http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
|
43
|
+
## power law fit to SMPS num conc at upper bins to log curve
|
|
44
|
+
|
|
45
|
+
## coefficient A, B
|
|
46
|
+
_smps_qc_cond = ((_smps != 0) & np.isfinite(_smps))
|
|
47
|
+
_smps_qc = _smps.where(_smps_qc_cond)
|
|
48
|
+
|
|
49
|
+
_size = _smps_qc_cond.sum(axis=1)
|
|
50
|
+
_size = _size.where(_size != 0.).copy()
|
|
51
|
+
|
|
52
|
+
_logx, _logy = np.log(_smps_qc.keys()._data.astype(float)), np.log(_smps_qc)
|
|
53
|
+
_x, _y, _xy, _xx = _logx.sum(), _logy.sum(axis=1), (_logx * _logy).sum(axis=1), (_logx ** 2).sum()
|
|
54
|
+
|
|
55
|
+
_coeB = ((_size * _xy - _x * _y) / (_size * _xx - _x ** 2.))
|
|
56
|
+
_coeA = np.exp((_y - _coeB * _x) / _size).values.reshape(-1, 1)
|
|
57
|
+
_coeB = _coeB.values.reshape(-1, 1)
|
|
58
|
+
|
|
59
|
+
## rebuild shift smps data by coe. A, B
|
|
60
|
+
## x_shift = (y_ori/A)**(1/B)
|
|
61
|
+
_aps_shift_x = (_aps / _coeA) ** (1 / _coeB)
|
|
62
|
+
_aps_shift_x = _aps_shift_x.where(np.isfinite(_aps_shift_x))
|
|
63
|
+
|
|
64
|
+
## the least squares of diameter
|
|
65
|
+
## the shift factor which the closest to 1
|
|
66
|
+
_shift_val = np.arange(0.3, 3.05, .05) ** .5
|
|
67
|
+
# _shift_val = np.arange(0.9, 1.805, .005)**.5
|
|
68
|
+
|
|
69
|
+
_shift_factor = DataFrame(columns=range(_shift_val.size), index=_aps_shift_x.index)
|
|
70
|
+
_shift_factor.loc[:, :] = _shift_val
|
|
71
|
+
|
|
72
|
+
# _dropna_idx = _shift_factor.dropna(how='all').index.copy()
|
|
73
|
+
_dropna_idx = _aps_shift_x.dropna(how='all').index.copy()
|
|
74
|
+
|
|
75
|
+
## use the target function to get the similar aps and smps bin
|
|
76
|
+
## S2 = sum( (smps_fit_line(dia) - aps(dia*shift_factor) )**2 )
|
|
77
|
+
## assumption : the same diameter between smps and aps should get the same conc.
|
|
78
|
+
|
|
79
|
+
## be sure they art in log value
|
|
80
|
+
_S2 = DataFrame(index=_aps_shift_x.index)
|
|
81
|
+
_dia_table = DataFrame(np.full(_aps_shift_x.shape, _aps_shift_x.keys()),
|
|
82
|
+
columns=_aps_shift_x.keys(), index=_aps_shift_x.index)
|
|
83
|
+
|
|
84
|
+
pool = Pool(cpu_count())
|
|
85
|
+
|
|
86
|
+
_S2 = pool.starmap(partial(_powerlaw_fit, _coeA, _coeB, _aps), list(enumerate(_shift_val)))
|
|
87
|
+
|
|
88
|
+
pool.close()
|
|
89
|
+
pool.join()
|
|
90
|
+
|
|
91
|
+
S2 = concat(_S2, axis=1)[np.arange(_shift_val.size)]
|
|
92
|
+
# S2 /= S2.max(axis=1).to_frame().values
|
|
93
|
+
|
|
94
|
+
shift_factor_dN = DataFrame(
|
|
95
|
+
_shift_factor.loc[_dropna_idx].values[range(len(_dropna_idx)), S2.loc[_dropna_idx].idxmin(axis=1).values],
|
|
96
|
+
index=_dropna_idx).reindex(_dt_indx).astype(float)
|
|
97
|
+
|
|
98
|
+
shift_factor_dN = shift_factor_dN.mask((shift_factor_dN ** 2 < 0.6) | (shift_factor_dN ** 2 > 2.6))
|
|
99
|
+
|
|
100
|
+
return shift_factor_dN
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _corr_fc(_aps_dia, _smps_dia, _smps_dn, _aps_dn, _smooth, _idx, _sh):
|
|
104
|
+
ds_fc = lambda _dt: _dt * _dt.index ** 2 * np.pi
|
|
105
|
+
dv_fc = lambda _dt: _dt * _dt.index ** 3 * np.pi / 6
|
|
106
|
+
|
|
107
|
+
_aps_sh = _aps_dia / _sh
|
|
108
|
+
_aps_sh_inp = _aps_sh.where((_aps_sh >= 500) & (_aps_sh <= 1500.)).copy()
|
|
109
|
+
_aps_sh_corr = _aps_sh.where((_aps_sh >= _smps_dia[-1]) & (_aps_sh <= 1500.)).copy()
|
|
110
|
+
|
|
111
|
+
corr_x = np.append(_smps_dia, _aps_sh_corr.dropna())
|
|
112
|
+
|
|
113
|
+
input_x = np.append(_smps_dia, _aps_sh_inp.dropna())
|
|
114
|
+
input_y = concat([_smps_dn, _aps_dn.iloc[:, ~np.isnan(_aps_sh_inp)]], axis=1)
|
|
115
|
+
input_y.columns = input_x
|
|
116
|
+
|
|
117
|
+
input_x.sort()
|
|
118
|
+
input_y = input_y[input_x]
|
|
119
|
+
corr_y = input_y[corr_x]
|
|
120
|
+
|
|
121
|
+
S2_lst = []
|
|
122
|
+
for (_tm, _inp_y_dn), (_tm, _cor_y_dn) in zip(input_y.dropna(how='all').iterrows(),
|
|
123
|
+
corr_y.dropna(how='all').iterrows()):
|
|
124
|
+
## corr(spec_data, spec_spline)
|
|
125
|
+
_spl_dt = [unvpline(input_x, _inp_y, s=_smooth)(corr_x) for _inp_y in
|
|
126
|
+
[_inp_y_dn, ds_fc(_inp_y_dn), dv_fc(_inp_y_dn)]]
|
|
127
|
+
_cor_dt = [_cor_y_dn, ds_fc(_cor_y_dn), dv_fc(_cor_y_dn)]
|
|
128
|
+
|
|
129
|
+
_cor_all = sum([np.corrcoef(_cor, _spl)[0, 1] for _cor, _spl in zip(_cor_dt, _spl_dt)])
|
|
130
|
+
|
|
131
|
+
S2_lst.append((3 - _cor_all) / 3)
|
|
132
|
+
|
|
133
|
+
return DataFrame(S2_lst, columns=[_idx])
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# def _S2_calculate_dSdV(_smps, _aps, _shft_dn, _S2, smps_ori, aps_ori):
|
|
137
|
+
# def _S2_calculate_dSdV(_smps, _aps, smps_ori=None):
|
|
138
|
+
def _corr_with_dNdSdV(_smps, _aps, _alg_type):
|
|
139
|
+
print(f"\t\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range correlation : {_alg_type}\033[0m")
|
|
140
|
+
|
|
141
|
+
_smps_dia = _smps.keys().astype(float)
|
|
142
|
+
_aps_dia = _aps.keys().astype(float)
|
|
143
|
+
|
|
144
|
+
all_index = _smps.index.copy()
|
|
145
|
+
qc_index = DatetimeIndex(set(_smps.dropna(how='all').index) & set(_aps.dropna(how='all').index)).sort_values()
|
|
146
|
+
|
|
147
|
+
_smps_dn = _smps.loc[qc_index].copy()
|
|
148
|
+
_aps_dn = _aps.loc[qc_index].copy()
|
|
149
|
+
|
|
150
|
+
ds_fc = lambda _dt: _dt * _dt.index ** 2 * np.pi
|
|
151
|
+
dv_fc = lambda _dt: _dt * _dt.index ** 3 * np.pi / 6
|
|
152
|
+
|
|
153
|
+
_std_bin = np.geomspace(11.8, 19810, 230)
|
|
154
|
+
_merge_bin = _std_bin[(_std_bin >= _smps_dia[-1]) & (_std_bin < 1500)].copy()
|
|
155
|
+
|
|
156
|
+
_smooth = 50
|
|
157
|
+
|
|
158
|
+
_shift_val = np.arange(0.5, 2.605, .005) ** .5
|
|
159
|
+
_shift_val = np.arange(0.9, 2.01, .01) ** .5
|
|
160
|
+
_shift_val = np.arange(0.9, 2.65, .05) ** .5
|
|
161
|
+
|
|
162
|
+
## spline fitting with shift aps and smps
|
|
163
|
+
pool = Pool(cpu_count())
|
|
164
|
+
|
|
165
|
+
S2_lst = pool.starmap(partial(_corr_fc, _aps_dia, _smps_dia, _smps_dn, _aps_dn, _smooth),
|
|
166
|
+
list(enumerate(_shift_val)))
|
|
167
|
+
|
|
168
|
+
pool.close()
|
|
169
|
+
pool.join()
|
|
170
|
+
|
|
171
|
+
S2_table = concat(S2_lst, axis=1).set_index(qc_index)[np.arange(_shift_val.size)].astype(float).dropna()
|
|
172
|
+
min_shft = S2_table.idxmin(axis=1).values
|
|
173
|
+
|
|
174
|
+
return DataFrame(_shift_val[min_shft.astype(int)], index=S2_table.index).astype(float).reindex(_smps.index)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
## Create merge data
|
|
178
|
+
## shift all smps bin and remove the aps bin which smaller than the latest old smps bin
|
|
179
|
+
## Return : merge bins, merge data, density
|
|
180
|
+
def _merge_data(_smps_ori, _aps_ori, _shift_ori, _smps_lb, _aps_hb, _shift_mode, _alg_type):
|
|
181
|
+
print(f"\t\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mcreate merge data : {_shift_mode} and {_alg_type}\033[0m")
|
|
182
|
+
|
|
183
|
+
_ori_idx = _smps_ori.index.copy()
|
|
184
|
+
# _merge_idx = _smps_ori.loc[_aps_ori.dropna(how='all').index].dropna(how='all').index
|
|
185
|
+
|
|
186
|
+
_corr_aps_cond = _aps_ori.keys() < 700
|
|
187
|
+
_corr_aps_ky = _aps_ori.keys()[_corr_aps_cond]
|
|
188
|
+
|
|
189
|
+
_merge_idx = DatetimeIndex(set(_smps_ori.dropna(how='all').index) & set(_aps_ori.dropna(how='all').index) &
|
|
190
|
+
set(_shift_ori.dropna(how='all').index)).sort_values()
|
|
191
|
+
|
|
192
|
+
_smps, _aps, _shift = _smps_ori.loc[_merge_idx], _aps_ori.loc[_merge_idx], _shift_ori.loc[_merge_idx].values
|
|
193
|
+
|
|
194
|
+
## parameter
|
|
195
|
+
_smps_key, _aps_key = _smps.keys()._data.astype(float), _aps.keys()._data.astype(float)
|
|
196
|
+
|
|
197
|
+
_cntr = 1000
|
|
198
|
+
_bin_lb = _smps_key[-1]
|
|
199
|
+
|
|
200
|
+
## make shift bins
|
|
201
|
+
_smps_bin = np.full(_smps.shape, _smps_key)
|
|
202
|
+
_aps_bin = np.full(_aps.shape, _aps_key)
|
|
203
|
+
|
|
204
|
+
_std_bin = np.geomspace(_smps_key[0], _aps_key[-1], 230)
|
|
205
|
+
_std_bin_merge = _std_bin[(_std_bin < _cntr) & (_std_bin > _bin_lb)]
|
|
206
|
+
_std_bin_inte1 = _std_bin[_std_bin <= _bin_lb]
|
|
207
|
+
_std_bin_inte2 = _std_bin[_std_bin >= _cntr]
|
|
208
|
+
|
|
209
|
+
if _shift_mode == 'mobility':
|
|
210
|
+
_aps_bin /= _shift
|
|
211
|
+
|
|
212
|
+
elif _shift_mode == 'aerodynamic':
|
|
213
|
+
_smps_bin *= _shift
|
|
214
|
+
|
|
215
|
+
## merge
|
|
216
|
+
_merge_lst, _corr_lst = [], []
|
|
217
|
+
for _bin_smps, _bin_aps, _dt_smps, _dt_aps, _sh in zip(_smps_bin, _aps_bin, _smps.values, _aps.values, _shift):
|
|
218
|
+
## keep complete smps bins and data
|
|
219
|
+
## remove the aps bin data lower than smps bin
|
|
220
|
+
_condi = _bin_aps >= _bin_smps[-1]
|
|
221
|
+
|
|
222
|
+
_merge_bin = np.hstack((_bin_smps, _bin_aps[_condi]))
|
|
223
|
+
_merge_dt = np.hstack((_dt_smps, _dt_aps[_condi]))
|
|
224
|
+
|
|
225
|
+
_merge_fit_loc = (_merge_bin < 1500) & (_merge_bin > _smps_lb)
|
|
226
|
+
|
|
227
|
+
## coeA and coeB
|
|
228
|
+
_unvpl_fc = unvpline(np.log(_merge_bin[_merge_fit_loc]), np.log(_merge_dt[_merge_fit_loc]), s=50)
|
|
229
|
+
_inte_fc = interp1d(_merge_bin, _merge_dt, kind='linear', fill_value='extrapolate')
|
|
230
|
+
|
|
231
|
+
_merge_dt_fit = np.hstack((_inte_fc(_std_bin_inte1), np.exp(_unvpl_fc(np.log(_std_bin_merge))),
|
|
232
|
+
_inte_fc(_std_bin_inte2)))
|
|
233
|
+
|
|
234
|
+
_merge_lst.append(_merge_dt_fit)
|
|
235
|
+
_corr_lst.append(interp1d(_std_bin, _merge_dt_fit)(_bin_aps[_corr_aps_cond]))
|
|
236
|
+
|
|
237
|
+
_df_merge = DataFrame(_merge_lst, columns=_std_bin, index=_merge_idx)
|
|
238
|
+
_df_merge = _df_merge.mask(_df_merge < 0)
|
|
239
|
+
|
|
240
|
+
_df_corr = DataFrame(_corr_lst, columns=_corr_aps_ky, index=_merge_idx) / _aps_ori.loc[_merge_idx, _corr_aps_ky]
|
|
241
|
+
|
|
242
|
+
## process output df
|
|
243
|
+
## average, align with index
|
|
244
|
+
def _out_df(*_df_arg, **_df_kwarg):
|
|
245
|
+
_df = DataFrame(*_df_arg, **_df_kwarg).reindex(_ori_idx)
|
|
246
|
+
_df.index.name = 'time'
|
|
247
|
+
return _df
|
|
248
|
+
|
|
249
|
+
return _out_df(_df_merge), _out_df(_shift_ori ** 2), _out_df(_df_corr)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _fitness_func(psd, rho, pm25):
|
|
253
|
+
psd_pm25 = psd[psd.keys()[psd.keys().values <= 2500]] * np.diff(np.log10(psd.keys())).mean()
|
|
254
|
+
rho_pm25 = pm25 / (psd_pm25 * np.pi * psd_pm25.keys().values ** 3 / 6 * 1e-9).sum(axis=1, min_count=1)
|
|
255
|
+
|
|
256
|
+
return (rho['density'] - rho_pm25) ** 2
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def merge_SMPS_APS(df_smps, df_aps, df_pm25, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000,
|
|
260
|
+
dndsdv_alg=True, times_range=(0.8, 1.25, .05)):
|
|
261
|
+
# merge_data, merge_data_dn, merge_data_dsdv, merge_data_cor_dn, density, density_dn, density_dsdv, density_cor_dn = [DataFrame([np.nan])] * 8
|
|
262
|
+
|
|
263
|
+
## set to the same units
|
|
264
|
+
smps, aps = df_smps.copy(), df_aps.copy()
|
|
265
|
+
smps.columns = smps.keys().to_numpy(float)
|
|
266
|
+
aps.columns = aps.keys().to_numpy(float)
|
|
267
|
+
|
|
268
|
+
if aps_unit == 'um':
|
|
269
|
+
aps.columns = aps.keys() * 1e3
|
|
270
|
+
|
|
271
|
+
fitness_typ = dict(dn=[], cor_dn=[], dndsdv=[], cor_dndsdv=[])
|
|
272
|
+
shift_typ = dict(dn=[], cor_dn=[], dndsdv=[], cor_dndsdv=[])
|
|
273
|
+
oth_typ = dict()
|
|
274
|
+
|
|
275
|
+
times_ary = np.arange(*times_range).round(4)
|
|
276
|
+
# times_ary = np.arange(*(0.8, 0.9, .05)).round(4)
|
|
277
|
+
|
|
278
|
+
for times in times_ary:
|
|
279
|
+
|
|
280
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mSMPS times value : {times}\033[0m")
|
|
281
|
+
|
|
282
|
+
aps_input = aps.copy()
|
|
283
|
+
aps_over = aps_input.loc[:, (aps.keys() > 700) & (aps.keys() < 1000)].copy()
|
|
284
|
+
|
|
285
|
+
smps_input = (smps * times).copy()
|
|
286
|
+
smps_over = smps_input[smps.keys()[smps.keys() > 500]].copy()
|
|
287
|
+
|
|
288
|
+
for _count in range(2):
|
|
289
|
+
|
|
290
|
+
## shift data calculate
|
|
291
|
+
## original
|
|
292
|
+
if _count == 0:
|
|
293
|
+
alg_type = 'dn'
|
|
294
|
+
shift = _powerlaw_fit_dN(smps_over, aps_over, alg_type)
|
|
295
|
+
|
|
296
|
+
if dndsdv_alg:
|
|
297
|
+
shift_dsdv = _corr_with_dNdSdV(smps_over, aps_over, 'dndsdv').mask(shift.isna())
|
|
298
|
+
|
|
299
|
+
## aps correct
|
|
300
|
+
else:
|
|
301
|
+
alg_type = 'cor_dndsdv'
|
|
302
|
+
shift_cor = _powerlaw_fit_dN(smps_over, aps_over, 'cor_dn')
|
|
303
|
+
|
|
304
|
+
if dndsdv_alg:
|
|
305
|
+
shift = _corr_with_dNdSdV(smps_over, aps_over, alg_type).mask(shift_cor.isna())
|
|
306
|
+
|
|
307
|
+
## merge aps and smps
|
|
308
|
+
## 1. power law fit (dn) -> return dn data and aps correct factor
|
|
309
|
+
## 2. correaltion with dn, ds, dv -> return corrected dn_ds_dv data
|
|
310
|
+
if (alg_type == 'dn') | dndsdv_alg:
|
|
311
|
+
merge_arg = (smps_input, aps_input, shift, smps_overlap_lowbound, aps_fit_highbound)
|
|
312
|
+
|
|
313
|
+
merge_data, density, _corr = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
|
|
314
|
+
density.columns = ['density']
|
|
315
|
+
|
|
316
|
+
fitness_typ[alg_type].append(_fitness_func(merge_data, density, df_pm25))
|
|
317
|
+
shift_typ[alg_type].append(shift[0])
|
|
318
|
+
|
|
319
|
+
## without aps correct
|
|
320
|
+
if _count == 0:
|
|
321
|
+
## merge aps and smps
|
|
322
|
+
## dn_ds_dv data
|
|
323
|
+
if dndsdv_alg:
|
|
324
|
+
alg_type = 'dndsdv'
|
|
325
|
+
merge_arg = (smps_input, aps_input, shift_dsdv, smps_overlap_lowbound, aps_fit_highbound)
|
|
326
|
+
|
|
327
|
+
merge_data_dsdv, density_dsdv, _ = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
|
|
328
|
+
density_dsdv.columns = ['density']
|
|
329
|
+
|
|
330
|
+
fitness_typ[alg_type].append(_fitness_func(merge_data_dsdv, density_dsdv, df_pm25))
|
|
331
|
+
shift_typ[alg_type].append(shift_dsdv[0])
|
|
332
|
+
|
|
333
|
+
## dn data
|
|
334
|
+
merge_data_dn, density_dn = merge_data.copy(), density.copy()
|
|
335
|
+
|
|
336
|
+
## correct aps data
|
|
337
|
+
corr = _corr.resample('1d').mean().reindex(smps.index).ffill()
|
|
338
|
+
corr = corr.mask(corr < 1, 1)
|
|
339
|
+
|
|
340
|
+
aps_input.loc[:, corr.keys()] *= corr
|
|
341
|
+
aps_over = aps_input.copy()
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
## with aps correct
|
|
345
|
+
else:
|
|
346
|
+
## merge aps and smps
|
|
347
|
+
## dn data
|
|
348
|
+
alg_type = 'cor_dn'
|
|
349
|
+
merge_arg = (smps_input, aps_input, shift_cor, smps_overlap_lowbound, aps_fit_highbound)
|
|
350
|
+
|
|
351
|
+
merge_data_cor_dn, density_cor_dn, _ = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
|
|
352
|
+
density_cor_dn.columns = ['density']
|
|
353
|
+
|
|
354
|
+
fitness_typ[alg_type].append(_fitness_func(merge_data_cor_dn, density_cor_dn, df_pm25))
|
|
355
|
+
shift_typ[alg_type].append(shift_cor[0])
|
|
356
|
+
|
|
357
|
+
## get times value and shift value
|
|
358
|
+
out_dic = {}
|
|
359
|
+
for (_typ, _lst), (_typ, _shft) in zip(fitness_typ.items(), shift_typ.items()):
|
|
360
|
+
oth_typ[_typ] = None
|
|
361
|
+
if len(_lst) == 0: continue
|
|
362
|
+
|
|
363
|
+
df_times_min = concat(_lst, axis=1, keys=range(len(_lst))).idxmin(axis=1).dropna().astype(int)
|
|
364
|
+
df_shift = concat(_shft, axis=1, keys=times_ary.tolist()).loc[df_times_min.index].values[
|
|
365
|
+
range(len(df_times_min.index)), df_times_min.values]
|
|
366
|
+
|
|
367
|
+
oth_typ[_typ] = DataFrame(np.array([df_shift, times_ary[df_times_min.values]]).T,
|
|
368
|
+
index=df_times_min.index, columns=['shift', 'times']).reindex(smps.index)
|
|
369
|
+
|
|
370
|
+
## re-calculate merge_data
|
|
371
|
+
alg_type = ['dn', 'cor_dn', 'dndsdv', 'cor_dndsdv'] if dndsdv_alg else ['dn', 'cor_dn']
|
|
372
|
+
|
|
373
|
+
out_dic = {}
|
|
374
|
+
den_lst, times_lst = [], []
|
|
375
|
+
for _typ in alg_type:
|
|
376
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mre-caculate merge data with times: {_typ}\033[0m")
|
|
377
|
+
typ = oth_typ[_typ]
|
|
378
|
+
smps_input = smps.copy() * typ['times'].to_frame().values
|
|
379
|
+
|
|
380
|
+
corr_typ = corr if 'cor' in _typ else 1
|
|
381
|
+
aps_input = aps.copy()
|
|
382
|
+
aps_input.loc[:, corr.keys()] *= corr_typ
|
|
383
|
+
|
|
384
|
+
merge_arg = (smps_input, aps_input, typ['shift'].to_frame(), smps_overlap_lowbound, aps_fit_highbound)
|
|
385
|
+
|
|
386
|
+
merge_data, density, _corr = _merge_data(*merge_arg, 'mobility', _alg_type=_typ)
|
|
387
|
+
density.columns = ['density']
|
|
388
|
+
|
|
389
|
+
out_dic[f'data_{_typ}'] = merge_data
|
|
390
|
+
|
|
391
|
+
den_lst.append(density)
|
|
392
|
+
times_lst.append(typ['times'])
|
|
393
|
+
|
|
394
|
+
out_rho = concat(den_lst, axis=1)
|
|
395
|
+
out_times = concat(times_lst, axis=1)
|
|
396
|
+
out_rho.columns = alg_type
|
|
397
|
+
out_times.columns = alg_type
|
|
398
|
+
|
|
399
|
+
# breakpoint()
|
|
400
|
+
|
|
401
|
+
## out
|
|
402
|
+
out_dic.update(dict(density=out_rho, times=out_times))
|
|
403
|
+
|
|
404
|
+
# out_dic = {
|
|
405
|
+
# 'data_cor_dndsdv' : merge_data,
|
|
406
|
+
# 'data_dn' : merge_data_dn,
|
|
407
|
+
# 'data_dndsdv' : merge_data_dsdv,
|
|
408
|
+
# 'data_cor_dn' : merge_data_cor_dn,
|
|
409
|
+
|
|
410
|
+
# 'density' : out_rho,
|
|
411
|
+
|
|
412
|
+
# 'data_all_aer' : merge_data_aer,
|
|
413
|
+
|
|
414
|
+
# 'density_cor_dndsdv' : density,
|
|
415
|
+
# 'density_dn' : density_dn,
|
|
416
|
+
# 'density_dndsdv' : density_dsdv,
|
|
417
|
+
# 'density_cor_dn' : density_cor_dn,
|
|
418
|
+
# }
|
|
419
|
+
|
|
420
|
+
## process data
|
|
421
|
+
for _nam, _df in out_dic.items():
|
|
422
|
+
out_dic[_nam] = _df.reindex(smps.index).copy()
|
|
423
|
+
|
|
424
|
+
return out_dic
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
__all__ = ['_basic']
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _geometric_prop(_dp, _prop):
|
|
5
|
+
import numpy as n
|
|
6
|
+
|
|
7
|
+
_prop_t = _prop.sum(axis=1)
|
|
8
|
+
_prop_t = _prop_t.where(_prop_t > 0).copy()
|
|
9
|
+
|
|
10
|
+
_dp = n.log(_dp)
|
|
11
|
+
_gmd = (((_prop * _dp).sum(axis=1)) / _prop_t.copy())
|
|
12
|
+
|
|
13
|
+
_dp_mesh, _gmd_mesh = n.meshgrid(_dp, _gmd)
|
|
14
|
+
_gsd = ((((_dp_mesh - _gmd_mesh) ** 2) * _prop).sum(axis=1) / _prop_t.copy()) ** .5
|
|
15
|
+
|
|
16
|
+
return _prop_t, _gmd.apply(n.exp), _gsd.apply(n.exp)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _basic(df, hybrid, unit, bin_rg, input_type):
|
|
20
|
+
import numpy as n
|
|
21
|
+
from pandas import DataFrame, concat
|
|
22
|
+
|
|
23
|
+
## get number conc. data and total, mode
|
|
24
|
+
dN = df
|
|
25
|
+
dN.columns = dN.keys().to_numpy(float)
|
|
26
|
+
|
|
27
|
+
dN_ky = dN.keys()[(dN.keys() >= bin_rg[0]) & (dN.keys() <= bin_rg[-1])]
|
|
28
|
+
dN = dN[dN_ky].copy()
|
|
29
|
+
|
|
30
|
+
out_dic = {}
|
|
31
|
+
## diameter
|
|
32
|
+
dp = dN.keys().to_numpy()
|
|
33
|
+
if hybrid:
|
|
34
|
+
dlog_dp = n.diff(n.log10(dp)).mean()
|
|
35
|
+
else:
|
|
36
|
+
dlog_dp = n.ones(dp.size)
|
|
37
|
+
dlog_dp[:hybrid] = n.diff(n.log10(dp[:hybrid])).mean()
|
|
38
|
+
dlog_dp[hybrid:] = n.diff(n.log10(dp[hybrid:])).mean()
|
|
39
|
+
|
|
40
|
+
## calculate normalize and non-normalize data
|
|
41
|
+
if input_type == 'norm':
|
|
42
|
+
out_dic['number'] = (dN * dlog_dp).copy()
|
|
43
|
+
out_dic['number_norm'] = dN.copy()
|
|
44
|
+
else:
|
|
45
|
+
out_dic['number'] = dN.copy()
|
|
46
|
+
out_dic['number_norm'] = (dN / dlog_dp).copy()
|
|
47
|
+
|
|
48
|
+
out_dic['surface'] = out_dic['number'] * n.pi * dp ** 2
|
|
49
|
+
out_dic['volume'] = out_dic['number'] * n.pi * (dp ** 3) / 6
|
|
50
|
+
|
|
51
|
+
out_dic['surface_norm'] = out_dic['number_norm'] * n.pi * dp ** 2
|
|
52
|
+
out_dic['volume_norm'] = out_dic['number_norm'] * n.pi * (dp ** 3) / 6
|
|
53
|
+
|
|
54
|
+
## size range mode process
|
|
55
|
+
df_oth = DataFrame(index=dN.index)
|
|
56
|
+
|
|
57
|
+
bound = n.array([(dp.min(), dp.max() + 1), (10, 25), (25, 100), (100, 1e3), (1e3, 2.5e3), ])
|
|
58
|
+
if unit == 'um':
|
|
59
|
+
bound[1:] /= 1e3
|
|
60
|
+
|
|
61
|
+
for _tp_nam, _tp_dt in zip(['num', 'surf', 'vol'], [out_dic['number'], out_dic['surface'], out_dic['volume']]):
|
|
62
|
+
|
|
63
|
+
for _md_nam, _range in zip(['all', 'Nucleation', 'Aitken', 'Accumulation', 'Coarse'], bound):
|
|
64
|
+
|
|
65
|
+
_dia = dp[(dp >= _range[0]) & (dp < _range[-1])]
|
|
66
|
+
if ~_dia.any(): continue
|
|
67
|
+
|
|
68
|
+
_dt = _tp_dt[_dia].copy()
|
|
69
|
+
|
|
70
|
+
df_oth[f'total_{_tp_nam}_{_md_nam}'], df_oth[f'GMD_{_tp_nam}_{_md_nam}'], df_oth[
|
|
71
|
+
f'GSD_{_tp_nam}_{_md_nam}'] = _geometric_prop(_dia, _dt)
|
|
72
|
+
df_oth[f'mode_{_tp_nam}_{_md_nam}'] = _dt.idxmax(axis=1)
|
|
73
|
+
|
|
74
|
+
## out
|
|
75
|
+
out_dic['other'] = df_oth
|
|
76
|
+
|
|
77
|
+
return out_dic
|
|
78
|
+
|
|
79
|
+
# old 20230113
|
|
80
|
+
|
|
81
|
+
# _dN = out_dic['number'][_dia].copy()
|
|
82
|
+
# df_oth[f'{_nam}_mode'] = _dN.idxmax(axis=1)
|
|
83
|
+
# df_oth[f'{_nam}_TNC'] = _dN.sum(axis=1,min_count=1)
|
|
84
|
+
|
|
85
|
+
## total, GMD and GSD
|
|
86
|
+
# df_oth['total'], df_oth['GMD'], df_oth['GSD'] = _geometric_prop(dp,out_dic['number'])
|
|
87
|
+
# df_oth['total_surf'], df_oth['GMD_surf'], df_oth['GSD_surf'] = _geometric_prop(dp,out_dic['surface'])
|
|
88
|
+
# df_oth['total_volume'], df_oth['GMD_volume'], df_oth['GSD_volume'] = _geometric_prop(dp,out_dic['volume'])
|
|
89
|
+
|
|
90
|
+
## mode
|
|
91
|
+
# df_oth['mode'] = out_dic['number'].idxmax(axis=1)
|
|
92
|
+
# df_oth['mode_surface'] = out_dic['surface'].idxmax(axis=1)
|
|
93
|
+
# df_oth['mode_volume'] = out_dic['volume'].idxmax(axis=1)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ..core import _writter, _run_process
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
|
|
5
|
+
'VOC',
|
|
6
|
+
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class VOC(_writter):
|
|
11
|
+
|
|
12
|
+
## Reconstruction
|
|
13
|
+
@_run_process('VOC - basic', 'voc_basic')
|
|
14
|
+
def VOC_basic(self, _df_voc):
|
|
15
|
+
from ._potential_par import _basic
|
|
16
|
+
|
|
17
|
+
out = _basic(_df_voc)
|
|
18
|
+
|
|
19
|
+
return self, out
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from datetime import datetime as dtm
|
|
2
|
+
from pandas import DataFrame, to_datetime, read_json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import pickle as pkl
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _basic(_df_voc):
|
|
10
|
+
## parameter
|
|
11
|
+
_keys = _df_voc.keys()
|
|
12
|
+
|
|
13
|
+
with (Path(__file__).parent / 'voc_par.pkl').open('rb') as f:
|
|
14
|
+
_par = pkl.load(f)
|
|
15
|
+
_MW, _MIR, _SOAP, _KOH = _par.loc['MW', _keys], _par.loc['MIR', _keys], _par.loc['SOAP', _keys], _par.loc[
|
|
16
|
+
'KOH', _keys]
|
|
17
|
+
|
|
18
|
+
with (Path(__file__).parent / 'voc_par.json').open('r', encoding='utf-8', errors='ignore') as f:
|
|
19
|
+
_parr = read_json(f)
|
|
20
|
+
_MW, _MIR, _SOAP, _KOH = _par.loc['MW', _keys], _par.loc['MIR', _keys], _par.loc['SOAP', _keys], _par.loc[
|
|
21
|
+
'KOH', _keys]
|
|
22
|
+
|
|
23
|
+
_voc_clasfy = {
|
|
24
|
+
'alkane_total': ['Isopentane', 'n-Butane', '2-Methylhexane', 'Cyclopentane', '3-Methylpentane',
|
|
25
|
+
'2,3-Dimethylbutane',
|
|
26
|
+
'2-Methylheptane', 'n-Nonane', 'Methylcyclohexane', '2,4-Dimethylpentane', '2-Methylpentane',
|
|
27
|
+
'n-Decane',
|
|
28
|
+
'n-Heptane', 'Cyclohexane', 'n-Octane', 'Isobutane', '2,2-Dimethylbutane',
|
|
29
|
+
'Methylcyclopentane', 'n-Hexane',
|
|
30
|
+
'2,3,4-Trimethylpentane', '3-Methylhexane', 'n-Undecane', '3-Methylheptane', 'Hexane',
|
|
31
|
+
'2,2,4-Trimethylpentane', 'n-Pentane', 'Ethane', 'Propane'],
|
|
32
|
+
|
|
33
|
+
'alkane_total': ['Isoprene', '1-Butene', 'cis-2-Butene', 'Propene', '1.3-Butadiene',
|
|
34
|
+
't-2-Butene', 'cis-2-Pentene', 'Propylene', 'isoprene', '1-Pentene',
|
|
35
|
+
'Ethylene', 't-2-Pentene', '1-Octene'],
|
|
36
|
+
|
|
37
|
+
'aromatic_total': ['o-Ethyltoluene', '1,3,5-Trimethylbenzene', 'Ethylbenzene', 'm,p-Xylene', 'n-Propylbenzene',
|
|
38
|
+
'Benzene', 'Toluene', '1.2.4-TMB', 'Styrene', 'p-Ethyltoluene', 'o-Xylene',
|
|
39
|
+
'm-Diethylbenzene',
|
|
40
|
+
'1.2.3-TMB', 'Isopropylbenzene', 'm-Ethyltoluene', '2-Ethyltoluene', '1.3.5-TMB',
|
|
41
|
+
'Iso-Propylbenzene',
|
|
42
|
+
'3.4-Ethyltoluene', 'p-Diethylbenzene', '1,2,4-Trimethylbenzene', 'm.p-Xylene',
|
|
43
|
+
'1,2,3-Trimethylbenzene'],
|
|
44
|
+
|
|
45
|
+
'alkyne_total': ['Acetylene'],
|
|
46
|
+
|
|
47
|
+
'OVOC': ['Acetaldehyde', 'Ethanol', 'Acetone', 'IPA', 'Ethyl Acetate', 'Butyl Acetate'],
|
|
48
|
+
|
|
49
|
+
'ClVOC': ['VCM', 'TCE', 'PCE', '1.4-DCB', '1.2-DCB'],
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_df_MW = (_df_voc * _MW).copy()
|
|
53
|
+
_df_dic = {
|
|
54
|
+
'Conc': _df_voc.copy(),
|
|
55
|
+
'OFP': _df_MW / 48 * _MIR,
|
|
56
|
+
'SOAP': _df_MW / 24.5 * _SOAP / 100 * 0.054,
|
|
57
|
+
'LOH': _df_MW / 24.5 / _MW * 0.602 * _KOH,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
## calculate
|
|
61
|
+
_out = {}
|
|
62
|
+
for _nam, _df in _df_dic.items():
|
|
63
|
+
|
|
64
|
+
_df_out = DataFrame(index=_df_voc.index)
|
|
65
|
+
|
|
66
|
+
for _voc_nam, _voc_lst in _voc_clasfy.items():
|
|
67
|
+
_lst = list(set(_keys) & set(_voc_lst))
|
|
68
|
+
if len(_lst) == 0: continue
|
|
69
|
+
|
|
70
|
+
_df_out[_voc_nam] = _df[_lst].sum(axis=1, min_count=1)
|
|
71
|
+
|
|
72
|
+
_df_out['Total'] = _df.sum(axis=1, min_count=1)
|
|
73
|
+
|
|
74
|
+
_out[_nam] = _df_out
|
|
75
|
+
|
|
76
|
+
return _out
|