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,161 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from pandas import read_csv, concat, read_json
|
|
5
|
+
|
|
6
|
+
from AeroViz.process.core import DataProc
|
|
7
|
+
from AeroViz.tools.datareader import DataReader
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ImproveProc(DataProc):
|
|
11
|
+
"""
|
|
12
|
+
A class for process improved chemical data.
|
|
13
|
+
|
|
14
|
+
Parameters:
|
|
15
|
+
-----------
|
|
16
|
+
reset : bool, optional
|
|
17
|
+
If True, resets the process. Default is False.
|
|
18
|
+
filename : str, optional
|
|
19
|
+
The name of the file to process. Default is None.
|
|
20
|
+
version : str, optional
|
|
21
|
+
The version of the data process. Should be one of 'revised' or 'modified'.
|
|
22
|
+
Default is None.
|
|
23
|
+
|
|
24
|
+
Methods:
|
|
25
|
+
--------
|
|
26
|
+
revised(_df):
|
|
27
|
+
Calculate revised version of particle contribution.
|
|
28
|
+
|
|
29
|
+
modified(_df):
|
|
30
|
+
Calculate modified version of particle contribution.
|
|
31
|
+
|
|
32
|
+
gas(_df):
|
|
33
|
+
Calculate gas contribution.
|
|
34
|
+
|
|
35
|
+
frh(_RH, version=None):
|
|
36
|
+
Helper function to get frh values based on relative humidity (RH) and version.
|
|
37
|
+
|
|
38
|
+
process_data():
|
|
39
|
+
Process data and save the result.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
-----------
|
|
43
|
+
DEFAULT_PATH : Path
|
|
44
|
+
The default path for data files.
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
---------
|
|
48
|
+
>>> df = ImproveProc(reset=True, filename='revised_IMPROVE.csv', version='revised').process_data()
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, file_paths: list[Path | str] = None):
|
|
53
|
+
super().__init__()
|
|
54
|
+
self.file_paths = [Path(fp) for fp in file_paths]
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def frh(_RH):
|
|
58
|
+
_frh = read_json(Path(__file__).parent.parent.parent / 'plot' / 'utils' / 'fRH.json')
|
|
59
|
+
if _RH is not None:
|
|
60
|
+
if _RH > 95:
|
|
61
|
+
_RH = 95
|
|
62
|
+
_RH = round(_RH)
|
|
63
|
+
return _frh.loc[_RH].values.T
|
|
64
|
+
|
|
65
|
+
return 1, 1, 1, 1
|
|
66
|
+
|
|
67
|
+
def revised(self, _df):
|
|
68
|
+
def mode(Mass):
|
|
69
|
+
L_mode = Mass ** 2 / 20 if Mass < 20 else Mass
|
|
70
|
+
S_mode = Mass - L_mode if Mass < 20 else 0
|
|
71
|
+
|
|
72
|
+
return L_mode, S_mode
|
|
73
|
+
|
|
74
|
+
_frh, _frhss, _frhs, _frhl = self.frh(_df['RH'])
|
|
75
|
+
|
|
76
|
+
L_AS, S_AS = mode(_df['AS'])
|
|
77
|
+
L_AN, S_AN = mode(_df['AN'])
|
|
78
|
+
L_OM, S_OM = mode(_df['OM'])
|
|
79
|
+
|
|
80
|
+
_df['AS_ext_dry'] = 2.2 * 1 * S_AS + 4.8 * 1 * L_AS
|
|
81
|
+
_df['AN_ext_dry'] = 2.4 * 1 * S_AN + 5.1 * 1 * L_AN
|
|
82
|
+
_df['OM_ext_dry'] = 2.8 * S_OM + 6.1 * L_OM
|
|
83
|
+
_df['Soil_ext_dry'] = 1 * _df['Soil']
|
|
84
|
+
_df['SS_ext_dry'] = 1.7 * 1 * _df['SS']
|
|
85
|
+
_df['EC_ext_dry'] = 10 * _df['EC']
|
|
86
|
+
_df['total_ext_dry'] = sum(_df['AS_ext_dry':'EC_ext_dry'])
|
|
87
|
+
|
|
88
|
+
_df['AS_ext'] = (2.2 * _frhs * S_AS) + (4.8 * _frhl * L_AS)
|
|
89
|
+
_df['AN_ext'] = (2.4 * _frhs * S_AN) + (5.1 * _frhl * L_AN)
|
|
90
|
+
_df['OM_ext'] = (2.8 * S_OM) + (6.1 * L_OM)
|
|
91
|
+
_df['Soil_ext'] = (1 * _df['Soil'])
|
|
92
|
+
_df['SS_ext'] = (1.7 * _frhss * _df['SS'])
|
|
93
|
+
_df['EC_ext'] = (10 * _df['EC'])
|
|
94
|
+
_df['total_ext'] = sum(_df['AS_ext':'EC_ext'])
|
|
95
|
+
|
|
96
|
+
_df['ALWC_AS_ext'] = _df['AS_ext'] - _df['AS_ext_dry']
|
|
97
|
+
_df['ALWC_AN_ext'] = _df['AN_ext'] - _df['AN_ext_dry']
|
|
98
|
+
_df['ALWC_SS_ext'] = _df['SS_ext'] - _df['SS_ext_dry']
|
|
99
|
+
_df['ALWC_ext'] = _df['total_ext'] - _df['total_ext_dry']
|
|
100
|
+
|
|
101
|
+
_df['fRH_IMPR'] = _df['total_ext'] / _df['total_ext_dry']
|
|
102
|
+
|
|
103
|
+
return _df['AS_ext_dry':]
|
|
104
|
+
|
|
105
|
+
def modified(self, _df):
|
|
106
|
+
_frh, _frhss, _frhs, _frhl = self.frh(_df['RH'])
|
|
107
|
+
|
|
108
|
+
_df['AS_ext_dry'] = 3 * 1 * _df['AS']
|
|
109
|
+
_df['AN_ext_dry'] = 3 * 1 * _df['AN']
|
|
110
|
+
_df['OM_ext_dry'] = 4 * _df['OM']
|
|
111
|
+
_df['Soil_ext_dry'] = 1 * _df['Soil']
|
|
112
|
+
_df['SS_ext_dry'] = 1.7 * 1 * _df['SS']
|
|
113
|
+
_df['EC_ext_dry'] = 10 * _df['EC']
|
|
114
|
+
_df['total_ext_dry'] = sum(_df['AS_ext_dry':'EC_ext_dry'])
|
|
115
|
+
|
|
116
|
+
_df['AS_ext'] = (3 * _frh * _df['AS'])
|
|
117
|
+
_df['AN_ext'] = (3 * _frh * _df['AN'])
|
|
118
|
+
_df['OM_ext'] = (4 * _df['OM'])
|
|
119
|
+
_df['Soil_ext'] = (1 * _df['Soil'])
|
|
120
|
+
_df['SS_ext'] = (1.7 * _frhss * _df['SS'])
|
|
121
|
+
_df['EC_ext'] = (10 * _df['EC'])
|
|
122
|
+
_df['total_ext'] = sum(_df['AS_ext':'EC_ext'])
|
|
123
|
+
|
|
124
|
+
_df['ALWC_AS_ext'] = _df['AS_ext'] - _df['AS_ext_dry']
|
|
125
|
+
_df['ALWC_AN_ext'] = _df['AN_ext'] - _df['AN_ext_dry']
|
|
126
|
+
_df['ALWC_SS_ext'] = _df['SS_ext'] - _df['SS_ext_dry']
|
|
127
|
+
_df['ALWC_ext'] = _df['total_ext'] - _df['total_ext_dry']
|
|
128
|
+
|
|
129
|
+
_df['fRH_IMPR'] = _df['total_ext'] / _df['total_ext_dry']
|
|
130
|
+
|
|
131
|
+
return _df['AS_ext_dry':]
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def gas(_df):
|
|
135
|
+
_df['ScatteringByGas'] = (11.4 * 293 / (273 + _df['AT']))
|
|
136
|
+
_df['AbsorptionByGas'] = (0.33 * _df['NO2'])
|
|
137
|
+
_df['ExtinctionByGas'] = _df['ScatteringByGas'] + _df['AbsorptionByGas']
|
|
138
|
+
return _df['ScatteringByGas':]
|
|
139
|
+
|
|
140
|
+
def process_data(self, reset: bool = False, save_file: Path | str = None,
|
|
141
|
+
version: Literal["revised", "modified"] = "revised"):
|
|
142
|
+
save_file = Path(save_file)
|
|
143
|
+
if save_file.exists() and not reset:
|
|
144
|
+
return read_csv(save_file, parse_dates=['Time'], index_col='Time')
|
|
145
|
+
else:
|
|
146
|
+
# data_files = ['EPB.csv', 'IMPACT.csv', 'chemical.csv']
|
|
147
|
+
df = concat([DataReader(file) for file in self.file_paths], axis=1)
|
|
148
|
+
|
|
149
|
+
# particle contribution '銨不足不納入計算'
|
|
150
|
+
improve_input_df = df.loc[df['NH4_status'] != 'Deficiency', ['AS', 'AN', 'OM', 'Soil', 'SS', 'EC', 'RH']]
|
|
151
|
+
|
|
152
|
+
df_improve = improve_input_df.dropna().copy().apply(self.revised if version == 'revised' else self.modified,
|
|
153
|
+
axis=1)
|
|
154
|
+
|
|
155
|
+
# gas contribution
|
|
156
|
+
df_ext_gas = df[['NO2', 'AT']].dropna().copy().apply(self.gas, axis=1)
|
|
157
|
+
|
|
158
|
+
_df = concat([df_improve, df_ext_gas], axis=1).reindex(df.index.copy())
|
|
159
|
+
_df.to_csv(save_file)
|
|
160
|
+
|
|
161
|
+
return _df
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from pandas import read_csv, concat, DataFrame
|
|
5
|
+
|
|
6
|
+
from AeroViz.process.core import DataProc
|
|
7
|
+
from AeroViz.tools.datareader import DataReader
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OthersProc(DataProc):
|
|
11
|
+
"""
|
|
12
|
+
A class for process impact data.
|
|
13
|
+
|
|
14
|
+
Parameters:
|
|
15
|
+
-----------
|
|
16
|
+
reset : bool, optional
|
|
17
|
+
If True, resets the process. Default is False.
|
|
18
|
+
filename : str, optional
|
|
19
|
+
The name of the file to process. Default is None.
|
|
20
|
+
|
|
21
|
+
Methods:
|
|
22
|
+
--------
|
|
23
|
+
process_data():
|
|
24
|
+
Process data and save the result.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
-----------
|
|
28
|
+
DEFAULT_PATH : Path
|
|
29
|
+
The default path for data files.
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
---------
|
|
33
|
+
>>> df = OthersProc().process_data(reset=True, filename=None)
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, file_paths: Path | list[Path | str] = None):
|
|
38
|
+
super().__init__()
|
|
39
|
+
self.file_paths = [Path(fp) for fp in file_paths]
|
|
40
|
+
|
|
41
|
+
def process_data(self, reset: bool = False, save_file: Path | str = None) -> DataFrame:
|
|
42
|
+
save_file = Path(save_file)
|
|
43
|
+
if save_file.exists() and not reset:
|
|
44
|
+
return read_csv(save_file, parse_dates=['Time'], index_col='Time')
|
|
45
|
+
else:
|
|
46
|
+
df = concat([DataReader(file) for file in self.file_paths], axis=1)
|
|
47
|
+
|
|
48
|
+
results = DataFrame(index=df.index)
|
|
49
|
+
|
|
50
|
+
results['PG'] = df[
|
|
51
|
+
['Scattering', 'Absorption', 'ScatteringByGas', 'AbsorptionByGas']].dropna().copy().apply(np.sum,
|
|
52
|
+
axis=1)
|
|
53
|
+
results['MAC'] = df['Absorption'] / df['T_EC']
|
|
54
|
+
results['Ox'] = df['NO2'] + df['O3']
|
|
55
|
+
results['N2O5_tracer'] = df['NO2'] * df['O3']
|
|
56
|
+
results['Vis_cal'] = 1096 / df['Extinction']
|
|
57
|
+
# results['fRH_Mix'] = df['Bext'] / df['Extinction']
|
|
58
|
+
# results['fRH_PNSD'] = df['Bext_internal'] / df['Bext_dry']
|
|
59
|
+
results['fRH_IMPR'] = df['total_ext'] / df['total_ext_dry']
|
|
60
|
+
results['OCEC_ratio'] = df['O_OC'] / df['O_EC']
|
|
61
|
+
results['PM1/PM25'] = np.where(df['PM1'] / df['PM25'] < 1, df['PM1'] / df['PM25'], np.nan)
|
|
62
|
+
# results['MEE_PNSD'] = df['Bext_internal'] / df['PM25']
|
|
63
|
+
# results['MEE_dry_PNSD'] = df['Bext_dry'] / df['PM25']
|
|
64
|
+
|
|
65
|
+
return results
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from pandas import concat, read_csv, DataFrame
|
|
4
|
+
|
|
5
|
+
from AeroViz.process.core import DataProc
|
|
6
|
+
from AeroViz.process.core.SizeDist import SizeDist
|
|
7
|
+
from AeroViz.process.script.AbstractDistCalc import DistributionCalculator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ParticleSizeDistProc(DataProc):
|
|
11
|
+
"""
|
|
12
|
+
A class for process particle size distribution (PSD) data.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
filename : str, optional
|
|
17
|
+
The name of the PSD data file.
|
|
18
|
+
Defaults to 'PNSD_dNdlogdp.csv' in the default path.
|
|
19
|
+
|
|
20
|
+
Attributes
|
|
21
|
+
----------
|
|
22
|
+
file_path : Path
|
|
23
|
+
The directory path where the PSD data file is located.
|
|
24
|
+
|
|
25
|
+
psd : SizeDist
|
|
26
|
+
The SizeDist object.
|
|
27
|
+
|
|
28
|
+
Methods
|
|
29
|
+
-------
|
|
30
|
+
process_data(filename='PSD.csv')
|
|
31
|
+
Process and save overall PSD properties.
|
|
32
|
+
|
|
33
|
+
Examples
|
|
34
|
+
--------
|
|
35
|
+
Example 1: Use default path and filename
|
|
36
|
+
>>> psd_data = ParticleSizeDistProc(filename='PNSD_dNdlogdp.csv').process_data(reset=True)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, file_path: Path | str = None):
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.file_path = Path(file_path)
|
|
42
|
+
|
|
43
|
+
self.psd = SizeDist(read_csv(file_path, parse_dates=['Time'], index_col='Time'))
|
|
44
|
+
|
|
45
|
+
def process_data(self, reset: bool = False, save_file: Path | str = None) -> DataFrame:
|
|
46
|
+
save_file = Path(save_file)
|
|
47
|
+
if save_file.exists() and not reset:
|
|
48
|
+
return read_csv(save_file, parse_dates=['Time'], index_col='Time')
|
|
49
|
+
|
|
50
|
+
number = DistributionCalculator('number', self.psd).useApply()
|
|
51
|
+
surface = DistributionCalculator('surface', self.psd).useApply()
|
|
52
|
+
volume = DistributionCalculator('volume', self.psd).useApply()
|
|
53
|
+
|
|
54
|
+
surface.to_csv(save_file.parent / 'PSSD_dSdlogdp.csv')
|
|
55
|
+
volume.to_csv(save_file.parent / 'PVSD_dVdlogdp.csv')
|
|
56
|
+
|
|
57
|
+
result_df = concat(
|
|
58
|
+
[DistributionCalculator('property', SizeDist(data=number, weighting='n')).useApply(),
|
|
59
|
+
DistributionCalculator('property', SizeDist(data=surface, weighting='s')).useApply(),
|
|
60
|
+
DistributionCalculator('property', SizeDist(data=volume, weighting='v')).useApply()
|
|
61
|
+
], axis=1)
|
|
62
|
+
|
|
63
|
+
result_df.to_csv(save_file)
|
|
64
|
+
return result_df
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ExtinctionDistProc(DataProc):
|
|
68
|
+
|
|
69
|
+
def __init__(self, file_path: Path | str = 'PNSD_dNdlogdp.csv', file_path_chem: Path | str = 'chemical.csv'):
|
|
70
|
+
super().__init__()
|
|
71
|
+
self.file_path = Path(file_path)
|
|
72
|
+
self.file_path_chem = Path(file_path_chem)
|
|
73
|
+
|
|
74
|
+
self.psd = SizeDist(read_csv(file_path, parse_dates=['Time'], index_col='Time'))
|
|
75
|
+
self.RI = read_csv(file_path_chem, parse_dates=['Time'], index_col='Time')[['n_dry', 'n_amb', 'k_dry', 'k_amb',
|
|
76
|
+
'AS_volume_ratio',
|
|
77
|
+
'AN_volume_ratio',
|
|
78
|
+
'OM_volume_ratio',
|
|
79
|
+
'Soil_volume_ratio',
|
|
80
|
+
'SS_volume_ratio',
|
|
81
|
+
'EC_volume_ratio',
|
|
82
|
+
'ALWC_volume_ratio']]
|
|
83
|
+
|
|
84
|
+
def process_data(self, reset: bool = False, save_file: Path | str = 'PESD.csv'):
|
|
85
|
+
save_file = Path(save_file)
|
|
86
|
+
if save_file.exists() and not reset:
|
|
87
|
+
return read_csv(save_file, parse_dates=['Time']).set_index('Time')
|
|
88
|
+
|
|
89
|
+
ext_internal = DistributionCalculator('extinction', self.psd, self.RI, method='internal',
|
|
90
|
+
result_type='extinction').useApply()
|
|
91
|
+
ext_external = DistributionCalculator('extinction', self.psd, self.RI, method='external',
|
|
92
|
+
result_type='extinction').useApply()
|
|
93
|
+
|
|
94
|
+
ext_internal.to_csv(save_file.parent / 'PESD_dextdlogdp_internal.csv')
|
|
95
|
+
ext_external.to_csv(save_file.parent / 'PESD_dextdlogdp_external.csv')
|
|
96
|
+
|
|
97
|
+
result_df = concat([
|
|
98
|
+
DistributionCalculator('property', SizeDist(data=ext_internal, weighting='ext_in')).useApply(),
|
|
99
|
+
DistributionCalculator('property', SizeDist(data=ext_internal, weighting='ext_ex')).useApply(),
|
|
100
|
+
], axis=1)
|
|
101
|
+
|
|
102
|
+
result_df.to_csv(save_file)
|
|
103
|
+
return result_df
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from pandas import DataFrame, read_csv, concat
|
|
5
|
+
|
|
6
|
+
from AeroViz.process.core import DataProc
|
|
7
|
+
from AeroViz.process.core.SizeDist import SizeDist
|
|
8
|
+
from AeroViz.tools import DataReader
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DryPSDProc(DataProc):
|
|
12
|
+
"""
|
|
13
|
+
A class for process impact data.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
reset : bool, optional
|
|
18
|
+
If True, resets the process. Default is False.
|
|
19
|
+
filename : str, optional
|
|
20
|
+
The name of the file to process. Default is None.
|
|
21
|
+
|
|
22
|
+
Methods
|
|
23
|
+
-------
|
|
24
|
+
process_data():
|
|
25
|
+
Process data and save the result.
|
|
26
|
+
|
|
27
|
+
Attributes
|
|
28
|
+
----------
|
|
29
|
+
DEFAULT_PATH : Path
|
|
30
|
+
The default path for data files.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Examples
|
|
34
|
+
--------
|
|
35
|
+
>>> df = DryPSDProc(reset=True, filename='PNSD_dNdlogdp_dry.csv').process_data()
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, file_path: Path | str = 'PNSD_dNdlogdp.csv', file_path_chem: Path | str = 'chemical.csv'):
|
|
39
|
+
super().__init__()
|
|
40
|
+
self.file_path = Path(file_path)
|
|
41
|
+
self.file_path_chem = Path(file_path_chem)
|
|
42
|
+
|
|
43
|
+
self.psd = SizeDist(read_csv(file_path, parse_dates=['Time'], index_col='Time'))
|
|
44
|
+
self.RI = read_csv(file_path_chem, parse_dates=['Time'], index_col='Time')[['n_dry', 'n_amb', 'k_dry', 'k_amb',
|
|
45
|
+
'AS_volume_ratio',
|
|
46
|
+
'AN_volume_ratio',
|
|
47
|
+
'OM_volume_ratio',
|
|
48
|
+
'Soil_volume_ratio',
|
|
49
|
+
'SS_volume_ratio',
|
|
50
|
+
'EC_volume_ratio',
|
|
51
|
+
'ALWC_volume_ratio']]
|
|
52
|
+
|
|
53
|
+
def process_data(self, reset: bool = False, save_filename: Path | str = None) -> DataFrame:
|
|
54
|
+
save_filename = Path(save_filename)
|
|
55
|
+
if save_filename.exists() and not reset:
|
|
56
|
+
return read_csv(save_filename, parse_dates=['Time']).set_index('Time')
|
|
57
|
+
_df = concat([self.psd, self.RI], axis=1)
|
|
58
|
+
_df.to_csv(save_filename)
|
|
59
|
+
return _df
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def dry_PNSD_process(dist, dp, **kwargs):
|
|
63
|
+
ndp = np.array(dist[:np.size(dp)])
|
|
64
|
+
gRH = resolved_gRH(dp, dist['gRH'], uniform=True)
|
|
65
|
+
|
|
66
|
+
dry_dp = dp / gRH
|
|
67
|
+
belong_which_ibin = np.digitize(dry_dp, dp) - 1
|
|
68
|
+
|
|
69
|
+
result = {}
|
|
70
|
+
for i, (ibin, dn) in enumerate(zip(belong_which_ibin, ndp)):
|
|
71
|
+
if dp[ibin] not in result:
|
|
72
|
+
result[dp[ibin]] = []
|
|
73
|
+
result[dp[ibin]].append(ndp[i])
|
|
74
|
+
|
|
75
|
+
dry_ndp = []
|
|
76
|
+
for key, val in result.items():
|
|
77
|
+
dry_ndp.append(sum(val) / len(val))
|
|
78
|
+
|
|
79
|
+
return np.array(dry_ndp)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def resolved_gRH(dp, gRH=1.31, uniform=True):
|
|
83
|
+
if uniform:
|
|
84
|
+
return np.array([gRH] * dp.size)
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
lognorm_dist = lambda x, geoMean, geoStd: (gRH / (np.log10(geoStd) * np.sqrt(2 * np.pi))) * np.exp(
|
|
88
|
+
-(x - np.log10(geoMean)) ** 2 / (2 * np.log10(geoStd) ** 2))
|
|
89
|
+
abc = lognorm_dist(np.log10(dp), 200, 2.0)
|
|
90
|
+
return np.where(abc < 1, 1, abc)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if __name__ == '__main__':
|
|
94
|
+
pass
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from pandas import DataFrame
|
|
4
|
+
|
|
5
|
+
from AeroViz.process.core.SizeDist import SizeDist
|
|
6
|
+
from AeroViz.process.method import Mie_PESD
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def retrieve_RI(_df: DataFrame,
|
|
10
|
+
_PNSD: DataFrame,
|
|
11
|
+
nMin: float = 1.33,
|
|
12
|
+
nMax: float = 1.60,
|
|
13
|
+
kMin: float = 0.00,
|
|
14
|
+
kMax: float = 0.60,
|
|
15
|
+
spaceSize: int = 31,
|
|
16
|
+
dlogdp: float = 0.014
|
|
17
|
+
) -> DataFrame:
|
|
18
|
+
nRange = np.linspace(nMin, nMax, num=spaceSize)
|
|
19
|
+
kRange = np.linspace(kMin, kMax, spaceSize)
|
|
20
|
+
Delta_array = np.zeros((spaceSize, spaceSize))
|
|
21
|
+
# 同一時間除了折射率其餘數據皆相同 因此在折射率的迴圈外
|
|
22
|
+
bext_mea, bsca_mea, babs_mea = _df['Extinction'], _df['Scattering'], _df['Absorption']
|
|
23
|
+
|
|
24
|
+
dp = SizeDist(data=_PNSD).dp
|
|
25
|
+
for ki, k in enumerate(kRange):
|
|
26
|
+
for ni, n in enumerate(nRange):
|
|
27
|
+
m = n + (1j * k)
|
|
28
|
+
ndp = np.array(_df[3:])
|
|
29
|
+
|
|
30
|
+
ext_dist, sca_dist, abs_dist = Mie_PESD(m, 550, dp, ndp)
|
|
31
|
+
|
|
32
|
+
bext_cal = sum(ext_dist) * dlogdp
|
|
33
|
+
bsca_cal = sum(sca_dist) * dlogdp
|
|
34
|
+
babs_cal = sum(abs_dist) * dlogdp
|
|
35
|
+
|
|
36
|
+
Delta_array[ni][ki] = ((babs_mea - babs_cal) / 18.23) ** 2 + ((bsca_mea - bsca_cal) / 83.67) ** 2
|
|
37
|
+
|
|
38
|
+
min_delta = Delta_array.argmin()
|
|
39
|
+
next_n = nRange[(min_delta // spaceSize)]
|
|
40
|
+
next_k = kRange[(min_delta % spaceSize)]
|
|
41
|
+
|
|
42
|
+
# 將網格變小
|
|
43
|
+
nMin_small = next_n - 0.02 if next_n > 1.33 else 1.33
|
|
44
|
+
nMax_small = next_n + 0.02
|
|
45
|
+
kMin_small = next_k - 0.04 if next_k > 0.04 else 0
|
|
46
|
+
kMax_small = next_k + 0.04
|
|
47
|
+
spaceSize_small = 41
|
|
48
|
+
|
|
49
|
+
nRange_small = np.linspace(nMin_small, nMax_small, spaceSize_small)
|
|
50
|
+
kRange_small = np.linspace(kMin_small, kMax_small, spaceSize_small)
|
|
51
|
+
Delta_array_small = np.zeros((spaceSize_small, spaceSize_small))
|
|
52
|
+
# 所有數據與大網格一致所以使用上方便數即可
|
|
53
|
+
for ki, k in enumerate(kRange_small):
|
|
54
|
+
for ni, n in enumerate(nRange_small):
|
|
55
|
+
m = n + (1j * k)
|
|
56
|
+
ndp = np.array(_df[3:])
|
|
57
|
+
ext_dist, sca_dist, abs_dist = Mie_PESD(m, 550, dp, ndp)
|
|
58
|
+
|
|
59
|
+
bext_cal = sum(ext_dist) * dlogdp
|
|
60
|
+
bsca_cal = sum(sca_dist) * dlogdp
|
|
61
|
+
babs_cal = sum(abs_dist) * dlogdp
|
|
62
|
+
|
|
63
|
+
Delta_array_small[ni][ki] = ((bext_mea - bext_cal) / 18.23) ** 2 + ((bsca_mea - bsca_cal) / 83.67) ** 2
|
|
64
|
+
|
|
65
|
+
min_delta_small = Delta_array_small.argmin()
|
|
66
|
+
_df['re_real'] = (nRange_small[(min_delta_small // spaceSize_small)])
|
|
67
|
+
_df['re_imaginary'] = (kRange_small[(min_delta_small % spaceSize_small)])
|
|
68
|
+
|
|
69
|
+
print(f'\t\tReal part:{_df['re_real']}\tIm part:{_df['re_imaginary']}', end='')
|
|
70
|
+
return _df['re_real':]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from AeroViz.rawDataReader.script import *
|
|
4
|
+
from AeroViz.rawDataReader.utils.config import meta
|
|
5
|
+
|
|
6
|
+
__all__ = ['RawDataReader']
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def RawDataReader(instrument_name: str,
|
|
10
|
+
_path,
|
|
11
|
+
QC: bool = True,
|
|
12
|
+
csv_raw: bool = True,
|
|
13
|
+
reset: bool = False,
|
|
14
|
+
rate: bool = False,
|
|
15
|
+
append_data: bool = False,
|
|
16
|
+
update_meta=None,
|
|
17
|
+
start: datetime | None = None,
|
|
18
|
+
end: datetime | None = None,
|
|
19
|
+
mean_freq='1h',
|
|
20
|
+
csv_out=True,
|
|
21
|
+
**kwargs
|
|
22
|
+
):
|
|
23
|
+
# Mapping of instrument names to their respective classes
|
|
24
|
+
instrument_class_map = {
|
|
25
|
+
'NEPH': NEPH,
|
|
26
|
+
'Aurora': Aurora,
|
|
27
|
+
'Table': Table,
|
|
28
|
+
'EPA_vertical': EPA_vertical,
|
|
29
|
+
'APS_3321': APS_3321,
|
|
30
|
+
'SMPS_TH': SMPS_TH,
|
|
31
|
+
'AE33': AE33,
|
|
32
|
+
'AE43': AE43,
|
|
33
|
+
'BC1054': BC1054,
|
|
34
|
+
'MA350': MA350,
|
|
35
|
+
'TEOM': TEOM,
|
|
36
|
+
'OCEC_RES': OCEC_RES,
|
|
37
|
+
'OCEC_LCRES': OCEC_LCRES,
|
|
38
|
+
'IGAC_TH': IGAC_TH,
|
|
39
|
+
'IGAC_ZM': IGAC_ZM,
|
|
40
|
+
'VOC_TH': VOC_TH,
|
|
41
|
+
'VOC_ZM': VOC_ZM,
|
|
42
|
+
'SMPS_genr': SMPS_genr,
|
|
43
|
+
'SMPS_aim11': SMPS_aim11,
|
|
44
|
+
'GRIMM': GRIMM
|
|
45
|
+
# Add other instruments and their corresponding classes here
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Check if the instrument name is in the map
|
|
49
|
+
if instrument_name not in meta.keys():
|
|
50
|
+
raise ValueError(f"Instrument name '{instrument_name}' is not valid. \nMust be one of: {list(meta.keys())}")
|
|
51
|
+
|
|
52
|
+
# Instantiate the class and return the instance
|
|
53
|
+
reader_module = instrument_class_map[instrument_name].Reader(
|
|
54
|
+
_path=_path,
|
|
55
|
+
QC=QC,
|
|
56
|
+
csv_raw=csv_raw,
|
|
57
|
+
reset=reset,
|
|
58
|
+
rate=rate,
|
|
59
|
+
append_data=append_data,
|
|
60
|
+
update_meta=update_meta
|
|
61
|
+
)
|
|
62
|
+
return reader_module(
|
|
63
|
+
start=start,
|
|
64
|
+
end=end,
|
|
65
|
+
mean_freq=mean_freq,
|
|
66
|
+
csv_out=csv_out,
|
|
67
|
+
**kwargs
|
|
68
|
+
)
|