AeroViz 0.1.6__py3-none-any.whl → 0.1.8__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 (57) hide show
  1. AeroViz/data/240228_00.txt +101 -0
  2. AeroViz/dataProcess/Chemistry/_ocec.py +20 -7
  3. AeroViz/plot/__init__.py +2 -0
  4. AeroViz/plot/hysplit/__init__.py +1 -0
  5. AeroViz/plot/hysplit/hysplit.py +79 -0
  6. AeroViz/plot/meteorology/meteorology.py +2 -0
  7. AeroViz/plot/optical/optical.py +60 -59
  8. AeroViz/plot/pie.py +14 -2
  9. AeroViz/plot/radar.py +184 -0
  10. AeroViz/plot/scatter.py +16 -7
  11. AeroViz/plot/templates/diurnal_pattern.py +24 -7
  12. AeroViz/plot/templates/koschmieder.py +11 -8
  13. AeroViz/plot/timeseries/template.py +2 -2
  14. AeroViz/plot/timeseries/timeseries.py +47 -7
  15. AeroViz/rawDataReader/__init__.py +75 -68
  16. AeroViz/rawDataReader/config/supported_instruments.py +52 -19
  17. AeroViz/rawDataReader/core/__init__.py +194 -106
  18. AeroViz/rawDataReader/script/AE33.py +11 -6
  19. AeroViz/rawDataReader/script/AE43.py +10 -5
  20. AeroViz/rawDataReader/script/Aurora.py +14 -10
  21. AeroViz/rawDataReader/script/BC1054.py +10 -6
  22. AeroViz/rawDataReader/script/EPA.py +39 -0
  23. AeroViz/rawDataReader/script/GRIMM.py +1 -2
  24. AeroViz/rawDataReader/script/IGAC.py +6 -23
  25. AeroViz/rawDataReader/script/MA350.py +12 -5
  26. AeroViz/rawDataReader/script/Minion.py +107 -30
  27. AeroViz/rawDataReader/script/NEPH.py +15 -5
  28. AeroViz/rawDataReader/script/OCEC.py +39 -15
  29. AeroViz/rawDataReader/script/SMPS.py +1 -0
  30. AeroViz/rawDataReader/script/TEOM.py +15 -11
  31. AeroViz/rawDataReader/script/VOC.py +1 -1
  32. AeroViz/rawDataReader/script/XRF.py +11 -0
  33. AeroViz/rawDataReader/script/__init__.py +2 -2
  34. {AeroViz-0.1.6.dist-info → AeroViz-0.1.8.dist-info}/METADATA +54 -30
  35. {AeroViz-0.1.6.dist-info → AeroViz-0.1.8.dist-info}/RECORD +40 -51
  36. AeroViz/process/__init__.py +0 -31
  37. AeroViz/process/core/DataProc.py +0 -19
  38. AeroViz/process/core/SizeDist.py +0 -90
  39. AeroViz/process/core/__init__.py +0 -4
  40. AeroViz/process/method/__init__.py +0 -2
  41. AeroViz/process/method/prop.py +0 -62
  42. AeroViz/process/script/AbstractDistCalc.py +0 -143
  43. AeroViz/process/script/Chemical.py +0 -177
  44. AeroViz/process/script/IMPACT.py +0 -49
  45. AeroViz/process/script/IMPROVE.py +0 -161
  46. AeroViz/process/script/Others.py +0 -65
  47. AeroViz/process/script/PSD.py +0 -103
  48. AeroViz/process/script/PSD_dry.py +0 -93
  49. AeroViz/process/script/__init__.py +0 -5
  50. AeroViz/process/script/retrieve_RI.py +0 -69
  51. AeroViz/rawDataReader/script/EPA_vertical.py +0 -46
  52. AeroViz/rawDataReader/script/Table.py +0 -27
  53. /AeroViz/{process/method → plot/optical}/PyMieScatt_update.py +0 -0
  54. /AeroViz/{process/method → plot/optical}/mie_theory.py +0 -0
  55. {AeroViz-0.1.6.dist-info → AeroViz-0.1.8.dist-info}/LICENSE +0 -0
  56. {AeroViz-0.1.6.dist-info → AeroViz-0.1.8.dist-info}/WHEEL +0 -0
  57. {AeroViz-0.1.6.dist-info → AeroViz-0.1.8.dist-info}/top_level.txt +0 -0
@@ -1,177 +0,0 @@
1
- from pathlib import Path
2
-
3
- import numpy as np
4
- from pandas import read_csv, concat, notna, DataFrame, to_numeric
5
-
6
- from AeroViz.process.core import DataProc
7
- from AeroViz.tools.datareader import DataReader
8
-
9
-
10
- class ChemicalProc(DataProc):
11
- """
12
- A class for process 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
-
21
- Methods:
22
- --------
23
- mass(_df):
24
- Calculate mass-related parameters.
25
-
26
- volume(_df):
27
- Calculate volume-related parameters.
28
-
29
- volume_average_mixing(_df):
30
- Calculate volume average mixing parameters.
31
-
32
- process_data():
33
- Process data and save the result.
34
-
35
- Attributes:
36
- -----------
37
- DEFAULT_PATH : Path
38
- The default path for data files.
39
-
40
- Examples:
41
- ---------
42
-
43
- """
44
-
45
- def __init__(self, file_paths: list[Path | str] = None):
46
- super().__init__()
47
- self.file_paths = [Path(fp) for fp in file_paths]
48
-
49
- @staticmethod
50
- def mass(_df): # Series like
51
- Ammonium, Sulfate, Nitrate, OC, Soil, SS, EC, PM25 = _df
52
- status = (Ammonium / 18) / (2 * (Sulfate / 96) + (Nitrate / 62))
53
-
54
- if status >= 1:
55
- _df['NH4_status'] = 'Enough'
56
- _df['AS'] = 1.375 * Sulfate
57
- _df['AN'] = 1.29 * Nitrate
58
-
59
- if status < 1:
60
- _df['NH4_status'] = 'Deficiency'
61
- mol_A = Ammonium / 18
62
- mol_S = Sulfate / 96
63
- mol_N = Nitrate / 62
64
- residual = mol_A - 2 * mol_S
65
-
66
- if residual > 0:
67
- _df['AS'] = 1.375 * Sulfate
68
- _df['AN'] = residual * 80 if residual <= mol_N else mol_N * 80
69
-
70
- else:
71
- _df['AS'] = mol_A / 2 * 132 if mol_A <= 2 * mol_S else mol_S * 132
72
- _df['AN'] = 0
73
-
74
- _df['OM'] = 1.8 * OC
75
- _df['Soil'] = 28.57 * Soil / 1000
76
- _df['SS'] = 2.54 * SS
77
- _df['EC'] = EC
78
- _df['SIA'] = _df['AS'] + _df['AN']
79
- _df['total_mass'] = _df[['AS', 'AN', 'OM', 'Soil', 'SS', 'EC']].sum()
80
- species_lst = ['AS', 'AN', 'OM', 'Soil', 'SS', 'EC', 'SIA', 'unknown_mass']
81
-
82
- _df['unknown_mass'] = PM25 - _df['total_mass'] if PM25 >= _df['total_mass'] else 0
83
- for _species, _val in _df[species_lst].items():
84
- _df[f'{_species}_ratio'] = _val / PM25 if PM25 >= _df['total_mass'] else _val / _df['total_mass']
85
-
86
- return _df['NH4_status':]
87
-
88
- @staticmethod
89
- def volume(_df):
90
- _df['AS_volume'] = (_df['AS'] / 1.76)
91
- _df['AN_volume'] = (_df['AN'] / 1.73)
92
- _df['OM_volume'] = (_df['OM'] / 1.4)
93
- _df['Soil_volume'] = (_df['Soil'] / 2.6)
94
- _df['SS_volume'] = (_df['SS'] / 2.16)
95
- _df['EC_volume'] = (_df['EC'] / 1.5)
96
- _df['ALWC_volume'] = _df['ALWC']
97
- _df['total_volume'] = sum(_df['AS_volume':'EC_volume'])
98
-
99
- for _species, _val in _df['AS_volume':'ALWC_volume'].items():
100
- _df[f'{_species}_ratio'] = _val / _df['total_volume']
101
-
102
- _df['density'] = _df['total_mass'] / _df['total_volume']
103
- return _df['AS_volume':]
104
-
105
- @staticmethod
106
- def volume_average_mixing(_df):
107
- _df['n_dry'] = (1.53 * _df['AS_volume_ratio'] +
108
- 1.55 * _df['AN_volume_ratio'] +
109
- 1.55 * _df['OM_volume_ratio'] +
110
- 1.56 * _df['Soil_volume_ratio'] +
111
- 1.54 * _df['SS_volume_ratio'] +
112
- 1.80 * _df['EC_volume_ratio'])
113
-
114
- _df['k_dry'] = (0.00 * _df['OM_volume_ratio'] +
115
- 0.01 * _df['Soil_volume_ratio'] +
116
- 0.54 * _df["EC_volume_ratio"])
117
-
118
- # 檢查_df['ALWC']是否缺失 -> 有值才計算ambient的折射率
119
- if notna(_df['ALWC']):
120
- v_dry = _df['total_volume']
121
- v_wet = _df['total_volume'] + _df['ALWC']
122
-
123
- multiplier = v_dry / v_wet
124
- _df['ALWC_volume_ratio'] = (1 - multiplier)
125
-
126
- _df['n_amb'] = (1.53 * _df['AS_volume_ratio'] +
127
- 1.55 * _df['AN_volume_ratio'] +
128
- 1.55 * _df['OM_volume_ratio'] +
129
- 1.56 * _df['Soil_volume_ratio'] +
130
- 1.54 * _df['SS_volume_ratio'] +
131
- 1.80 * _df['EC_volume_ratio']) * multiplier + \
132
- (1.33 * _df['ALWC_volume_ratio'])
133
-
134
- _df['k_amb'] = (0.00 * _df['OM_volume_ratio'] +
135
- 0.01 * _df['Soil_volume_ratio'] +
136
- 0.54 * _df['EC_volume_ratio']) * multiplier
137
-
138
- _df['gRH'] = (v_wet / v_dry) ** (1 / 3)
139
-
140
- return _df[['n_dry', 'k_dry', 'n_amb', 'k_amb', 'gRH']]
141
-
142
- @staticmethod
143
- def kappa(_df, diameter=0.5):
144
- surface_tension, Mw, density, universal_gas_constant = 0.072, 18, 1, 8.314 # J/mole*K
145
-
146
- A = 4 * (surface_tension * Mw) / (density * universal_gas_constant * (_df['AT'] + 273))
147
- power = A / diameter
148
- a_w = (_df['RH'] / 100) * (np.exp(-power))
149
-
150
- _df['kappa_chem'] = (_df['gRH'] ** 3 - 1) * (1 - a_w) / a_w
151
- _df['kappa_vam'] = np.nan
152
-
153
- @staticmethod
154
- def ISORROPIA():
155
- pass
156
-
157
- def process_data(self, reset: bool = False, save_file: Path | str = None) -> DataFrame:
158
- save_file = Path(save_file)
159
- if save_file.exists() and not reset:
160
- return read_csv(save_file, parse_dates=['Time'], index_col='Time')
161
- else:
162
- df = concat([DataReader(file) for file in self.file_paths], axis=1).apply(to_numeric, errors='coerce')
163
-
164
- df_mass = df[['NH4+', 'SO42-', 'NO3-', 'Optical_OC', 'Fe', 'Na+', 'Optical_EC', 'PM2.5']].dropna().apply(
165
- self.mass,
166
- axis=1)
167
- df_mass['ALWC'] = df['ALWC']
168
- df_volume = df_mass[['AS', 'AN', 'OM', 'Soil', 'SS', 'EC', 'total_mass', 'ALWC']].dropna().apply(
169
- self.volume,
170
- axis=1)
171
- df_volume['ALWC'] = df['ALWC']
172
- df_vam = df_volume.dropna().apply(self.volume_average_mixing, axis=1)
173
-
174
- _df = concat([df_mass, df_volume.drop(['ALWC'], axis=1), df_vam], axis=1).reindex(df.index.copy())
175
- _df.to_csv(save_file)
176
-
177
- return _df
@@ -1,49 +0,0 @@
1
- from pathlib import Path
2
-
3
- from pandas import DataFrame, read_csv, concat
4
-
5
- from AeroViz.process.core import DataProc
6
- from AeroViz.tools.datareader import DataReader
7
-
8
-
9
- class ImpactProc(DataProc):
10
- """
11
- A class for processing impact data.
12
-
13
- Parameters:
14
- -----------
15
- reset : bool, optional
16
- If True, resets the processing. Default is False.
17
- save_filename : str or Path, optional
18
- The name or path to save the processed data. Default is 'IMPACT.csv'.
19
-
20
- Methods:
21
- --------
22
- process_data(reset: bool = False, save_filename: str | Path = 'IMPACT.csv') -> DataFrame:
23
- Process data and save the result.
24
-
25
- save_data(data: DataFrame, save_filename: str | Path):
26
- Save processed data to a file.
27
-
28
- Attributes:
29
- -----------
30
- DEFAULT_PATH : Path
31
- The default path for data files.
32
-
33
- Examples:
34
- ---------
35
- >>> df_custom = ImpactProc().process_data(reset=True, save_filename='custom_file.csv')
36
- """
37
-
38
- def __init__(self, file_paths: list[Path | str] = None):
39
- super().__init__()
40
- self.file_paths = [Path(fp) for fp in file_paths]
41
-
42
- def process_data(self, reset: bool = False, save_file: Path | str = None) -> DataFrame:
43
- save_file = Path(save_file)
44
- if save_file.exists() and not reset:
45
- return read_csv(save_file, parse_dates=['Time'], index_col='Time')
46
- else:
47
- _df = concat([DataReader(file) for file in self.file_paths], axis=1)
48
- _df.to_csv(save_file)
49
- return _df
@@ -1,161 +0,0 @@
1
- from pathlib import Path
2
- from typing import Literal
3
-
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
@@ -1,65 +0,0 @@
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['PM2.5'] < 1, df['PM1'] / df['PM2.5'], 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
@@ -1,103 +0,0 @@
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
@@ -1,93 +0,0 @@
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
-
9
-
10
- class DryPSDProc(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
-
32
- Examples
33
- --------
34
- >>> df = DryPSDProc(reset=True, filename='PNSD_dNdlogdp_dry.csv').process_data()
35
- """
36
-
37
- def __init__(self, file_path: Path | str = 'PNSD_dNdlogdp.csv', file_path_chem: Path | str = 'chemical.csv'):
38
- super().__init__()
39
- self.file_path = Path(file_path)
40
- self.file_path_chem = Path(file_path_chem)
41
-
42
- self.psd = SizeDist(read_csv(file_path, parse_dates=['Time'], index_col='Time'))
43
- self.RI = read_csv(file_path_chem, parse_dates=['Time'], index_col='Time')[['n_dry', 'n_amb', 'k_dry', 'k_amb',
44
- 'AS_volume_ratio',
45
- 'AN_volume_ratio',
46
- 'OM_volume_ratio',
47
- 'Soil_volume_ratio',
48
- 'SS_volume_ratio',
49
- 'EC_volume_ratio',
50
- 'ALWC_volume_ratio']]
51
-
52
- def process_data(self, reset: bool = False, save_filename: Path | str = None) -> DataFrame:
53
- save_filename = Path(save_filename)
54
- if save_filename.exists() and not reset:
55
- return read_csv(save_filename, parse_dates=['Time']).set_index('Time')
56
- _df = concat([self.psd, self.RI], axis=1)
57
- _df.to_csv(save_filename)
58
- return _df
59
-
60
-
61
- def dry_PNSD_process(dist, dp, **kwargs):
62
- ndp = np.array(dist[:np.size(dp)])
63
- gRH = resolved_gRH(dp, dist['gRH'], uniform=True)
64
-
65
- dry_dp = dp / gRH
66
- belong_which_ibin = np.digitize(dry_dp, dp) - 1
67
-
68
- result = {}
69
- for i, (ibin, dn) in enumerate(zip(belong_which_ibin, ndp)):
70
- if dp[ibin] not in result:
71
- result[dp[ibin]] = []
72
- result[dp[ibin]].append(ndp[i])
73
-
74
- dry_ndp = []
75
- for key, val in result.items():
76
- dry_ndp.append(sum(val) / len(val))
77
-
78
- return np.array(dry_ndp)
79
-
80
-
81
- def resolved_gRH(dp, gRH=1.31, uniform=True):
82
- if uniform:
83
- return np.array([gRH] * dp.size)
84
-
85
- else:
86
- lognorm_dist = lambda x, geoMean, geoStd: (gRH / (np.log10(geoStd) * np.sqrt(2 * np.pi))) * np.exp(
87
- -(x - np.log10(geoMean)) ** 2 / (2 * np.log10(geoStd) ** 2))
88
- abc = lognorm_dist(np.log10(dp), 200, 2.0)
89
- return np.where(abc < 1, 1, abc)
90
-
91
-
92
- if __name__ == '__main__':
93
- pass
@@ -1,5 +0,0 @@
1
- from .Chemical import ChemicalProc
2
- from .IMPACT import ImpactProc
3
- from .IMPROVE import ImproveProc
4
- from .Others import OthersProc
5
- from .PSD import ParticleSizeDistProc, ExtinctionDistProc