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.

Files changed (102) hide show
  1. AeroViz/__init__.py +15 -0
  2. AeroViz/dataProcess/Chemistry/__init__.py +63 -0
  3. AeroViz/dataProcess/Chemistry/_calculate.py +27 -0
  4. AeroViz/dataProcess/Chemistry/_isoropia.py +99 -0
  5. AeroViz/dataProcess/Chemistry/_mass_volume.py +175 -0
  6. AeroViz/dataProcess/Chemistry/_ocec.py +184 -0
  7. AeroViz/dataProcess/Chemistry/_partition.py +29 -0
  8. AeroViz/dataProcess/Chemistry/_teom.py +16 -0
  9. AeroViz/dataProcess/Optical/_IMPROVE.py +61 -0
  10. AeroViz/dataProcess/Optical/__init__.py +62 -0
  11. AeroViz/dataProcess/Optical/_absorption.py +54 -0
  12. AeroViz/dataProcess/Optical/_extinction.py +36 -0
  13. AeroViz/dataProcess/Optical/_mie.py +16 -0
  14. AeroViz/dataProcess/Optical/_mie_sd.py +143 -0
  15. AeroViz/dataProcess/Optical/_scattering.py +30 -0
  16. AeroViz/dataProcess/SizeDistr/__init__.py +61 -0
  17. AeroViz/dataProcess/SizeDistr/__merge.py +250 -0
  18. AeroViz/dataProcess/SizeDistr/_merge.py +245 -0
  19. AeroViz/dataProcess/SizeDistr/_merge_v1.py +254 -0
  20. AeroViz/dataProcess/SizeDistr/_merge_v2.py +243 -0
  21. AeroViz/dataProcess/SizeDistr/_merge_v3.py +518 -0
  22. AeroViz/dataProcess/SizeDistr/_merge_v4.py +424 -0
  23. AeroViz/dataProcess/SizeDistr/_size_distr.py +93 -0
  24. AeroViz/dataProcess/VOC/__init__.py +19 -0
  25. AeroViz/dataProcess/VOC/_potential_par.py +76 -0
  26. AeroViz/dataProcess/__init__.py +11 -0
  27. AeroViz/dataProcess/core/__init__.py +92 -0
  28. AeroViz/plot/__init__.py +7 -0
  29. AeroViz/plot/distribution/__init__.py +1 -0
  30. AeroViz/plot/distribution/distribution.py +582 -0
  31. AeroViz/plot/improve/__init__.py +1 -0
  32. AeroViz/plot/improve/improve.py +240 -0
  33. AeroViz/plot/meteorology/__init__.py +1 -0
  34. AeroViz/plot/meteorology/meteorology.py +317 -0
  35. AeroViz/plot/optical/__init__.py +2 -0
  36. AeroViz/plot/optical/aethalometer.py +77 -0
  37. AeroViz/plot/optical/optical.py +388 -0
  38. AeroViz/plot/templates/__init__.py +8 -0
  39. AeroViz/plot/templates/contour.py +47 -0
  40. AeroViz/plot/templates/corr_matrix.py +108 -0
  41. AeroViz/plot/templates/diurnal_pattern.py +42 -0
  42. AeroViz/plot/templates/event_evolution.py +65 -0
  43. AeroViz/plot/templates/koschmieder.py +156 -0
  44. AeroViz/plot/templates/metal_heatmap.py +57 -0
  45. AeroViz/plot/templates/regression.py +256 -0
  46. AeroViz/plot/templates/scatter.py +130 -0
  47. AeroViz/plot/templates/templates.py +398 -0
  48. AeroViz/plot/timeseries/__init__.py +1 -0
  49. AeroViz/plot/timeseries/timeseries.py +317 -0
  50. AeroViz/plot/utils/__init__.py +3 -0
  51. AeroViz/plot/utils/_color.py +71 -0
  52. AeroViz/plot/utils/_decorator.py +74 -0
  53. AeroViz/plot/utils/_unit.py +55 -0
  54. AeroViz/process/__init__.py +31 -0
  55. AeroViz/process/core/DataProc.py +19 -0
  56. AeroViz/process/core/SizeDist.py +90 -0
  57. AeroViz/process/core/__init__.py +4 -0
  58. AeroViz/process/method/PyMieScatt_update.py +567 -0
  59. AeroViz/process/method/__init__.py +2 -0
  60. AeroViz/process/method/mie_theory.py +258 -0
  61. AeroViz/process/method/prop.py +62 -0
  62. AeroViz/process/script/AbstractDistCalc.py +143 -0
  63. AeroViz/process/script/Chemical.py +176 -0
  64. AeroViz/process/script/IMPACT.py +49 -0
  65. AeroViz/process/script/IMPROVE.py +161 -0
  66. AeroViz/process/script/Others.py +65 -0
  67. AeroViz/process/script/PSD.py +103 -0
  68. AeroViz/process/script/PSD_dry.py +94 -0
  69. AeroViz/process/script/__init__.py +5 -0
  70. AeroViz/process/script/retrieve_RI.py +70 -0
  71. AeroViz/rawDataReader/__init__.py +68 -0
  72. AeroViz/rawDataReader/core/__init__.py +397 -0
  73. AeroViz/rawDataReader/script/AE33.py +31 -0
  74. AeroViz/rawDataReader/script/AE43.py +34 -0
  75. AeroViz/rawDataReader/script/APS_3321.py +47 -0
  76. AeroViz/rawDataReader/script/Aurora.py +38 -0
  77. AeroViz/rawDataReader/script/BC1054.py +46 -0
  78. AeroViz/rawDataReader/script/EPA_vertical.py +18 -0
  79. AeroViz/rawDataReader/script/GRIMM.py +35 -0
  80. AeroViz/rawDataReader/script/IGAC_TH.py +104 -0
  81. AeroViz/rawDataReader/script/IGAC_ZM.py +90 -0
  82. AeroViz/rawDataReader/script/MA350.py +45 -0
  83. AeroViz/rawDataReader/script/NEPH.py +57 -0
  84. AeroViz/rawDataReader/script/OCEC_LCRES.py +34 -0
  85. AeroViz/rawDataReader/script/OCEC_RES.py +28 -0
  86. AeroViz/rawDataReader/script/SMPS_TH.py +41 -0
  87. AeroViz/rawDataReader/script/SMPS_aim11.py +51 -0
  88. AeroViz/rawDataReader/script/SMPS_genr.py +51 -0
  89. AeroViz/rawDataReader/script/TEOM.py +46 -0
  90. AeroViz/rawDataReader/script/Table.py +28 -0
  91. AeroViz/rawDataReader/script/VOC_TH.py +30 -0
  92. AeroViz/rawDataReader/script/VOC_ZM.py +37 -0
  93. AeroViz/rawDataReader/script/__init__.py +22 -0
  94. AeroViz/tools/__init__.py +3 -0
  95. AeroViz/tools/database.py +94 -0
  96. AeroViz/tools/dataclassifier.py +117 -0
  97. AeroViz/tools/datareader.py +66 -0
  98. AeroViz-0.1.0.dist-info/LICENSE +21 -0
  99. AeroViz-0.1.0.dist-info/METADATA +117 -0
  100. AeroViz-0.1.0.dist-info/RECORD +102 -0
  101. AeroViz-0.1.0.dist-info/WHEEL +5 -0
  102. 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
@@ -0,0 +1,11 @@
1
+ from .Chemistry import Chemistry
2
+ from .Optical import Optical
3
+ from .SizeDistr import SizeDistr
4
+ from .VOC import VOC
5
+
6
+ __all__ = [
7
+ 'Optical',
8
+ 'SizeDistr',
9
+ 'Chemistry',
10
+ 'VOC',
11
+ ]