AeroViz 0.1.21__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.
Files changed (180) hide show
  1. AeroViz/__init__.py +13 -0
  2. AeroViz/__pycache__/__init__.cpython-312.pyc +0 -0
  3. AeroViz/data/DEFAULT_DATA.csv +1417 -0
  4. AeroViz/data/DEFAULT_PNSD_DATA.csv +1417 -0
  5. AeroViz/data/hysplit_example_data.txt +101 -0
  6. AeroViz/dataProcess/Chemistry/__init__.py +149 -0
  7. AeroViz/dataProcess/Chemistry/__pycache__/__init__.cpython-312.pyc +0 -0
  8. AeroViz/dataProcess/Chemistry/_calculate.py +557 -0
  9. AeroViz/dataProcess/Chemistry/_isoropia.py +150 -0
  10. AeroViz/dataProcess/Chemistry/_mass_volume.py +487 -0
  11. AeroViz/dataProcess/Chemistry/_ocec.py +172 -0
  12. AeroViz/dataProcess/Chemistry/isrpia.cnf +21 -0
  13. AeroViz/dataProcess/Chemistry/isrpia2.exe +0 -0
  14. AeroViz/dataProcess/Optical/PyMieScatt_update.py +577 -0
  15. AeroViz/dataProcess/Optical/_IMPROVE.py +452 -0
  16. AeroViz/dataProcess/Optical/__init__.py +281 -0
  17. AeroViz/dataProcess/Optical/__pycache__/PyMieScatt_update.cpython-312.pyc +0 -0
  18. AeroViz/dataProcess/Optical/__pycache__/__init__.cpython-312.pyc +0 -0
  19. AeroViz/dataProcess/Optical/__pycache__/mie_theory.cpython-312.pyc +0 -0
  20. AeroViz/dataProcess/Optical/_derived.py +518 -0
  21. AeroViz/dataProcess/Optical/_extinction.py +123 -0
  22. AeroViz/dataProcess/Optical/_mie_sd.py +912 -0
  23. AeroViz/dataProcess/Optical/_retrieve_RI.py +243 -0
  24. AeroViz/dataProcess/Optical/coefficient.py +72 -0
  25. AeroViz/dataProcess/Optical/fRH.pkl +0 -0
  26. AeroViz/dataProcess/Optical/mie_theory.py +260 -0
  27. AeroViz/dataProcess/README.md +271 -0
  28. AeroViz/dataProcess/SizeDistr/__init__.py +245 -0
  29. AeroViz/dataProcess/SizeDistr/__pycache__/__init__.cpython-312.pyc +0 -0
  30. AeroViz/dataProcess/SizeDistr/__pycache__/_size_dist.cpython-312.pyc +0 -0
  31. AeroViz/dataProcess/SizeDistr/_size_dist.py +810 -0
  32. AeroViz/dataProcess/SizeDistr/merge/README.md +93 -0
  33. AeroViz/dataProcess/SizeDistr/merge/__init__.py +20 -0
  34. AeroViz/dataProcess/SizeDistr/merge/_merge_v0.py +251 -0
  35. AeroViz/dataProcess/SizeDistr/merge/_merge_v0_1.py +246 -0
  36. AeroViz/dataProcess/SizeDistr/merge/_merge_v1.py +255 -0
  37. AeroViz/dataProcess/SizeDistr/merge/_merge_v2.py +244 -0
  38. AeroViz/dataProcess/SizeDistr/merge/_merge_v3.py +518 -0
  39. AeroViz/dataProcess/SizeDistr/merge/_merge_v4.py +422 -0
  40. AeroViz/dataProcess/SizeDistr/prop.py +62 -0
  41. AeroViz/dataProcess/VOC/__init__.py +14 -0
  42. AeroViz/dataProcess/VOC/__pycache__/__init__.cpython-312.pyc +0 -0
  43. AeroViz/dataProcess/VOC/_potential_par.py +108 -0
  44. AeroViz/dataProcess/VOC/support_voc.json +446 -0
  45. AeroViz/dataProcess/__init__.py +66 -0
  46. AeroViz/dataProcess/__pycache__/__init__.cpython-312.pyc +0 -0
  47. AeroViz/dataProcess/core/__init__.py +272 -0
  48. AeroViz/dataProcess/core/__pycache__/__init__.cpython-312.pyc +0 -0
  49. AeroViz/mcp_server.py +352 -0
  50. AeroViz/plot/__init__.py +13 -0
  51. AeroViz/plot/__pycache__/__init__.cpython-312.pyc +0 -0
  52. AeroViz/plot/__pycache__/bar.cpython-312.pyc +0 -0
  53. AeroViz/plot/__pycache__/box.cpython-312.pyc +0 -0
  54. AeroViz/plot/__pycache__/pie.cpython-312.pyc +0 -0
  55. AeroViz/plot/__pycache__/radar.cpython-312.pyc +0 -0
  56. AeroViz/plot/__pycache__/regression.cpython-312.pyc +0 -0
  57. AeroViz/plot/__pycache__/scatter.cpython-312.pyc +0 -0
  58. AeroViz/plot/__pycache__/violin.cpython-312.pyc +0 -0
  59. AeroViz/plot/bar.py +126 -0
  60. AeroViz/plot/box.py +69 -0
  61. AeroViz/plot/distribution/__init__.py +1 -0
  62. AeroViz/plot/distribution/__pycache__/__init__.cpython-312.pyc +0 -0
  63. AeroViz/plot/distribution/__pycache__/distribution.cpython-312.pyc +0 -0
  64. AeroViz/plot/distribution/distribution.py +576 -0
  65. AeroViz/plot/meteorology/CBPF.py +295 -0
  66. AeroViz/plot/meteorology/__init__.py +3 -0
  67. AeroViz/plot/meteorology/__pycache__/CBPF.cpython-312.pyc +0 -0
  68. AeroViz/plot/meteorology/__pycache__/__init__.cpython-312.pyc +0 -0
  69. AeroViz/plot/meteorology/__pycache__/hysplit.cpython-312.pyc +0 -0
  70. AeroViz/plot/meteorology/__pycache__/wind_rose.cpython-312.pyc +0 -0
  71. AeroViz/plot/meteorology/hysplit.py +93 -0
  72. AeroViz/plot/meteorology/wind_rose.py +77 -0
  73. AeroViz/plot/optical/__init__.py +1 -0
  74. AeroViz/plot/optical/__pycache__/__init__.cpython-312.pyc +0 -0
  75. AeroViz/plot/optical/__pycache__/optical.cpython-312.pyc +0 -0
  76. AeroViz/plot/optical/optical.py +388 -0
  77. AeroViz/plot/pie.py +210 -0
  78. AeroViz/plot/radar.py +184 -0
  79. AeroViz/plot/regression.py +200 -0
  80. AeroViz/plot/scatter.py +174 -0
  81. AeroViz/plot/templates/__init__.py +6 -0
  82. AeroViz/plot/templates/__pycache__/__init__.cpython-312.pyc +0 -0
  83. AeroViz/plot/templates/__pycache__/ammonium_rich.cpython-312.pyc +0 -0
  84. AeroViz/plot/templates/__pycache__/contour.cpython-312.pyc +0 -0
  85. AeroViz/plot/templates/__pycache__/corr_matrix.cpython-312.pyc +0 -0
  86. AeroViz/plot/templates/__pycache__/diurnal_pattern.cpython-312.pyc +0 -0
  87. AeroViz/plot/templates/__pycache__/koschmieder.cpython-312.pyc +0 -0
  88. AeroViz/plot/templates/__pycache__/metal_heatmap.cpython-312.pyc +0 -0
  89. AeroViz/plot/templates/ammonium_rich.py +34 -0
  90. AeroViz/plot/templates/contour.py +47 -0
  91. AeroViz/plot/templates/corr_matrix.py +267 -0
  92. AeroViz/plot/templates/diurnal_pattern.py +61 -0
  93. AeroViz/plot/templates/koschmieder.py +95 -0
  94. AeroViz/plot/templates/metal_heatmap.py +164 -0
  95. AeroViz/plot/timeseries/__init__.py +2 -0
  96. AeroViz/plot/timeseries/__pycache__/__init__.cpython-312.pyc +0 -0
  97. AeroViz/plot/timeseries/__pycache__/template.cpython-312.pyc +0 -0
  98. AeroViz/plot/timeseries/__pycache__/timeseries.cpython-312.pyc +0 -0
  99. AeroViz/plot/timeseries/template.py +47 -0
  100. AeroViz/plot/timeseries/timeseries.py +446 -0
  101. AeroViz/plot/utils/__init__.py +4 -0
  102. AeroViz/plot/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  103. AeroViz/plot/utils/__pycache__/_color.cpython-312.pyc +0 -0
  104. AeroViz/plot/utils/__pycache__/_unit.cpython-312.pyc +0 -0
  105. AeroViz/plot/utils/__pycache__/plt_utils.cpython-312.pyc +0 -0
  106. AeroViz/plot/utils/__pycache__/sklearn_utils.cpython-312.pyc +0 -0
  107. AeroViz/plot/utils/_color.py +71 -0
  108. AeroViz/plot/utils/_unit.py +55 -0
  109. AeroViz/plot/utils/fRH.json +390 -0
  110. AeroViz/plot/utils/plt_utils.py +92 -0
  111. AeroViz/plot/utils/sklearn_utils.py +49 -0
  112. AeroViz/plot/utils/units.json +89 -0
  113. AeroViz/plot/violin.py +80 -0
  114. AeroViz/rawDataReader/FLOW.md +138 -0
  115. AeroViz/rawDataReader/__init__.py +220 -0
  116. AeroViz/rawDataReader/__pycache__/__init__.cpython-312.pyc +0 -0
  117. AeroViz/rawDataReader/config/__init__.py +0 -0
  118. AeroViz/rawDataReader/config/__pycache__/__init__.cpython-312.pyc +0 -0
  119. AeroViz/rawDataReader/config/__pycache__/supported_instruments.cpython-312.pyc +0 -0
  120. AeroViz/rawDataReader/config/supported_instruments.py +135 -0
  121. AeroViz/rawDataReader/core/__init__.py +658 -0
  122. AeroViz/rawDataReader/core/__pycache__/__init__.cpython-312.pyc +0 -0
  123. AeroViz/rawDataReader/core/__pycache__/logger.cpython-312.pyc +0 -0
  124. AeroViz/rawDataReader/core/__pycache__/pre_process.cpython-312.pyc +0 -0
  125. AeroViz/rawDataReader/core/__pycache__/qc.cpython-312.pyc +0 -0
  126. AeroViz/rawDataReader/core/__pycache__/report.cpython-312.pyc +0 -0
  127. AeroViz/rawDataReader/core/logger.py +171 -0
  128. AeroViz/rawDataReader/core/pre_process.py +308 -0
  129. AeroViz/rawDataReader/core/qc.py +961 -0
  130. AeroViz/rawDataReader/core/report.py +579 -0
  131. AeroViz/rawDataReader/script/AE33.py +173 -0
  132. AeroViz/rawDataReader/script/AE43.py +151 -0
  133. AeroViz/rawDataReader/script/APS.py +339 -0
  134. AeroViz/rawDataReader/script/Aurora.py +191 -0
  135. AeroViz/rawDataReader/script/BAM1020.py +90 -0
  136. AeroViz/rawDataReader/script/BC1054.py +161 -0
  137. AeroViz/rawDataReader/script/EPA.py +79 -0
  138. AeroViz/rawDataReader/script/GRIMM.py +68 -0
  139. AeroViz/rawDataReader/script/IGAC.py +140 -0
  140. AeroViz/rawDataReader/script/MA350.py +179 -0
  141. AeroViz/rawDataReader/script/Minion.py +218 -0
  142. AeroViz/rawDataReader/script/NEPH.py +199 -0
  143. AeroViz/rawDataReader/script/OCEC.py +173 -0
  144. AeroViz/rawDataReader/script/Q-ACSM.py +12 -0
  145. AeroViz/rawDataReader/script/SMPS.py +389 -0
  146. AeroViz/rawDataReader/script/TEOM.py +181 -0
  147. AeroViz/rawDataReader/script/VOC.py +106 -0
  148. AeroViz/rawDataReader/script/Xact.py +244 -0
  149. AeroViz/rawDataReader/script/__init__.py +28 -0
  150. AeroViz/rawDataReader/script/__pycache__/AE33.cpython-312.pyc +0 -0
  151. AeroViz/rawDataReader/script/__pycache__/AE43.cpython-312.pyc +0 -0
  152. AeroViz/rawDataReader/script/__pycache__/APS.cpython-312.pyc +0 -0
  153. AeroViz/rawDataReader/script/__pycache__/Aurora.cpython-312.pyc +0 -0
  154. AeroViz/rawDataReader/script/__pycache__/BAM1020.cpython-312.pyc +0 -0
  155. AeroViz/rawDataReader/script/__pycache__/BC1054.cpython-312.pyc +0 -0
  156. AeroViz/rawDataReader/script/__pycache__/EPA.cpython-312.pyc +0 -0
  157. AeroViz/rawDataReader/script/__pycache__/GRIMM.cpython-312.pyc +0 -0
  158. AeroViz/rawDataReader/script/__pycache__/IGAC.cpython-312.pyc +0 -0
  159. AeroViz/rawDataReader/script/__pycache__/MA350.cpython-312.pyc +0 -0
  160. AeroViz/rawDataReader/script/__pycache__/Minion.cpython-312.pyc +0 -0
  161. AeroViz/rawDataReader/script/__pycache__/NEPH.cpython-312.pyc +0 -0
  162. AeroViz/rawDataReader/script/__pycache__/OCEC.cpython-312.pyc +0 -0
  163. AeroViz/rawDataReader/script/__pycache__/Q-ACSM.cpython-312.pyc +0 -0
  164. AeroViz/rawDataReader/script/__pycache__/SMPS.cpython-312.pyc +0 -0
  165. AeroViz/rawDataReader/script/__pycache__/TEOM.cpython-312.pyc +0 -0
  166. AeroViz/rawDataReader/script/__pycache__/VOC.cpython-312.pyc +0 -0
  167. AeroViz/rawDataReader/script/__pycache__/Xact.cpython-312.pyc +0 -0
  168. AeroViz/rawDataReader/script/__pycache__/__init__.cpython-312.pyc +0 -0
  169. AeroViz/tools/__init__.py +2 -0
  170. AeroViz/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  171. AeroViz/tools/__pycache__/database.cpython-312.pyc +0 -0
  172. AeroViz/tools/__pycache__/dataclassifier.cpython-312.pyc +0 -0
  173. AeroViz/tools/database.py +95 -0
  174. AeroViz/tools/dataclassifier.py +117 -0
  175. AeroViz/tools/dataprinter.py +58 -0
  176. aeroviz-0.1.21.dist-info/METADATA +294 -0
  177. aeroviz-0.1.21.dist-info/RECORD +180 -0
  178. aeroviz-0.1.21.dist-info/WHEEL +5 -0
  179. aeroviz-0.1.21.dist-info/licenses/LICENSE +21 -0
  180. aeroviz-0.1.21.dist-info/top_level.txt +1 -0
@@ -0,0 +1,518 @@
1
+ # from ContainerHandle.dataProcess.config import _union_index
2
+
3
+ from datetime import datetime as dtm
4
+
5
+ import numpy as n
6
+ from pandas import DataFrame, concat, DatetimeIndex
7
+ # from scipy.interpolate import interp1d
8
+ from scipy.interpolate import UnivariateSpline as unvpline, interp1d
9
+
10
+ np = n
11
+ from multiprocessing import Pool, cpu_count
12
+ from functools import partial
13
+
14
+ import warnings
15
+
16
+ warnings.filterwarnings("ignore")
17
+
18
+ __all__ = [
19
+ '_merge_SMPS_APS',
20
+
21
+ ]
22
+
23
+
24
+ def _test_plot(ax, smps, aps, unp, shft):
25
+ fs = 22.
26
+ font_dic = dict(fontsize=fs, math_fontfamily='custom')
27
+
28
+ ax.plot(smps, c='#2693ff', label='smps')
29
+ ax.plot(aps, c='#ff4c4d', label='aps_ori')
30
+ ax.plot(aps.index / shft, aps.values, c='#ff181b', label='aps_shft', ls='--')
31
+ ax.plot(unp, c='#333333', label='unp')
32
+
33
+ # ax.tick_params(which='major', length=7, labelsize=fs-2.5)
34
+ # ax.tick_params(which='minor', length=4.5)
35
+ # ax.spines[['right', 'top']].set_visible(False)
36
+ ax.set(xlim=(11.8, 2500), xscale='log')
37
+
38
+
39
+ # ax.set_xlabel('', **font_dic)
40
+ # ax.set_ylabel('', **font_dic)
41
+
42
+ # ax.set_xticks(,**font_dic)
43
+ # ax.set_yticks(,**font_dic)
44
+
45
+ # ax.legend(handles=[], framealpha=0, fontsize=fs-2.)
46
+
47
+ # ax.set_title(shft, **font_dic)
48
+ # fig.suptitle('', fontsize=fs+2.)
49
+ # fig.savefig(/'.png')
50
+ # show()
51
+ # close()
52
+
53
+
54
+ def test_plot(smps, aps, unp, shft):
55
+ from matplotlib.pyplot import subplots, show, rcParams
56
+
57
+ ## parameter
58
+ fs = 22.
59
+ # font_fam = 'DejaVu Sans'
60
+ font_fam = 'Times New Roman'
61
+ rcParams['font.sans-serif'] = font_fam
62
+ rcParams['mathtext.fontset'] = 'custom'
63
+ font_dic = dict(fontsize=fs, math_fontfamily='custom')
64
+
65
+ ## plot
66
+ fig, axes = subplots(3, 1)
67
+
68
+ ds_fc = lambda _dt: _dt * _dt.index ** 2 * np.pi
69
+ dv_fc = lambda _dt: _dt * _dt.index ** 3 * np.pi / 6
70
+
71
+ axes[0].set_title(shft, **font_dic)
72
+ # axes[0].legend(framealpha=0, fontsize=fs * .6)
73
+
74
+ _test_plot(axes[0], smps, aps, unp, shft)
75
+ _test_plot(axes[1], ds_fc(smps), ds_fc(aps), ds_fc(unp), shft)
76
+ _test_plot(axes[2], dv_fc(smps), dv_fc(aps), dv_fc(unp), shft)
77
+
78
+ show()
79
+
80
+
81
+ def _powerlaw_fit(_coeA, _coeB, _aps, _idx, _factor):
82
+ # breakpoint()
83
+
84
+ _smps_fit_df = _coeA * (_aps.keys().values / _factor) ** _coeB
85
+ return DataFrame(((_smps_fit_df.copy() - _aps.copy()) ** 2).sum(axis=1), columns=[_idx])
86
+
87
+
88
+ ## Calculate S2
89
+ ## 1. SMPS and APS power law fitting
90
+ ## 2. shift factor from 0.5 ~ 3
91
+ ## 3. calculate S2
92
+ ## return : S2
93
+ # def _S2_calculate_dN(_smps, _aps):
94
+ def _powerlaw_fit_dN(_smps, _aps, _alg_type):
95
+ print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range fitting : {_alg_type}\033[0m")
96
+
97
+ ## overlap fitting
98
+ ## parmeter
99
+ _dt_indx = _smps.index
100
+
101
+ ## use SMPS data apply power law fitting
102
+ ## y = Ax^B, A = e**coefa, B = coefb, x = logx, y = logy
103
+ ## ref : http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
104
+ ## power law fit to SMPS num conc at upper bins to log curve
105
+
106
+ ## coefficient A, B
107
+ _smps_qc_cond = ((_smps != 0) & np.isfinite(_smps))
108
+ _smps_qc = _smps.where(_smps_qc_cond)
109
+
110
+ _size = _smps_qc_cond.sum(axis=1)
111
+ _size = _size.where(_size != 0.).copy()
112
+
113
+ _logx, _logy = n.log(_smps_qc.keys()._data.astype(float)), n.log(_smps_qc)
114
+ _x, _y, _xy, _xx = _logx.sum(), _logy.sum(axis=1), (_logx * _logy).sum(axis=1), (_logx ** 2).sum()
115
+
116
+ _coeB = ((_size * _xy - _x * _y) / (_size * _xx - _x ** 2.))
117
+ _coeA = n.exp((_y - _coeB * _x) / _size).values.reshape(-1, 1)
118
+ _coeB = _coeB.values.reshape(-1, 1)
119
+
120
+ ## rebuild shift smps data by coe. A, B
121
+ ## x_shift = (y_ori/A)**(1/B)
122
+ _aps_shift_x = (_aps / _coeA) ** (1 / _coeB)
123
+ _aps_shift_x = _aps_shift_x.where(np.isfinite(_aps_shift_x))
124
+
125
+ ## the least squares of diameter
126
+ ## the shift factor which the closest to 1
127
+ _shift_val = np.arange(0.3, 3.05, .05) ** .5
128
+ # _shift_val = np.arange(0.9, 1.805, .005)**.5
129
+
130
+ _shift_factor = DataFrame(columns=range(_shift_val.size), index=_aps_shift_x.index)
131
+ _shift_factor.loc[:, :] = _shift_val
132
+
133
+ # _dropna_idx = _shift_factor.dropna(how='all').index.copy()
134
+ _dropna_idx = _aps_shift_x.dropna(how='all').index.copy()
135
+
136
+ ## use the target function to get the similar aps and smps bin
137
+ ## S2 = sum( (smps_fit_line(dia) - aps(dia*shift_factor) )**2 )
138
+ ## assumption : the same diameter between smps and aps should get the same conc.
139
+
140
+ ## be sure they art in log value
141
+ _S2 = DataFrame(index=_aps_shift_x.index)
142
+ _dia_table = DataFrame(n.full(_aps_shift_x.shape, _aps_shift_x.keys()),
143
+ columns=_aps_shift_x.keys(), index=_aps_shift_x.index)
144
+
145
+ pool = Pool(cpu_count())
146
+
147
+ _S2 = pool.starmap(partial(_powerlaw_fit, _coeA, _coeB, _aps), list(enumerate(_shift_val)))
148
+
149
+ pool.close()
150
+ pool.join()
151
+
152
+ S2 = concat(_S2, axis=1)[np.arange(_shift_val.size)]
153
+ # S2 /= S2.max(axis=1).to_frame().values
154
+
155
+ shift_factor_dN = DataFrame(
156
+ _shift_factor.loc[_dropna_idx].values[range(len(_dropna_idx)), S2.loc[_dropna_idx].idxmin(axis=1).values],
157
+ index=_dropna_idx).reindex(_dt_indx).astype(float)
158
+
159
+ shift_factor_dN = shift_factor_dN.mask((shift_factor_dN ** 2 < 0.6) | (shift_factor_dN ** 2 > 2.6))
160
+
161
+ return shift_factor_dN
162
+
163
+
164
+ def _corr_fc(_aps_dia, _smps_dia, _smps_dn, _aps_dn, _smooth, _idx, _sh):
165
+ ds_fc = lambda _dt: _dt * _dt.index ** 2 * np.pi
166
+ dv_fc = lambda _dt: _dt * _dt.index ** 3 * np.pi / 6
167
+
168
+ _aps_sh = _aps_dia / _sh
169
+ _aps_sh_inp = _aps_sh.where((_aps_sh >= 500) & (_aps_sh <= 1500.)).copy()
170
+ _aps_sh_corr = _aps_sh.where((_aps_sh >= _smps_dia[-1]) & (_aps_sh <= 1500.)).copy()
171
+
172
+ corr_x = np.append(_smps_dia, _aps_sh_corr.dropna())
173
+
174
+ input_x = np.append(_smps_dia, _aps_sh_inp.dropna())
175
+ input_y = concat([_smps_dn, _aps_dn.iloc[:, ~np.isnan(_aps_sh_inp)]], axis=1)
176
+ input_y.columns = input_x
177
+
178
+ input_x.sort()
179
+ input_y = input_y[input_x]
180
+ corr_y = input_y[corr_x]
181
+
182
+ S2_lst = []
183
+ for (_tm, _inp_y_dn), (_tm, _cor_y_dn) in zip(input_y.dropna(how='all').iterrows(),
184
+ corr_y.dropna(how='all').iterrows()):
185
+ ## corr(spec_data, spec_spline)
186
+ _spl_dt = [unvpline(input_x, _inp_y, s=_smooth)(corr_x) for _inp_y in
187
+ [_inp_y_dn, ds_fc(_inp_y_dn), dv_fc(_inp_y_dn)]]
188
+ _cor_dt = [_cor_y_dn, ds_fc(_cor_y_dn), dv_fc(_cor_y_dn)]
189
+
190
+ _cor_all = sum([np.corrcoef(_cor, _spl)[0, 1] for _cor, _spl in zip(_cor_dt, _spl_dt)])
191
+
192
+ S2_lst.append((3 - _cor_all) / 3)
193
+
194
+ return DataFrame(S2_lst, columns=[_idx])
195
+
196
+
197
+ # def _S2_calculate_dSdV(_smps, _aps, _shft_dn, _S2, smps_ori, aps_ori):
198
+ # def _S2_calculate_dSdV(_smps, _aps, smps_ori=None):
199
+ def _corr_with_dNdSdV(_smps, _aps, _alg_type):
200
+ print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range correlation : {_alg_type}\033[0m")
201
+
202
+ _smps_dia = _smps.keys().astype(float)
203
+ _aps_dia = _aps.keys().astype(float)
204
+
205
+ all_index = _smps.index.copy()
206
+ qc_index = DatetimeIndex(set(_smps.dropna(how='all').index) & set(_aps.dropna(how='all').index)).sort_values()
207
+
208
+ _smps_dn = _smps.loc[qc_index].copy()
209
+ _aps_dn = _aps.loc[qc_index].copy()
210
+
211
+ ds_fc = lambda _dt: _dt * _dt.index ** 2 * np.pi
212
+ dv_fc = lambda _dt: _dt * _dt.index ** 3 * np.pi / 6
213
+
214
+ _std_bin = np.geomspace(11.8, 19810, 230)
215
+ _merge_bin = _std_bin[(_std_bin >= _smps_dia[-1]) & (_std_bin < 1500)].copy()
216
+
217
+ _smooth = 50
218
+
219
+ _shift_val = np.arange(0.5, 2.605, .005) ** .5
220
+ _shift_val = np.arange(0.9, 2.01, .01) ** .5
221
+ _shift_val = np.arange(0.9, 2.65, .05) ** .5
222
+
223
+ ## spline fitting with shift aps and smps
224
+ pool = Pool(cpu_count())
225
+
226
+ S2_lst = pool.starmap(partial(_corr_fc, _aps_dia, _smps_dia, _smps_dn, _aps_dn, _smooth),
227
+ list(enumerate(_shift_val)))
228
+
229
+ pool.close()
230
+ pool.join()
231
+
232
+ S2_table = concat(S2_lst, axis=1).set_index(qc_index)[np.arange(_shift_val.size)].astype(float).dropna()
233
+ min_shft = S2_table.idxmin(axis=1).values
234
+
235
+ return DataFrame(_shift_val[min_shft.astype(int)], index=S2_table.index).astype(float).reindex(_smps.index)
236
+
237
+
238
+ ## Create merge data
239
+ ## shift all smps bin and remove the aps bin which smaller than the latest old smps bin
240
+ ## Return : merge bins, merge data, density
241
+ def _merge_data(_smps_ori, _aps_ori, _shift_ori, _smps_lb, _aps_hb, _shift_mode, _alg_type):
242
+ print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mcreate merge data : {_shift_mode} and {_alg_type}\033[0m")
243
+
244
+ _ori_idx = _smps_ori.index
245
+ _merge_idx = _smps_ori.loc[_aps_ori.dropna(how='all').index].dropna(how='all').index
246
+
247
+ _corr_aps_cond = _aps_ori.keys() < 700
248
+ _corr_aps_ky = _aps_ori.keys()[_corr_aps_cond]
249
+
250
+ _uni_idx = DatetimeIndex(set(_smps_ori.dropna(how='all').index) & set(_aps_ori.dropna(how='all').index) &
251
+ set(_shift_ori.dropna(how='all').index)).sort_values()
252
+
253
+ _smps, _aps, _shift = _smps_ori.loc[_merge_idx], _aps_ori.loc[_merge_idx], _shift_ori.loc[_merge_idx].values
254
+
255
+ ## parameter
256
+ _smps_key, _aps_key = _smps.keys()._data.astype(float), _aps.keys()._data.astype(float)
257
+
258
+ _cntr = 1000
259
+ _bin_lb = _smps_key[-1]
260
+
261
+ ## make shift bins
262
+ _smps_bin = n.full(_smps.shape, _smps_key)
263
+ _aps_bin = n.full(_aps.shape, _aps_key)
264
+
265
+ _std_bin = n.geomspace(_smps_key[0], _aps_key[-1], 230)
266
+ _std_bin_merge = _std_bin[(_std_bin < _cntr) & (_std_bin > _bin_lb)]
267
+ _std_bin_inte1 = _std_bin[_std_bin <= _bin_lb]
268
+ _std_bin_inte2 = _std_bin[_std_bin >= _cntr]
269
+ # breakpoint()
270
+ if _shift_mode == 'mobility':
271
+ _aps_bin /= _shift
272
+
273
+ elif _shift_mode == 'aerodynamic':
274
+ _smps_bin *= _shift
275
+
276
+ ## merge
277
+ _merge_lst, _corr_lst = [], []
278
+ for _bin_smps, _bin_aps, _dt_smps, _dt_aps, _sh in zip(_smps_bin, _aps_bin, _smps.values, _aps.values, _shift):
279
+ ## keep complete smps bins and data
280
+ ## remove the aps bin data lower than smps bin
281
+ _condi = _bin_aps >= _bin_smps[-1]
282
+
283
+ _merge_bin = n.hstack((_bin_smps, _bin_aps[_condi]))
284
+ _merge_dt = n.hstack((_dt_smps, _dt_aps[_condi]))
285
+
286
+ _merge_fit_loc = (_merge_bin < 1500) & (_merge_bin > _smps_lb)
287
+
288
+ ## coeA and coeB
289
+ _unvpl_fc = unvpline(n.log(_merge_bin[_merge_fit_loc]), n.log(_merge_dt[_merge_fit_loc]), s=50)
290
+ _inte_fc = interp1d(_merge_bin, _merge_dt, kind='linear', fill_value='extrapolate')
291
+
292
+ _merge_dt_fit = n.hstack((_inte_fc(_std_bin_inte1), n.exp(_unvpl_fc(n.log(_std_bin_merge))),
293
+ _inte_fc(_std_bin_inte2)))
294
+
295
+ _merge_lst.append(_merge_dt_fit)
296
+ _corr_lst.append(interp1d(_std_bin, _merge_dt_fit)(_bin_aps[_corr_aps_cond]))
297
+
298
+ _df_merge = DataFrame(_merge_lst, columns=_std_bin, index=_merge_idx)
299
+ _df_merge = _df_merge.mask(_df_merge < 0)
300
+
301
+ _df_corr = DataFrame(_corr_lst, columns=_corr_aps_ky, index=_merge_idx) / _aps_ori.loc[_merge_idx, _corr_aps_ky]
302
+
303
+ ## process output df
304
+ ## average, align with index
305
+ def _out_df(*_df_arg, **_df_kwarg):
306
+ _df = DataFrame(*_df_arg, **_df_kwarg).reindex(_ori_idx)
307
+ _df.index.name = 'time'
308
+ return _df
309
+
310
+ return _out_df(_df_merge), _out_df(_shift_ori ** 2), _out_df(_df_corr)
311
+
312
+
313
+ def merge_SMPS_APS(df_smps, df_aps, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True):
314
+ merge_data, merge_data_dn, merge_data_dsdv, merge_data_cor_dn, density, density_dn, density_dsdv, density_cor_dn = [
315
+ DataFrame(
316
+ [
317
+ np.nan])] * 8
318
+
319
+ ## set to the same units
320
+ smps, aps_ori = df_smps.copy(), df_aps.copy()
321
+ smps.columns = smps.keys().to_numpy(float)
322
+ aps_ori.columns = aps_ori.keys().to_numpy(float)
323
+
324
+ if aps_unit == 'um':
325
+ aps_ori.columns = aps_ori.keys() * 1e3
326
+
327
+ den_lst, mer_lst = [], []
328
+ aps_input = aps_ori.loc[:, (aps_ori.keys() > 700) & (aps_ori.keys() < 1000)].copy()
329
+
330
+ # aps_over = aps[ aps.keys()[aps.keys() < 1000] ].copy()
331
+ smps_over = smps[smps.keys()[smps.keys() > 500]].copy()
332
+
333
+ for _count in range(2):
334
+
335
+ ## shift data calculate
336
+ if _count == 0:
337
+ alg_type = 'dn'
338
+ shift = _powerlaw_fit_dN(smps_over, aps_input)
339
+
340
+ if dndsdv_alg:
341
+ shift_dsdv = _corr_with_dNdSdV(smps_over, aps_input).mask(shift.isna())
342
+
343
+ else:
344
+ alg_type = 'cor_dndsdv'
345
+ shift_cor = _powerlaw_fit_dN(smps_over, aps_input)
346
+
347
+ if dndsdv_alg:
348
+ shift = _corr_with_dNdSdV(smps_over, aps_input).mask(shift_cor.isna())
349
+
350
+ ## merge aps and smps
351
+ ## 1. power law fit (dn) -> return dn data and aps correct factor
352
+ ## 2. correaltion with dn, ds, dv -> return corrected dn_ds_dv data
353
+ if (alg_type == 'dn') | dndsdv_alg:
354
+ merge_arg = (smps, aps_ori, shift, smps_overlap_lowbound, aps_fit_highbound)
355
+
356
+ merge_data, density, _corr = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
357
+ density.columns = ['density']
358
+
359
+ ## without aps correct
360
+ if _count == 0:
361
+ ## merge aps and smps
362
+ ## dn_ds_dv data
363
+ if dndsdv_alg:
364
+ merge_arg = (smps, aps_ori, shift_dsdv, smps_overlap_lowbound, aps_fit_highbound)
365
+
366
+ merge_data_dsdv, density_dsdv, _ = _merge_data(*merge_arg, 'mobility', _alg_type='dndsdv')
367
+ density_dsdv.columns = ['density']
368
+
369
+ ## dn data
370
+ merge_data_dn, density_dn = merge_data.copy(), density.copy()
371
+
372
+ ## correct aps data
373
+ corr = _corr.resample('1d').mean().reindex(smps.index).ffill()
374
+ corr = corr.mask(corr < 1, 1)
375
+ aps_ori.loc[:, corr.keys()] *= corr
376
+
377
+ aps_input = aps_ori.copy()
378
+
379
+ ## with aps correct
380
+ else:
381
+ ## merge aps and smps
382
+ ## dn data
383
+ merge_arg = (smps, aps_ori, shift_cor, smps_overlap_lowbound, aps_fit_highbound)
384
+
385
+ merge_data_cor_dn, density_cor_dn, _ = _merge_data(*merge_arg, 'mobility', _alg_type='cor_dn')
386
+ density_cor_dn.columns = ['density']
387
+
388
+ out_rho = concat([density_dn, density_cor_dn, density_dsdv, density], axis=1)
389
+ out_rho.columns = ['dn', 'cor_dn', 'dndsdv', 'cor_dndsdv']
390
+
391
+ ## out
392
+ out_dic = {
393
+ 'data_cor_dndsdv': merge_data,
394
+ 'data_dn': merge_data_dn,
395
+ 'data_dndsdv': merge_data_dsdv,
396
+ 'data_cor_dn': merge_data_cor_dn,
397
+
398
+ 'density': out_rho,
399
+
400
+ # 'data_all_aer' : merge_data_aer,
401
+
402
+ # 'density_cor_dndsdv' : density,
403
+ # 'density_dn' : density_dn,
404
+ # 'density_dndsdv' : density_dsdv,
405
+ # 'density_cor_dn' : density_cor_dn,
406
+ }
407
+
408
+ ## process data
409
+ for _nam, _df in out_dic.items():
410
+ out_dic[_nam] = _df.reindex(smps.index).copy()
411
+
412
+ return out_dic
413
+
414
+
415
+ def merge_SMPS_APS(df_smps, df_aps, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True):
416
+ # merge_data, merge_data_dn, merge_data_dsdv, merge_data_cor_dn, density, density_dn, density_dsdv, density_cor_dn = [DataFrame([np.nan])] * 8
417
+
418
+ ## set to the same units
419
+ smps, aps = df_smps.copy(), df_aps.copy()
420
+ smps.columns = smps.keys().to_numpy(float)
421
+ aps.columns = aps.keys().to_numpy(float)
422
+
423
+ if aps_unit == 'um':
424
+ aps.columns = aps.keys() * 1e3
425
+
426
+ oth_typ = dict()
427
+
428
+ aps_input = aps.copy()
429
+ aps_over = aps_input.loc[:, (aps.keys() > 700) & (aps.keys() < 1000)].copy()
430
+
431
+ smps_input = smps.copy()
432
+ smps_over = smps_input[smps.keys()[smps.keys() > 500]].copy()
433
+
434
+ for _count in range(2):
435
+
436
+ ## shift data calculate
437
+ ## original
438
+ if _count == 0:
439
+ alg_type = 'dn'
440
+ shift = _powerlaw_fit_dN(smps_over, aps_over, alg_type)
441
+
442
+ if dndsdv_alg:
443
+ shift_dsdv = _corr_with_dNdSdV(smps_over, aps_over, 'dndsdv').mask(shift.isna())
444
+
445
+ ## aps correct
446
+ else:
447
+ alg_type = 'cor_dndsdv'
448
+ shift_cor = _powerlaw_fit_dN(smps_over, aps_over, 'cor_dn')
449
+
450
+ if dndsdv_alg:
451
+ shift = _corr_with_dNdSdV(smps_over, aps_over, alg_type).mask(shift_cor.isna())
452
+
453
+ ## merge aps and smps
454
+ ## 1. power law fit (dn) -> return dn data and aps correct factor
455
+ ## 2. correaltion with dn, ds, dv -> return corrected dn_ds_dv data
456
+ if (alg_type == 'dn') | dndsdv_alg:
457
+ merge_arg = (smps_input, aps_input, shift, smps_overlap_lowbound, aps_fit_highbound)
458
+
459
+ merge_data, density, _corr = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
460
+ density.columns = ['density']
461
+
462
+ ## without aps correct
463
+ if _count == 0:
464
+ ## merge aps and smps
465
+ ## dn_ds_dv data
466
+ if dndsdv_alg:
467
+ alg_type = 'dndsdv'
468
+ merge_arg = (smps_input, aps_input, shift_dsdv, smps_overlap_lowbound, aps_fit_highbound)
469
+
470
+ merge_data_dsdv, density_dsdv, _ = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
471
+ density_dsdv.columns = ['density']
472
+
473
+ ## dn data
474
+ merge_data_dn, density_dn = merge_data.copy(), density.copy()
475
+
476
+ ## correct aps data
477
+ corr = _corr.resample('1d').mean().reindex(smps.index).ffill()
478
+ corr = corr.mask(corr < 1, 1)
479
+
480
+ aps_input.loc[:, corr.keys()] *= corr
481
+ aps_over = aps_input.copy()
482
+
483
+
484
+ ## with aps correct
485
+ else:
486
+ ## merge aps and smps
487
+ ## dn data
488
+ alg_type = 'cor_dn'
489
+ merge_arg = (smps_input, aps_input, shift_cor, smps_overlap_lowbound, aps_fit_highbound)
490
+
491
+ merge_data_cor_dn, density_cor_dn, _ = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
492
+ density_cor_dn.columns = ['density']
493
+
494
+ ## out
495
+ out_rho = concat([density_dn, density_cor_dn, density_dsdv, density], axis=1)
496
+ out_rho.columns = ['dn', 'cor_dn', 'dndsdv', 'cor_dndsdv']
497
+
498
+ out_dic = {
499
+ 'data_cor_dndsdv': merge_data,
500
+ 'data_dn': merge_data_dn,
501
+ 'data_dndsdv': merge_data_dsdv,
502
+ 'data_cor_dn': merge_data_cor_dn,
503
+
504
+ 'density': out_rho,
505
+
506
+ # 'data_all_aer' : merge_data_aer,
507
+
508
+ # 'density_cor_dndsdv' : density,
509
+ # 'density_dn' : density_dn,
510
+ # 'density_dndsdv' : density_dsdv,
511
+ # 'density_cor_dn' : density_cor_dn,
512
+ }
513
+
514
+ ## process data
515
+ for _nam, _df in out_dic.items():
516
+ out_dic[_nam] = _df.reindex(smps.index).copy()
517
+
518
+ return out_dic