diff --git a/.mplstyle b/.mplstyle index 02cc468..b9a1831 100644 --- a/.mplstyle +++ b/.mplstyle @@ -1,9 +1,9 @@ # GENERAL SETTINGS font.size: 10 text.usetex: True -text.latex.preamble: "\usepackage{arev}\usepackage{siunitx}\usepackage{mhchem}\newcommand{\der}[1]{\ensuremath{\textnormal{d}\,{#1}}}" -lines.linewidth: 0.75 # line width in points -lines.linestyle: - # solid line +text.latex.preamble: "\usepackage{arev}\usepackage{accents}\usepackage{siunitx}\usepackage{mhchem}\newcommand{\der}[1]{\ensuremath{\textnormal{d}\,{#1}}}" +lines.linewidth: 1.00 # line width in points +lines.linestyle: - # solid line # AXES AND BOX axes.facecolor: white # axes background color @@ -64,7 +64,6 @@ ytick.alignment: center_baseline # alignment of yticks # TITLE axes.titlelocation: left -axes.titlesize: large # font size of the axes title axes.titleweight: normal # font weight of title # LEGEND diff --git a/defs/Helper.py b/defs/Helper.py new file mode 100644 index 0000000..fde9ba4 --- /dev/null +++ b/defs/Helper.py @@ -0,0 +1,30 @@ +import pandas as pd + +class DATFile(): + + + def __init__(self, file, data_name = 'value'): + + self.file = file + self.data_name = data_name + self.data = None + + def read(self): + + dates = [] + times = [] + values = [] + + with open(self.file, 'r', errors='ignore') as f: + for i, line in enumerate(f.readlines()): + + if i >= 3: + + items = line.split() + dates.append(items[0]) + times.append(items[1]) + values.append(float(items[2])) + + 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 diff --git a/defs/OFR.py b/defs/OFR.py index 92cd201..3351885 100644 --- a/defs/OFR.py +++ b/defs/OFR.py @@ -149,6 +149,8 @@ class OFRSample: number: int The sample number + start_time: pd.datetime + The starting time of the sample recording sourcefile: str The path to the data file headlines: int @@ -177,8 +179,17 @@ class OFRSample: midpoints = [] counts = [] + date_line = None + time_line = None + with open(self.source_file, 'r', errors = 'ignore') as file: for i, line in enumerate(file.readlines()): + if i == 18: + date_line = line + continue + if i == 19: + time_line = line + continue if i == 20: data_flag = True continue @@ -212,6 +223,16 @@ class OFRSample: self.sampling_time = self.scan_up_time + self.scan_down_time data_lines.pop(-1) + # obtaining the starting time + raw_date_items = date_line.split(self.__data_separator) + raw_time_items = time_line.split(self.__data_separator) + del raw_date_items[0] + del raw_time_items[0] + + raw_date = raw_date_items[(self.sample_number - 1) * 2].splitlines()[0] + raw_time = raw_time_items[(self.sample_number - 1) * 2].splitlines()[0] + self.start_time = pd.to_datetime(raw_date + ' ' + raw_time, format=r'%m/%d/%y %H:%M:%S') + for line in data_lines: items = line.split(self.__data_separator) local_diameter_midpoint = float(items[0]) @@ -227,7 +248,6 @@ class OFRSample: self.lower_diameter_bound = self.lower_diameter_bound * 1E-9 self.upper_diameter_bound = self.upper_diameter_bound * 1E-9 - # Input data from file is in (nm) return [np.array(midpoints), np.array(counts)] @@ -239,6 +259,7 @@ class OFRSample: # Sample information self.source_file = source_file # where the data is stored, i.e. "C:/Users/tschan_d/..." self.sample_number = sample_number # sample number, i.e. 33 + self.start_time = None self.__data_separator = data_separator self.scan_up_time = None # Time to scan up the voltage range in DMA, in (s) @@ -280,7 +301,7 @@ class OFRAnalyser: ---------- data_source_file: str A string pointing to the data source file - flows_source_file: str + flow_source_file: str A string pointing to the flow data source file sample_number_list: [int] A list of integers indicating which samples to consider @@ -290,11 +311,11 @@ class OFRAnalyser: A dataframe containing all the data """ - def __init__(self, data_source_file, flows_source_file, sample_number_list, data_separator = ',') -> None: + def __init__(self, data_source_file, flow_source_file, smpl_number_list, data_separator = ',') -> None: self.data_source_file = data_source_file - self.flows_source_file = flows_source_file - self.sample_number_list = sample_number_list + self.flow_source_file = flow_source_file + self.smpl_number_list = smpl_number_list self.__data_separator = data_separator with open(self.data_source_file, 'r', errors = 'ignore') as file: @@ -308,11 +329,11 @@ class OFRAnalyser: self.max_samples = max_sample_number self.data = None - def __sample_number_list_check(self, sample_number_list) -> None: + def __smpl_number_list_check(self, smpl_number_list) -> None: # TODO: implement a similar check for the flow data file! - last_sample = sample_number_list[-1] + last_sample = smpl_number_list[-1] if last_sample > self.max_samples: raise IndexError("The last sample(s) are out of range of the number of recorded samples") @@ -329,32 +350,29 @@ class OFRAnalyser: # TODO make this work also for a specific sample! # TODO convert everything is SI units, making live a lot easier... - self.__sample_number_list_check(self.sample_number_list) + self.__smpl_number_list_check(self.smpl_number_list) + # Quantities of interest: numbers_of_sample = [] # (-) + starting_times = [] # (timestamp) + sampling_times = [] # (s) number_concentrations = [] # (#/m^3) length_concentrations = [] # (m/m^3) surface_concentrations = [] # (m^2/m^3) volume_concentrations = [] # (m^3/m^3) aerosol_densities = [] # (kg/m^3) mass_concentrations = [] # (kg/m^3) - starting_times = [] # (s) - stopping_times = [] # (s) - sampling_times = [] # (s) - dry_air_flows = [] # (m^3/s) - species_flows = [] # (m^3/s) - ozone_flows = [] # (m^3/s) - total_flows = None # (m^3/s) - cpc_flows = [] # (m^3/s) + filter_flows = [] # (m^3/s) mass_production_rate = None # (kg/s) mass_produced = None # (kg) cumulative_mass_produced = None # (kg) - - for sample_number in self.sample_number_list: + for sample_number in self.smpl_number_list: sample = OFRSample(source_file=self.data_source_file, sample_number = sample_number, data_separator = self.__data_separator) numbers_of_sample.append(sample_number) + starting_times.append(sample.start_time) + number_concentrations.append(sample.Count.integrate()) length_concentrations.append(sample.Length.integrate()) surface_concentrations.append(sample.Surface.integrate()) @@ -362,14 +380,11 @@ class OFRAnalyser: aerosol_densities.append(sample.SOA_density) mass_concentrations.append(sample.Mass.integrate()) - starting_times.append((sample_number - 1) * sample.sampling_time) - stopping_times.append(sample_number * sample.sampling_time) - sampling_times.append(sample.sampling_time) out_flag = False - with open(self.flows_source_file, 'r', errors='ignore') as file: + with open(self.flow_source_file, 'r', errors='ignore') as file: j = 1 @@ -389,11 +404,8 @@ class OFRAnalyser: while i < int(items[-1]): - if j in self.sample_number_list: - dry_air_flows.append(float(items[0]) * 6E-4) - species_flows.append(float(items[1]) * 6E-4) - ozone_flows.append(float(items[2]) * 6E-4) - cpc_flows.append(float(items[3]) * 6E-4) + if j in self.smpl_number_list: + filter_flows.append(float(items[0]) * 6E-4) i += 1 j += 1 @@ -401,26 +413,20 @@ class OFRAnalyser: else: continue - total_flows = [(flow_1 + flow_2 + flow_3) for flow_1, flow_2, flow_3 in zip(dry_air_flows, species_flows, ozone_flows)] - mass_production_rate = [(mass * flow) for mass, flow in zip(mass_concentrations, total_flows)] + mass_production_rate = [(mass * flow) for mass, flow in zip(mass_concentrations, filter_flows)] mass_produced = [(mpr * duration) for mpr, duration in zip(mass_production_rate, sampling_times)] cumulative_mass_produced = np.cumsum(np.array(mass_produced)).tolist() self.data = pd.DataFrame({'Sample (-)': numbers_of_sample, + 'Starting Time (-)': starting_times, + 'Sampling Time (s)': sampling_times, 'Number (#/m^3)': number_concentrations, 'Length (m/m^3)': length_concentrations, 'Surface (m^2/m^3)': surface_concentrations, 'Volume (m^3/m^3)': volume_concentrations, 'Density (kg/m^3)': aerosol_densities, 'Mass (kg/m^3)': mass_concentrations, - 'Start (s)': starting_times, - 'Stop (s)': stopping_times, - 'Duration (s)': sampling_times, - 'Dry Air Flow (m^3/s)': dry_air_flows, - 'Species Flow (m^3/s)': species_flows, - 'Ozone Flow (m^3/s)': ozone_flows, - 'Total Flow (m^3/s)': total_flows, - 'CPC Flow (m^3/s)': cpc_flows, + 'Filter Flow (m^3/s)': filter_flows, 'Mass Production Rate (kg/s)': mass_production_rate, 'Mass Produced (kg)': mass_produced, 'Cumulative Mass Produced (kg)': cumulative_mass_produced}) diff --git a/defs/LabDevices.py b/defs/OzoneAnalyser.py similarity index 100% rename from defs/LabDevices.py rename to defs/OzoneAnalyser.py diff --git a/main.py b/main.py index 1b39ff9..d267817 100644 --- a/main.py +++ b/main.py @@ -1,14 +1,11 @@ # Imports from defs.OFR import OFRSample, OFRAnalyser -import numpy as np from pathlib import Path -# Path to the source file -source_file = Path(r"Z:\People\David_Tschan\data\OFR_SMPS\2026\processed\07_July\20260709\20260709_aPinene_SOA_full.txt") -flows_source_file = Path(r"Z:\People\David_Tschan\data\OFR_SMPS\2026\processed\07_July\20260709\20260709_aPinene_SOA_flows.txt") +# Linking to the data -# Code to debug -sample_number_list = range(1, 609) -analyser = OFRAnalyser(data_source_file=source_file, flows_source_file=flows_source_file, sample_number_list=sample_number_list, data_separator=',') -analyser.GetData() -print(analyser.data) +# Specify the source file paths to for SMPS data +smps_analyser = OFRAnalyser(data_source_file = Path(r"Z:\Data\SMPS\processed\2026\09_September\20260902\20260902_aPinene_SOA.txt"), + flow_source_file = Path(r"Z:\People\David_Tschan\OFR_SMPS\flow_files\20260902_SET02_EXP13_flow_file.txt"), + smpl_number_list = range(1, 60)) +smps_analyser.GetData()