From 75d7f6f287949be625d222c81562d2f4ece1ae68 Mon Sep 17 00:00:00 2001 From: Tschan Date: Mon, 7 Sep 2026 19:14:04 +0200 Subject: [PATCH] Updating the Helper.py file and integrating the averager function into the OzonMonitor submodule, which now includes a class OzoneAnalyser. --- .vscode/launch.json | 2 +- defs/Helper.py | 41 +++++++++++++++-- defs/OzoneAnalyser.py | 20 --------- defs/OzoneMonitor.py | 96 ++++++++++++++++++++++++++++++++++++++++ defs/{OFR.py => SMPS.py} | 18 +++++++- main.py | 63 +++++--------------------- 6 files changed, 161 insertions(+), 79 deletions(-) delete mode 100644 defs/OzoneAnalyser.py create mode 100644 defs/OzoneMonitor.py rename defs/{OFR.py => SMPS.py} (95%) diff --git a/.vscode/launch.json b/.vscode/launch.json index 805f644..eef90c4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -3,7 +3,7 @@ "configurations": [ { - "name": "Python Debugger: Current File", + "name": "Debug Current File", "type": "debugpy", "request": "launch", "program": "${file}", diff --git a/defs/Helper.py b/defs/Helper.py index 51c8f51..f111fc3 100644 --- a/defs/Helper.py +++ b/defs/Helper.py @@ -1,9 +1,40 @@ +# Author: David Tschan +# Institute: Paul-Scherrer Institute, Laboratory for Atmospheric Chemistry, Multiphase Chemistry Group, +# Forschungsstrasse 111, 5232 Villigen PSI, Switzerland +# Role: Master Student +# License: n/a +# Date: 2026 + +### ----------------------------------------------------------------------------------------------------------------------------------- ### +### SUBMODULE DOCSTRING + +"""The docstring for this submodule +""" + +### ----------------------------------------------------------------------------------------------------------------------------------- ### +### IMPORTS AND CONFIGURATIONS + import pandas as pd +### ----------------------------------------------------------------------------------------------------------------------------------- ### +### Helper.py + class DATFile(): - # hekko - + """The docstring for this class + + Description: Bla bla bla + + Attributes + ---------- + word: str + A word + + Methods + ------- + __init__: HelloWorld + The class constructor + """ def __init__(self, file, data_name = 'value'): @@ -29,4 +60,8 @@ class DATFile(): pre_datetimes = [date + ' ' + time for date, time in zip(dates, times)] datetimes = pd.to_datetime(pre_datetimes, format=r'%d.%m.%Y %H:%M:%S') - self.data = pd.DataFrame({'timestamp': datetimes, self.data_name: values}) \ No newline at end of file + self.data = pd.DataFrame({'timestamp': datetimes, self.data_name: values}) + + def exclude_outliers(): + + pass \ No newline at end of file diff --git a/defs/OzoneAnalyser.py b/defs/OzoneAnalyser.py deleted file mode 100644 index 04bf80b..0000000 --- a/defs/OzoneAnalyser.py +++ /dev/null @@ -1,20 +0,0 @@ -class HelloWorld(): - - """The docstring for this class - - Description: Bla bla bla - - Attributes - ---------- - word: str - A word - - Methods - ------- - __init__: HelloWorld - The class constructor - """ - - def __init__(self, word = "Hellow"): - - self.word = word \ No newline at end of file diff --git a/defs/OzoneMonitor.py b/defs/OzoneMonitor.py new file mode 100644 index 0000000..cf399c6 --- /dev/null +++ b/defs/OzoneMonitor.py @@ -0,0 +1,96 @@ +# Author: David Tschan +# Institute: Paul-Scherrer Institute, Laboratory for Atmospheric Chemistry, Multiphase Chemistry Group, +# Forschungsstrasse 111, 5232 Villigen PSI, Switzerland +# Role: Master Student +# License: n/a +# Date: 2026 + +### ----------------------------------------------------------------------------------------------------------------------------------- ### +### SUBMODULE DOCSTRING + +"""The docstring for this submodule +""" + +### ----------------------------------------------------------------------------------------------------------------------------------- ### +### IMPORTS AND CONFIGURATIONS + +import numpy as np +import pandas as pd +from defs.Helper import DATFile +from defs.SMPS import OFRAnalyser + +### ----------------------------------------------------------------------------------------------------------------------------------- ### +### OzoneAnalyser.py + +class OzoneAnalyser(): + + """The docstring for this class + + Description: Bla bla bla + + Attributes + ---------- + word: str + A word + + Methods + ------- + __init__: HelloWorld + The class constructor + """ + + def __init__(self, file: DATFile, on_time_interval): + + self.file = file + self.on_time_interval = on_time_interval + self.averaged_data = None + + def GetData(self): + + self.file.read() + + def AverageWithSMPS(self, smps_analyser: OFRAnalyser, cut_edges: int, subtract_baseline = ['yes', 5]): + + avg_ozone = [] + mprr_data = [] + + local_smps_data = smps_analyser.data[(smps_analyser.data['Starting Time (-)'] >= smps_analyser.on_time_interval[0]) & + (smps_analyser.data['Starting Time (-)'] <= smps_analyser.on_time_interval[1])].reset_index() + + baseline = None + + for index, data in local_smps_data.iterrows(): + + start = data['Starting Time (-)'] + + if index == 0: + + if subtract_baseline[0] == 'yes': + baseline_start = start - pd.Timedelta(minutes = int(subtract_baseline[1])) + baseline_ozone_data = self.file.data[(self.file.data['timestamp'] >= baseline_start) & + (self.file.data['timestamp'] <= start)] + baseline = np.average(baseline_ozone_data['ozone']) + + if index in list(range(cut_edges)): + continue + + stop = start + pd.Timedelta(seconds = data['Sampling Time (s)']) + + mprr_data.append(data['Mass Production Rate (kg/s)']) + + local_ozone_data = self.file.data[(self.file.data['timestamp'] >= start) & + (self.file.data['timestamp'] <= stop)] + avg_ozone.append(np.average(local_ozone_data['ozone'])) + + for sub_index in list(range(cut_edges)): + del avg_ozone[-(sub_index + 1)] + del mprr_data[-(sub_index + 1)] + + out_mprr_data = np.array(mprr_data) + + if subtract_baseline[0] == 'yes': + out_avg_ozone = np.array([baseline - value for value in avg_ozone]) + elif subtract_baseline[0] != 'yes': + out_avg_ozone = np.array([value for value in avg_ozone]) + + self.averaged_data = pd.DataFrame({'Ozone (ppb)': out_avg_ozone, 'Mass Production Rate (kg/s)': out_mprr_data}) \ No newline at end of file diff --git a/defs/OFR.py b/defs/SMPS.py similarity index 95% rename from defs/OFR.py rename to defs/SMPS.py index 3351885..4fdcf4d 100644 --- a/defs/OFR.py +++ b/defs/SMPS.py @@ -1,3 +1,13 @@ +# Author: David Tschan +# Institute: Paul-Scherrer Institute, Laboratory for Atmospheric Chemistry, Multiphase Chemistry Group, +# Forschungsstrasse 111, 5232 Villigen PSI, Switzerland +# Role: Master Student +# License: n/a +# Date: 2026 + +### ----------------------------------------------------------------------------------------------------------------------------------- ### +### SUBMODULE DOCSTRING + """The docstring for this submodule """ @@ -10,7 +20,7 @@ import math import warnings ### ----------------------------------------------------------------------------------------------------------------------------------- ### -### OFR ANALYSIS +### OFR.py class Distribution(): @@ -305,19 +315,23 @@ class OFRAnalyser: A string pointing to the flow data source file sample_number_list: [int] A list of integers indicating which samples to consider + on_time_interval: [pd.Timestamp] + A list with two elements containing the pd.Timestamp objects when the VOC flow was switched on and off, respectively. max_samples: int The highest sample available in the data data: pandas.DataFrame A dataframe containing all the data """ - def __init__(self, data_source_file, flow_source_file, smpl_number_list, data_separator = ',') -> None: + def __init__(self, data_source_file, flow_source_file, smpl_number_list, on_time_interval, data_separator = ',') -> None: self.data_source_file = data_source_file self.flow_source_file = flow_source_file self.smpl_number_list = smpl_number_list + self.on_time_interval = on_time_interval self.__data_separator = data_separator + with open(self.data_source_file, 'r', errors = 'ignore') as file: for i, line in enumerate(file.readlines()): if i == 17: diff --git a/main.py b/main.py index 1584d6e..0cd0331 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,6 @@ # Importing custom code -from defs.OFR import OFRSample, OFRAnalyser +from defs.SMPS import OFRAnalyser +from defs.OzoneMonitor import OzoneAnalyser from defs.Helper import DATFile # Importing required libraries @@ -7,62 +8,18 @@ import numpy as np from pathlib import Path import pandas as pd -def ozone_averager(ozon_analyser: DATFile, smps_analyser: OFRAnalyser, voc_on_times, cut_edges = True, subtract_5min_baseline = True) -> np.ndarray: - - average_ozon_values = [] - mass_production_rates = [] - - local_smps_data = smps_analyser.data[(smps_analyser.data['Starting Time (-)'] >= voc_on_times[0]) & (smps_analyser.data['Starting Time (-)'] >= voc_on_times[0])].reset_index() - - baseline = None - - for index, data in local_smps_data.iterrows(): - - start = data['Starting Time (-)'] - - if index == 0: - - if subtract_5min_baseline == True: - baseline_start = start - pd.Timedelta(minutes=5) - baseline_ozone_data = ozon_analyser.data[(ozon_analyser.data['timestamp'] >= baseline_start) & (ozon_analyser.data['timestamp'] <= start)] - baseline = np.average(baseline_ozone_data['ozone']) - - if cut_edges == True: - continue - - stop = start + pd.Timedelta(seconds = data['Sampling Time (s)']) - - mass_production_rates.append(data['Mass Production Rate (kg/s)']) - - local_ozone_data = ozon_analyser.data[(ozon_analyser.data['timestamp'] >= start) & (ozon_analyser.data['timestamp'] <= stop)] - average_ozon_values.append(np.average(local_ozone_data['ozone'])) - - if cut_edges == True: - del average_ozon_values[-1] - del mass_production_rates[-1] - - if subtract_5min_baseline == True: - output_average_ozone_values = [baseline - value for value in average_ozon_values] - elif subtract_5min_baseline == False: - output_average_ozone_values = [value for value in average_ozon_values] - - return output_average_ozone_values, mass_production_rates - -# Linking to the data - # Specify the source file paths to for SMPS data for the different filters used smps_analyser = OFRAnalyser(data_source_file = Path(r"Z:\Data\SMPS\processed\2026\09_September\20260903\20260903_aPinene_SOA.txt"), - flow_source_file = Path(r"Z:\People\David_Tschan\OFR_SMPS\flow_files\20260903_SET02_EXP14_flow_file.txt"), - smpl_number_list = range(1, 60)) + flow_source_file = Path(r"Z:\People\David_Tschan\OFR_SMPS\flow_files\20260903_SET02_EXP14_flow_file.txt"), + smpl_number_list = range(1, 60), + on_time_interval = [pd.Timestamp('2026-09-03 15:20:00'), pd.Timestamp('2026-09-03 16:20:00')]) -# Specify the source file paths to the API O3 monitor data for the different filters used -ozon_analyser =DATFile(file = Path(r"Z:\Data\API_O3\2026\09_September\2026-09-03_14.23.44_PC14750_API_O3.dat"), data_name = 'ozone') - -voc_on_times = [pd.Timestamp('2026-09-03 15:20:00'), pd.Timestamp('2026-09-03 16:20:00')] +ozon_analyser = OzoneAnalyser(file = DATFile(file = Path(r"Z:\Data\API_O3\2026\09_September\2026-09-03_14.23.44_PC14750_API_O3.dat"), data_name = 'ozone'), + on_time_interval=[pd.Timestamp('2026-09-03 14:35:00'), pd.Timestamp('2026-09-03 16:50:00')]) smps_analyser.GetData() -ozon_analyser.read() +ozon_analyser.GetData() -ozone_average, mass_production_rates = ozone_averager(ozon_analyser=ozon_analyser, smps_analyser=smps_analyser, voc_on_times = voc_on_times, cut_edges=False, subtract_5min_baseline=True) +ozon_analyser.AverageWithSMPS(smps_analyser = smps_analyser, cut_edges = 2, subtract_baseline = ['yes', 5]) -print('hello') +print(len(ozon_analyser.averaged_data))