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,388 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
import numpy as np
|
|
6
|
+
# from PyMieScatt import ScatteringFunction
|
|
7
|
+
from matplotlib.pyplot import Figure, Axes
|
|
8
|
+
|
|
9
|
+
from AeroViz.plot.utils import *
|
|
10
|
+
from AeroViz.process.method.mie_theory import Mie_Q, Mie_MEE, Mie_PESD
|
|
11
|
+
|
|
12
|
+
__all__ = ['Q_plot',
|
|
13
|
+
'RI_couple',
|
|
14
|
+
'RRI_2D',
|
|
15
|
+
# 'scattering_phase',
|
|
16
|
+
'response_surface',
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
mapping_dic = {'AS': {'m': 1.53 + 0j, 'density': 1.73, 'label': fr'$NH_{4}NO_{3}$', 'color': '#A65E58'},
|
|
20
|
+
'AN': {'m': 1.55 + 0j, 'density': 1.77, 'label': fr'$(NH_{4})_{2}SO_{4}$', 'color': '#A5BF6B'},
|
|
21
|
+
'OM': {'m': 1.54 + 0j, 'density': 1.40, 'label': 'OM', 'color': '#F2BF5E'},
|
|
22
|
+
'Soil': {'m': 1.56 + 0.01j, 'density': 2.60, 'label': 'Soil', 'color': '#3F83BF'},
|
|
23
|
+
'SS': {'m': 1.54 + 0j, 'density': 1.90, 'label': 'SS', 'color': '#B777C2'},
|
|
24
|
+
'BC': {'m': 1.80 + 0.54j, 'density': 1.50, 'label': 'BC', 'color': '#D1CFCB'},
|
|
25
|
+
'Water': {'m': 1.333 + 0j, 'density': 1.00, 'label': 'Water', 'color': '#96c8e6'}}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@set_figure
|
|
29
|
+
def Q_plot(species: Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"] | list[
|
|
30
|
+
Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"]],
|
|
31
|
+
x: Literal["dp", "sp"] = 'dp',
|
|
32
|
+
y: Literal["Q", "MEE"] = "Q",
|
|
33
|
+
mode: Literal["ext", "sca", "abs"] = 'ext',
|
|
34
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
35
|
+
"""
|
|
36
|
+
Generate a plot showing optical efficiency or mass optical efficiency for different particle species.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
species : Union[Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"], list[Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"]]]
|
|
41
|
+
The particle species or list of particle species to plot. Valid species include 'AS' (Ammonium Sulfate),
|
|
42
|
+
'AN' (Ammonium Nitrate), 'OM' (Organic Matter), 'Soil', 'SS' (Sea Salt), 'BC' (Black Carbon), and 'Water'.
|
|
43
|
+
|
|
44
|
+
x : Literal["dp", "sp"], optional
|
|
45
|
+
The x-axis parameter. 'dp' represents particle diameter, and 'sp' represents size parameter (alpha).
|
|
46
|
+
Default is 'dp'.
|
|
47
|
+
|
|
48
|
+
y : Literal["Q", "MEE"], optional
|
|
49
|
+
The y-axis parameter. 'Q' represents optical efficiency (Q_ext, Q_sca, Q_abs), and 'MEE' represents
|
|
50
|
+
mass optical efficiency (MEE, MSE, MAE). Default is 'Q'.
|
|
51
|
+
|
|
52
|
+
mode : Literal["ext", "sca", "abs"], optional
|
|
53
|
+
The mode of efficiency to plot. 'ext' for extinction efficiency, 'sca' for scattering efficiency,
|
|
54
|
+
and 'abs' for absorption efficiency. Default is 'ext'.
|
|
55
|
+
|
|
56
|
+
**kwargs
|
|
57
|
+
Additional keyword arguments to pass to the plot function.
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
ax : Axes
|
|
62
|
+
Matplotlib Axes object containing the generated plot.
|
|
63
|
+
|
|
64
|
+
Examples
|
|
65
|
+
--------
|
|
66
|
+
Example usage of the Q_plot function:
|
|
67
|
+
|
|
68
|
+
>>> Q_plot('AS', x='dp', y='Q', mode='ext')
|
|
69
|
+
>>> Q_plot(['AS', 'AN'], x='sp', y='MEE')
|
|
70
|
+
"""
|
|
71
|
+
dp = np.geomspace(10, 10000, 2000)
|
|
72
|
+
|
|
73
|
+
mode_mapping = {'ext': 0, 'sca': 1, 'abs': 2}
|
|
74
|
+
|
|
75
|
+
xlabel_mapping = {'dp': 'Particle Diameter (nm)',
|
|
76
|
+
'sp': 'Size parameter (\\alpha)'}
|
|
77
|
+
|
|
78
|
+
ylabel_mapping = {'Q': {'ext': r'$Extinction\ efficiency\ (Q_{ext})$',
|
|
79
|
+
'sca': r'$Scattering\ efficiency\ (Q_{sca})$',
|
|
80
|
+
'abs': r'$Absorption\ efficiency\ (Q_{abs})$'},
|
|
81
|
+
'MEE': {'ext': r'$MEE\ (m^{2}/g)$',
|
|
82
|
+
'sca': r'$MSE\ (m^{2}/g)$',
|
|
83
|
+
'abs': r'$MAE\ (m^{2}/g)$'}}
|
|
84
|
+
|
|
85
|
+
typ = mode_mapping.get(mode, None)
|
|
86
|
+
xlabel = xlabel_mapping.get(x, None)
|
|
87
|
+
ylabel = ylabel_mapping.get(y, None).get(mode, None)
|
|
88
|
+
|
|
89
|
+
fig, ax = plt.subplots()
|
|
90
|
+
|
|
91
|
+
if x == "sp":
|
|
92
|
+
size_para = math.pi * dp.copy() / 550
|
|
93
|
+
dp_ = size_para
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
plt.semilogx()
|
|
97
|
+
dp_ = dp.copy()
|
|
98
|
+
|
|
99
|
+
if isinstance(species, list):
|
|
100
|
+
for i, specie in enumerate(species):
|
|
101
|
+
label = mapping_dic[specie].get('label', None)
|
|
102
|
+
color = mapping_dic[specie].get('color', None)
|
|
103
|
+
|
|
104
|
+
mapping_dic[specie]['Q'] = Mie_Q(mapping_dic[specie]['m'], 550, dp)
|
|
105
|
+
mapping_dic[specie]['MEE'] = Mie_MEE(mapping_dic[specie]['m'], 550, dp, mapping_dic[specie]['density'])
|
|
106
|
+
|
|
107
|
+
plt.plot(dp_, mapping_dic[specie][f'{y}'][typ], color=color, label=label, alpha=1, lw=2)
|
|
108
|
+
|
|
109
|
+
else:
|
|
110
|
+
legend_label = {'Q': [r'$\bf Q_{{ext}}$', r'$\bf Q_{{scat}}$', r'$\bf Q_{{abs}}$'],
|
|
111
|
+
'MEE': [r'$\bf MEE$', r'$\bf MSE$', r'$\bf MAE$']}
|
|
112
|
+
|
|
113
|
+
ylabel_mapping = {'Q': r'$\bf Optical\ efficiency\ (Q_{{ext, sca, abs}})$',
|
|
114
|
+
'MEE': r'$\bf Mass\ Optical\ Efficiency\ (m^2/g)$'}
|
|
115
|
+
|
|
116
|
+
legend = legend_label.get(y, None)
|
|
117
|
+
ylabel = ylabel_mapping.get(y, None)
|
|
118
|
+
|
|
119
|
+
mapping_dic[species]['Q'] = Mie_Q(mapping_dic[species]['m'], 550, dp)
|
|
120
|
+
mapping_dic[species]['MEE'] = Mie_MEE(mapping_dic[species]['m'], 550, dp, mapping_dic[species]['density'])
|
|
121
|
+
|
|
122
|
+
plt.plot(dp_, mapping_dic[species][f'{y}'][0], color='b', label=legend[0])
|
|
123
|
+
plt.plot(dp_, mapping_dic[species][f'{y}'][1], color='g', label=legend[1])
|
|
124
|
+
plt.plot(dp_, mapping_dic[species][f'{y}'][2], color='r', label=legend[2])
|
|
125
|
+
plt.text(0.04, 0.92, mapping_dic[species]['label'], transform=ax.transAxes, weight='bold')
|
|
126
|
+
|
|
127
|
+
ax.set(xlim=(dp.min(), dp.max()), ylim=(0, None), xlabel=xlabel, ylabel=ylabel)
|
|
128
|
+
ax.grid(color='k', axis='x', which='major', linestyle='dashdot', linewidth=0.4, alpha=0.4)
|
|
129
|
+
ax.legend(loc='best', prop={'weight': 'bold'})
|
|
130
|
+
|
|
131
|
+
# fig.savefig(PATH_MAIN/f'Q_{species}')
|
|
132
|
+
plt.show()
|
|
133
|
+
|
|
134
|
+
return fig, ax
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@set_figure(figsize=(9, 4))
|
|
138
|
+
def RI_couple(**kwargs) -> tuple[Figure, Axes]:
|
|
139
|
+
"""
|
|
140
|
+
Generate a plot to test the influence of imaginary parts on scattering and absorption efficiencies.
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
**kwargs
|
|
145
|
+
Additional keyword arguments to pass to the plot function.
|
|
146
|
+
|
|
147
|
+
Returns
|
|
148
|
+
-------
|
|
149
|
+
ax : Axes
|
|
150
|
+
Matplotlib Axes object containing the generated plot.
|
|
151
|
+
|
|
152
|
+
Examples
|
|
153
|
+
--------
|
|
154
|
+
Example usage of the IJ_couple function:
|
|
155
|
+
|
|
156
|
+
>>> ax = RI_couple()
|
|
157
|
+
"""
|
|
158
|
+
dp = np.geomspace(10, 10000, 5000)
|
|
159
|
+
|
|
160
|
+
a = Mie_Q(1.50 + 0.01j, 550, dp)
|
|
161
|
+
b = Mie_Q(1.50 + 0.1j, 550, dp)
|
|
162
|
+
c = Mie_Q(1.50 + 0.5j, 550, dp)
|
|
163
|
+
|
|
164
|
+
fig, ax = plt.subplots(1, 2)
|
|
165
|
+
plt.subplots_adjust(right=0.9, wspace=0.4)
|
|
166
|
+
(ax1, ax2) = ax
|
|
167
|
+
size_para = math.pi * dp / 550
|
|
168
|
+
|
|
169
|
+
ax1.plot(size_para, a[1], 'k-', alpha=1, label=r'$\bf\ k\ =\ 0.01$')
|
|
170
|
+
ax1.plot(size_para, b[1], 'b-', alpha=1, label=r'$\bf\ k\ =\ 0.10$')
|
|
171
|
+
ax1.plot(size_para, c[1], 'g-', alpha=1, label=r'$\bf\ k\ =\ 0.50$')
|
|
172
|
+
ax1.legend()
|
|
173
|
+
|
|
174
|
+
ax1.set_xlim(0, size_para[-1])
|
|
175
|
+
ax1.set_ylim(0, None)
|
|
176
|
+
ax1.set_xlabel(r'$\bf Size\ parameter\ (\alpha)$')
|
|
177
|
+
ax1.set_ylabel(r'$\bf Scattering\ efficiency\ (Q_{{scat}})$')
|
|
178
|
+
|
|
179
|
+
ax2.plot(size_para, a[2], 'k-', alpha=1, label=r'$\bf\ k\ =\ 0.01$')
|
|
180
|
+
ax2.plot(size_para, b[2], 'b-', alpha=1, label=r'$\bf\ k\ =\ 0.10$')
|
|
181
|
+
ax2.plot(size_para, c[2], 'g-', alpha=1, label=r'$\bf\ k\ =\ 0.50$')
|
|
182
|
+
ax2.legend()
|
|
183
|
+
|
|
184
|
+
ax2.set_xlim(0, size_para[-1])
|
|
185
|
+
ax2.set_ylim(0, None)
|
|
186
|
+
ax2.set_xlabel(r'$\bf Size\ parameter\ (\alpha)$')
|
|
187
|
+
ax2.set_ylabel(r'$\bf Absorption\ efficiency\ (Q_{{abs}})$')
|
|
188
|
+
|
|
189
|
+
fig.suptitle(r'$\bf n\ =\ 1.50 $')
|
|
190
|
+
# fig.savefig(PATH_MAIN/f'IJ_couple')
|
|
191
|
+
|
|
192
|
+
plt.show()
|
|
193
|
+
|
|
194
|
+
return fig, ax
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@set_figure
|
|
198
|
+
def RRI_2D(mode: Literal["ext", "sca", "abs"] = 'ext',
|
|
199
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
200
|
+
"""
|
|
201
|
+
Generate a 2D plot of scattering efficiency (Q) against real and imaginary parts of the refractive index.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
mode : {'ext', 'sca', 'abs'}, optional
|
|
206
|
+
The mode of scattering efficiency to plot:
|
|
207
|
+
- 'ext' for extinction efficiency (Q_ext)
|
|
208
|
+
- 'sca' for scattering efficiency (Q_sca)
|
|
209
|
+
- 'abs' for absorption efficiency (Q_abs)
|
|
210
|
+
Default is 'ext'.
|
|
211
|
+
|
|
212
|
+
**kwargs
|
|
213
|
+
Additional keyword arguments to pass to the plot function.
|
|
214
|
+
|
|
215
|
+
Returns
|
|
216
|
+
-------
|
|
217
|
+
ax : Axes
|
|
218
|
+
Matplotlib Axes object containing the generated 2D plot.
|
|
219
|
+
|
|
220
|
+
Examples
|
|
221
|
+
--------
|
|
222
|
+
Example usage of the RRI_2D function:
|
|
223
|
+
|
|
224
|
+
>>> RRI_2D(mode='sca', xlabel='Real Part (n)', ylabel='Imaginary Part (k)', title='Scattering Efficiency 2D Plot')
|
|
225
|
+
"""
|
|
226
|
+
mode_mapping = {'ext': 0, 'sca': 1, 'abs': 2}
|
|
227
|
+
|
|
228
|
+
typ = mode_mapping.get(mode, None)
|
|
229
|
+
|
|
230
|
+
for dp in [400, 550, 700]:
|
|
231
|
+
RRI = np.linspace(1.3, 2, 100)
|
|
232
|
+
IRI = np.linspace(0, 0.7, 100)
|
|
233
|
+
arr = np.zeros((RRI.size, IRI.size))
|
|
234
|
+
|
|
235
|
+
for i, I_RI in enumerate(IRI):
|
|
236
|
+
for j, R_RI in enumerate(RRI):
|
|
237
|
+
arr[i, j] = Mie_Q(R_RI + 1j * I_RI, 550, dp)[typ]
|
|
238
|
+
|
|
239
|
+
fig, ax = plt.subplots()
|
|
240
|
+
plt.title(fr'$\bf dp\ = {dp}\ nm$', )
|
|
241
|
+
plt.xlabel(r'$\bf Real\ part\ (n)$', )
|
|
242
|
+
plt.ylabel(r'$\bf Imaginary\ part\ (k)$', )
|
|
243
|
+
|
|
244
|
+
im = plt.imshow(arr, extent=(1.3, 2, 0, 0.7), cmap='jet', origin='lower')
|
|
245
|
+
color_bar = plt.colorbar(im, extend='both')
|
|
246
|
+
color_bar.set_label(label=fr'$\bf Q_{{{mode}}}$')
|
|
247
|
+
|
|
248
|
+
# fig.savefig(PATH_MAIN/f'RRI_{mode}_{dp}')
|
|
249
|
+
|
|
250
|
+
plt.show()
|
|
251
|
+
|
|
252
|
+
return fig, ax
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# @set_figure
|
|
256
|
+
# def scattering_phase(m: complex = 1.55 + 0.01j,
|
|
257
|
+
# wave: float = 600,
|
|
258
|
+
# dp: float = 200) -> tuple[Figure, Axes]:
|
|
259
|
+
# """
|
|
260
|
+
# Generate a polar plot to visualize the scattering phase function.
|
|
261
|
+
#
|
|
262
|
+
# Parameters
|
|
263
|
+
# ----------
|
|
264
|
+
# m : complex, optional
|
|
265
|
+
# The complex refractive index of the scattering medium. Default is 1.55 + 0.01j.
|
|
266
|
+
# wave : float, optional
|
|
267
|
+
# The wavelength of the incident light in nanometers. Default is 600 nm.
|
|
268
|
+
# dp : float, optional
|
|
269
|
+
# The particle diameter in nanometers. Default is 200 nm.
|
|
270
|
+
#
|
|
271
|
+
# Returns
|
|
272
|
+
# -------
|
|
273
|
+
# ax : Axes
|
|
274
|
+
# Matplotlib Axes object containing the generated polar plot.
|
|
275
|
+
#
|
|
276
|
+
# Examples
|
|
277
|
+
# --------
|
|
278
|
+
# Example usage of the scattering_phase function:
|
|
279
|
+
#
|
|
280
|
+
# >>> ax = scattering_phase(m=1.55 + 0.01j, wave=600, dp=200)
|
|
281
|
+
# """
|
|
282
|
+
# theta, _SL, _SR, _SU = ScatteringFunction(m, wave, dp)
|
|
283
|
+
#
|
|
284
|
+
# SL = np.append(_SL, _SL[::-1])
|
|
285
|
+
# SR = np.append(_SR, _SR[::-1])
|
|
286
|
+
# SU = np.append(_SU, _SU[::-1])
|
|
287
|
+
#
|
|
288
|
+
# angles = ['0', '60', '120', '180', '240', '300']
|
|
289
|
+
#
|
|
290
|
+
# fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
|
|
291
|
+
#
|
|
292
|
+
# theta = np.linspace(0, 2 * np.pi, len(SL))
|
|
293
|
+
#
|
|
294
|
+
# plt.thetagrids(range(0, 360, int(360 / len(angles))), angles)
|
|
295
|
+
#
|
|
296
|
+
# plt.plot(theta, SL, '-', linewidth=2, color='#115162', label='SL')
|
|
297
|
+
# plt.fill(theta, SL, '#afe0f5', alpha=0.5)
|
|
298
|
+
# plt.plot(theta, SR, '-', linewidth=2, color='#7FAE80', label='SR')
|
|
299
|
+
# plt.fill(theta, SR, '#b5e6c5', alpha=0.5)
|
|
300
|
+
# plt.plot(theta, SU, '-', linewidth=2, color='#621129', label='SU')
|
|
301
|
+
# plt.fill(theta, SU, '#f5afbd', alpha=0.5)
|
|
302
|
+
#
|
|
303
|
+
# plt.legend(loc='best', bbox_to_anchor=(1, 0, 0.2, 1), prop={'weight': 'bold'})
|
|
304
|
+
# plt.title(r'$\bf Scattering\ phase\ function$')
|
|
305
|
+
#
|
|
306
|
+
# plt.show()
|
|
307
|
+
# return fig, ax
|
|
308
|
+
#
|
|
309
|
+
|
|
310
|
+
@set_figure
|
|
311
|
+
def response_surface(real_range=(1.33, 1.7),
|
|
312
|
+
gmd_range=(10, 500),
|
|
313
|
+
num=50,
|
|
314
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
315
|
+
"""
|
|
316
|
+
Generate a response surface plot for sensitivity tests of extinction based on Mie scattering.
|
|
317
|
+
|
|
318
|
+
Parameters
|
|
319
|
+
----------
|
|
320
|
+
real_range : tuple, optional
|
|
321
|
+
The range of real part (refractive index) values for sensitivity testing. Default is (1.33, 1.7).
|
|
322
|
+
|
|
323
|
+
gmd_range : tuple, optional
|
|
324
|
+
The range of geometric mean diameter (GMD) values for sensitivity testing. Default is (60, 400).
|
|
325
|
+
|
|
326
|
+
num : int, optional
|
|
327
|
+
The number of points to generate within the specified ranges. Default is 50.
|
|
328
|
+
|
|
329
|
+
**kwargs
|
|
330
|
+
Additional keyword arguments to pass to the plot function.
|
|
331
|
+
|
|
332
|
+
Returns
|
|
333
|
+
-------
|
|
334
|
+
ax : Axes
|
|
335
|
+
Matplotlib Axes object containing the generated response surface plot.
|
|
336
|
+
|
|
337
|
+
Examples
|
|
338
|
+
--------
|
|
339
|
+
Example usage of the response_surface function:
|
|
340
|
+
|
|
341
|
+
>>> response_surface(real_range=(1.4, 1.6), gmd_range=(100, 300), num=30, xlabel='Real Part (n)',
|
|
342
|
+
... ylabel='GMD (nm)', zlabel='Extinction (1/Mm)', title='Sensitivity Tests of Extinction')
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
def function(RI, GMD):
|
|
346
|
+
Z = np.zeros_like(RI) # 使用 np.zeros_like 可以確保 Z 和 RI 具有相同的形狀
|
|
347
|
+
|
|
348
|
+
for i in range(RI.shape[0]):
|
|
349
|
+
for j in range(RI.shape[1]):
|
|
350
|
+
_RI, _GMD = RI[i, j], GMD[i, j]
|
|
351
|
+
Bext, Bsca, Babs = Mie_PESD(m=_RI, lognormal=True, geoMean=_GMD, geoStdDev=2.)
|
|
352
|
+
Z[i, j] = np.sum(Bext)
|
|
353
|
+
|
|
354
|
+
return Z
|
|
355
|
+
|
|
356
|
+
# 假設 RI、GSD、GMD
|
|
357
|
+
RI = np.linspace(real_range[0], real_range[1], num)
|
|
358
|
+
GMD = np.linspace(gmd_range[0], gmd_range[1], num)
|
|
359
|
+
|
|
360
|
+
# 建立三維 meshgrid
|
|
361
|
+
real, gmd = np.meshgrid(RI, GMD, indexing='xy')
|
|
362
|
+
|
|
363
|
+
# Result
|
|
364
|
+
ext = function(real, gmd)
|
|
365
|
+
|
|
366
|
+
# plot
|
|
367
|
+
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
|
|
368
|
+
ax.plot_surface(real, gmd, ext, rstride=1, cstride=1, cmap=plt.get_cmap('jet'), edgecolor='none')
|
|
369
|
+
|
|
370
|
+
ax.set(xlabel='Real part (n)', ylabel='GMD (nm)', zlabel=Unit('Extinction'),
|
|
371
|
+
title='Sensitive tests of Extinction')
|
|
372
|
+
|
|
373
|
+
ax.zaxis.get_offset_text().set_visible(False)
|
|
374
|
+
exponent = math.floor(math.log10(np.max(ext)))
|
|
375
|
+
ax.text(ax.get_xlim()[1] * 1.01, ax.get_ylim()[1], ax.get_zlim()[1] * 1.1, s=fr'${{\times}}\ 10^{exponent}$')
|
|
376
|
+
ax.ticklabel_format(style='sci', axis='z', scilimits=(0, 0), useOffset=False)
|
|
377
|
+
|
|
378
|
+
plt.show()
|
|
379
|
+
|
|
380
|
+
return fig, ax
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
if __name__ == '__main__':
|
|
384
|
+
Q_plot(['AS', 'AN', 'OM', 'Soil', 'SS', 'BC'], x='dp', y='MEE')
|
|
385
|
+
Q_plot(['AS', 'AN', 'OM', 'Soil', 'SS', 'BC'], x='dp', y='Q')
|
|
386
|
+
|
|
387
|
+
# RI_couple()
|
|
388
|
+
# response_surface()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import numpy as np
|
|
3
|
+
from matplotlib.pyplot import Figure, Axes
|
|
4
|
+
from scipy.optimize import curve_fit
|
|
5
|
+
|
|
6
|
+
from AeroViz.plot.utils import *
|
|
7
|
+
|
|
8
|
+
__all__ = ['contour']
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@set_figure
|
|
12
|
+
def contour(df, ax: Axes | None = None, **kwargs) -> tuple[Figure, Axes]:
|
|
13
|
+
fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
|
|
14
|
+
|
|
15
|
+
npoints = 1000
|
|
16
|
+
xreg = np.linspace(df.PM25.min(), df.PM25.max(), 83)
|
|
17
|
+
yreg = np.linspace(df.gRH.min(), df.gRH.max(), 34)
|
|
18
|
+
X, Y = np.meshgrid(xreg, yreg)
|
|
19
|
+
|
|
20
|
+
d_f = df.copy()
|
|
21
|
+
df['gRH'] = d_f['gRH'].round(2)
|
|
22
|
+
df['PM25'] = d_f['PM25'].round(2)
|
|
23
|
+
|
|
24
|
+
def func(data, *params):
|
|
25
|
+
return params[0] * data ** (params[1])
|
|
26
|
+
|
|
27
|
+
initial_guess = [1.0, 1.0]
|
|
28
|
+
|
|
29
|
+
fit_df = df[['PM25', 'gRH', 'Extinction']].dropna()
|
|
30
|
+
popt, pcov = curve_fit(func, xdata=(fit_df['PM25'] * fit_df['gRH']), ydata=fit_df['Extinction'], p0=initial_guess,
|
|
31
|
+
maxfev=2000000, method='trf')
|
|
32
|
+
|
|
33
|
+
x, y = df.PM25, df.gRH
|
|
34
|
+
|
|
35
|
+
# pcolor = ax.pcolormesh(X, Y, (X * 4.5 * Y ** (1 / 3)), cmap='jet', shading='auto', vmin=0, vmax=843, alpha=0.8)
|
|
36
|
+
Z = func(X * Y, *popt)
|
|
37
|
+
cont = ax.contour(X, Y, Z, colors='black', levels=5, vmin=0, vmax=Z.max())
|
|
38
|
+
conf = ax.contourf(X, Y, Z, cmap='YlGnBu', levels=100, vmin=0, vmax=Z.max())
|
|
39
|
+
ax.clabel(cont, colors=['black'], fmt=lambda s: f"{s:.0f} 1/Mm")
|
|
40
|
+
ax.set(xlabel=Unit('PM25'), ylabel=Unit('gRH'), xlim=(x.min(), x.max()), ylim=(y.min(), y.max()))
|
|
41
|
+
|
|
42
|
+
color_bar = plt.colorbar(conf, pad=0.02, fraction=0.05, label='Extinction (1/Mm)')
|
|
43
|
+
color_bar.ax.set_xticklabels(color_bar.ax.get_xticks().astype(int))
|
|
44
|
+
|
|
45
|
+
plt.show()
|
|
46
|
+
|
|
47
|
+
return fig, ax
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import seaborn as sns
|
|
5
|
+
from matplotlib import colormaps
|
|
6
|
+
from matplotlib.pyplot import Figure, Axes
|
|
7
|
+
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
|
8
|
+
from scipy.stats import pearsonr
|
|
9
|
+
|
|
10
|
+
from AeroViz.plot.utils import *
|
|
11
|
+
|
|
12
|
+
__all__ = ['corr_matrix']
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@set_figure(fs=8)
|
|
16
|
+
def corr_matrix(data: pd.DataFrame,
|
|
17
|
+
cmap: str = "RdBu",
|
|
18
|
+
ax: Axes | None = None,
|
|
19
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
20
|
+
fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
|
|
21
|
+
|
|
22
|
+
columns = ['Extinction', 'Scattering', 'Absorption', 'PM1', 'PM25', 'PM10', 'PBLH', 'VC',
|
|
23
|
+
'AT', 'RH', 'WS', 'NO', 'NO2', 'NOx', 'O3', 'Benzene', 'Toluene',
|
|
24
|
+
'SO2', 'CO', 'THC', 'CH4', 'NMHC', 'NH3', 'HCl', 'HNO2', 'HNO3',
|
|
25
|
+
'Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+', 'Cl-', 'NO2-', 'NO3-', 'SO42-', ]
|
|
26
|
+
|
|
27
|
+
df = data[columns]
|
|
28
|
+
|
|
29
|
+
_corr = df.corr()
|
|
30
|
+
corr = pd.melt(_corr.reset_index(),
|
|
31
|
+
id_vars='index') # Unpivot the dataframe, so we can get pair of arrays for x and y
|
|
32
|
+
corr.columns = ['x', 'y', 'value']
|
|
33
|
+
|
|
34
|
+
p_values = _corr.apply(lambda col1: _corr.apply(lambda col2: pearsonr(col1, col2)[1]))
|
|
35
|
+
p_values = p_values.mask(p_values > 0.05)
|
|
36
|
+
p_values = pd.melt(p_values.reset_index(), id_vars='index').dropna()
|
|
37
|
+
p_values.columns = ['x', 'y', 'value']
|
|
38
|
+
|
|
39
|
+
# Mapping from column names to integer coordinates
|
|
40
|
+
x_labels = [v for v in sorted(corr['x'].unique())]
|
|
41
|
+
y_labels = [v for v in sorted(corr['y'].unique())]
|
|
42
|
+
x_to_num = {p[1]: p[0] for p in enumerate(x_labels)}
|
|
43
|
+
y_to_num = {p[1]: p[0] for p in enumerate(y_labels)}
|
|
44
|
+
|
|
45
|
+
# Show column labels on the axes
|
|
46
|
+
ax.set_xticks([x_to_num[v] for v in x_labels])
|
|
47
|
+
ax.set_xticklabels(x_labels, rotation=90, horizontalalignment='center')
|
|
48
|
+
ax.set_yticks([y_to_num[v] for v in y_labels])
|
|
49
|
+
ax.set_yticklabels(y_labels)
|
|
50
|
+
|
|
51
|
+
# ax.tick_params(axis='both', which='major', direction='out', top=True, left=True)
|
|
52
|
+
|
|
53
|
+
ax.grid(False, 'major')
|
|
54
|
+
ax.grid(True, 'minor')
|
|
55
|
+
ax.set_xticks([t + 0.5 for t in ax.get_xticks()], minor=True)
|
|
56
|
+
ax.set_yticks([t + 0.5 for t in ax.get_yticks()], minor=True)
|
|
57
|
+
|
|
58
|
+
ax.set_xlim([-0.5, max([v for v in x_to_num.values()]) + 0.5])
|
|
59
|
+
ax.set_ylim([-0.5, max([v for v in y_to_num.values()]) + 0.5])
|
|
60
|
+
|
|
61
|
+
n_colors = 256 # Use 256 colors for the diverging color palette
|
|
62
|
+
palette = sns.color_palette(cmap, n_colors=n_colors) # Create the palette
|
|
63
|
+
|
|
64
|
+
# Range of values that will be mapped to the palette, i.e. min and max possible correlation
|
|
65
|
+
color_min, color_max = [-1, 1]
|
|
66
|
+
|
|
67
|
+
def value_to_color(val):
|
|
68
|
+
val_position = float((val - color_min)) / (color_max - color_min)
|
|
69
|
+
ind = int(val_position * (n_colors - 1)) # target index in the color palette
|
|
70
|
+
return palette[ind]
|
|
71
|
+
|
|
72
|
+
point = ax.scatter(
|
|
73
|
+
x=corr['x'].map(x_to_num),
|
|
74
|
+
y=corr['y'].map(y_to_num),
|
|
75
|
+
s=corr['value'].abs() * 70,
|
|
76
|
+
c=corr['value'].apply(value_to_color), # Vector of square color values, mapped to color palette
|
|
77
|
+
marker='s',
|
|
78
|
+
label='$R^{2}$'
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
axes_image = plt.cm.ScalarMappable(cmap=colormaps[cmap])
|
|
82
|
+
|
|
83
|
+
cax = inset_axes(ax, width="5%",
|
|
84
|
+
height="100%",
|
|
85
|
+
loc='lower left',
|
|
86
|
+
bbox_to_anchor=(1.02, 0., 1, 1),
|
|
87
|
+
bbox_transform=ax.transAxes,
|
|
88
|
+
borderpad=0)
|
|
89
|
+
|
|
90
|
+
cbar = plt.colorbar(mappable=axes_image, cax=cax, label=r'$R^{2}$')
|
|
91
|
+
|
|
92
|
+
cbar.set_ticks([0, 0.25, 0.5, 0.75, 1])
|
|
93
|
+
cbar.set_ticklabels(np.linspace(-1, 1, 5))
|
|
94
|
+
|
|
95
|
+
point2 = ax.scatter(
|
|
96
|
+
x=p_values['x'].map(x_to_num),
|
|
97
|
+
y=p_values['y'].map(y_to_num),
|
|
98
|
+
s=10,
|
|
99
|
+
marker='*',
|
|
100
|
+
color='k',
|
|
101
|
+
label='p < 0.05'
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
ax.legend(handles=[point2], labels=['p < 0.05'], bbox_to_anchor=(0.05, 1, 0.1, 0.05))
|
|
105
|
+
|
|
106
|
+
plt.show()
|
|
107
|
+
|
|
108
|
+
return fig, ax
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from matplotlib.pyplot import Figure, Axes
|
|
4
|
+
from matplotlib.ticker import AutoMinorLocator
|
|
5
|
+
|
|
6
|
+
from AeroViz.plot.utils import *
|
|
7
|
+
|
|
8
|
+
__all__ = ['diurnal_pattern']
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@set_figure(figsize=(4, 4), fs=8)
|
|
12
|
+
def diurnal_pattern(data_set: pd.DataFrame,
|
|
13
|
+
data_std: pd.DataFrame,
|
|
14
|
+
y: str | list[str],
|
|
15
|
+
std_area=0.5,
|
|
16
|
+
ax: Axes | None = None,
|
|
17
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
18
|
+
fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
|
|
19
|
+
|
|
20
|
+
Hour = range(0, 24)
|
|
21
|
+
|
|
22
|
+
mean = data_set[y]
|
|
23
|
+
std = data_std[y] * std_area
|
|
24
|
+
|
|
25
|
+
# Plot Diurnal pattern
|
|
26
|
+
ax.plot(Hour, mean, 'blue')
|
|
27
|
+
ax.fill_between(Hour, y1=mean + std, y2=mean - std, alpha=0.5, color='blue', edgecolor=None)
|
|
28
|
+
|
|
29
|
+
ax.set(xlabel=kwargs.get('xlabel', 'Hours'),
|
|
30
|
+
ylabel=kwargs.get('ylabel', Unit(y)),
|
|
31
|
+
xlim=kwargs.get('xlim', (0, 23)),
|
|
32
|
+
ylim=kwargs.get('ylim', (None, None)),
|
|
33
|
+
xticks=kwargs.get('xticks', [0, 4, 8, 12, 16, 20]))
|
|
34
|
+
|
|
35
|
+
ax.tick_params(axis='both', which='major')
|
|
36
|
+
ax.tick_params(axis='x', which='minor')
|
|
37
|
+
ax.xaxis.set_minor_locator(AutoMinorLocator())
|
|
38
|
+
ax.ticklabel_format(axis='y', style='sci', scilimits=(-2, 3), useMathText=True)
|
|
39
|
+
|
|
40
|
+
plt.show()
|
|
41
|
+
|
|
42
|
+
return fig, ax
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from os.path import join as pth
|
|
2
|
+
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import numpy as np
|
|
5
|
+
from pandas import date_range, read_csv
|
|
6
|
+
|
|
7
|
+
from AeroViz.plot.utils import *
|
|
8
|
+
|
|
9
|
+
# TODO:
|
|
10
|
+
|
|
11
|
+
# read csv file
|
|
12
|
+
blh = read_csv(pth('事件分析.csv'), parse_dates=['Time'], index_col='Time')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@set_figure(figsize=(12, 5))
|
|
16
|
+
def event_evolution(_df, **kwargs):
|
|
17
|
+
print(f'Plot : {_df.month[0]}')
|
|
18
|
+
|
|
19
|
+
st_tm, fn_tm = _df.index[0], _df.index[-1]
|
|
20
|
+
tick_time = date_range(st_tm, fn_tm, freq='1d') # set tick
|
|
21
|
+
|
|
22
|
+
# seperate day and night
|
|
23
|
+
_df_day = _df.between_time('6:00', '17:00').reindex(date_range(st_tm, fn_tm, freq='1h'))
|
|
24
|
+
_df_night = _df.between_time('18:00', '5:00').reindex(date_range(st_tm, fn_tm, freq='1h'))
|
|
25
|
+
|
|
26
|
+
## plot
|
|
27
|
+
fig, ax = plt.subplots()
|
|
28
|
+
|
|
29
|
+
## plot background
|
|
30
|
+
shade_value, _ = np.meshgrid(_df['PM2.5'], np.arange((1., 2500), 100))
|
|
31
|
+
ax.pcolormesh(_df.index, np.arange((1., 2500), 100), shade_value, cmap='binary', vmin=0, vmax=300,
|
|
32
|
+
shading='auto')
|
|
33
|
+
|
|
34
|
+
## plot day and night
|
|
35
|
+
ld = ax.scatter(_df.index[0:], _df_day['Ext'], s=50, c='#73b9ff', label='Day Ext', marker='o', alpha=.7)
|
|
36
|
+
ln = ax.scatter(_df.index[0:], _df_night['Ext'], s=50, c='#00238c', label='Night Ext', marker='o', alpha=.7)
|
|
37
|
+
|
|
38
|
+
ax2 = ax.twinx()
|
|
39
|
+
# ld, = ax2.plot(_df_day['VC'],c='#FF9797',label='day 06:00~18:00')
|
|
40
|
+
# ln, = ax2.plot(_df_night['VC'],c='#FF0000',label='night 18:00~06:00')
|
|
41
|
+
ld2 = ax2.scatter(_df.index, _df_day['VC'], s=50, c='#FF9797', label='Day VC', marker='o', alpha=.5)
|
|
42
|
+
ln2 = ax2.scatter(_df.index, _df_night['VC'], s=50, c='#FF0000', label='Night VC', marker='o', alpha=.5)
|
|
43
|
+
|
|
44
|
+
# add legend on the first axes
|
|
45
|
+
ax.legend(handles=[ld, ln, ld2, ln2], framealpha=0, prop={'weight': 'bold'}, loc='upper left')
|
|
46
|
+
|
|
47
|
+
# add xlabel, ylabel, suptitle
|
|
48
|
+
ax.set(xlabel='Date',
|
|
49
|
+
ylabel='Ext (1/Mm)',
|
|
50
|
+
xlim=(st_tm, fn_tm),
|
|
51
|
+
ylim=(1., 600),
|
|
52
|
+
xticks=tick_time,
|
|
53
|
+
xticklabels=[_tm.strftime("%F %H:00") for _tm in tick_time])
|
|
54
|
+
|
|
55
|
+
ax2.set(ylabel=r'$VC (m^{2}/s)$',
|
|
56
|
+
ylim=(1., 2500))
|
|
57
|
+
|
|
58
|
+
fig.suptitle(f'Event evolution ({st_tm.strftime("%F")}_{fn_tm.strftime("%F")})')
|
|
59
|
+
|
|
60
|
+
# save figure
|
|
61
|
+
fig.savefig(pth(f"event_evolution_{st_tm.strftime("%F")}_{fn_tm.strftime("%F")}"))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == '__main__':
|
|
65
|
+
event_evolution(blh)
|