Enabling OFR Analyser class to include timestamp of sample, commencing Helper file with some functions and classes that can be used to handle data formats delivered by several instruments.

This commit is contained in:
tschan_d committed 2026-09-05 23:20:38 +02:00
1 parent 1ab5362d2b
commit f66e1d50e5
5 files changed
+82 -50

No files matched your search

+3 -4
View File
@@ -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
+30
View File
@@ -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})
+43 -37
View File
@@ -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})
File renamed without changes.
+6 -9
View File
@@ -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()