Debugging the OFRAnalyser, it is now completely functional. Remaining to-do's: Check transformations, implement median, mode, mean and variance methods of Distribution class, implement sample number list check for flow file. The two to-do's in the GetData method of the OFRAnalyser can be neglected as they are already fulfilled.

This commit is contained in:
tschan_d committed 2026-07-08 22:10:31 +02:00
1 parent 76b0129b6f
commit fdc30aeb36
4 files changed
+107 -190

No files matched your search

+1 -1
View File
@@ -52,7 +52,7 @@
"editor.suggestOnTriggerCharacters": false,
"window.zoomLevel": -1,
"editor.wordWrap": "on",
"editor.rulers": [140],
"editor.rulers": [145],
"notebook.editorOptionsCustomizations": {"editor.rulers":[]},
"editor.fontSize": 16,
"jupiter.notebookFileRoot": "${workspaceFolder}/"
+92 -178
View File
@@ -24,30 +24,47 @@ class Distribution():
warnings.warn("Already in desired format. Transformation has no effect.")
return
# TODO: check the transformations!
# TODO: check the transformations!
# TODO: check the order of the transformation (first variable, then function, or vice versa) to avoid having a mess!
match to:
case 'linear':
if self.__form == 'ln':
self.var = np.exp(self.var)
self.fct = np.divide(self.fct, self.var)
self.__form = 'linear'
elif self.__form == 'log10':
self.var = np.power(10, self.var)
self.fct = np.divide(self.fct, self.var * np.log(10))
self.__form = 'linear'
case 'ln':
if self.__form == 'linear':
self.var = np.log(self.var)
self.fct = np.multiply(self.var, self.fct)
self.__form = 'ln'
elif self.__form == 'log10':
self.var = np.log(np.power(10, self.var))
self.fct = np.divide(self.fct, np.log(10))
self.__form = 'ln'
case 'log10':
if self.__form == 'linear':
self.var = np.log10(self.var)
self.fct = np.multiply(self.var, self.fct * np.log(10))
self.var = np.log10(self.var)
self.__form = 'log10'
elif self.__form == 'ln':
self.var = np.log10(np.exp(self.var))
self.fct = np.multiply(self.fct, np.log(10))
self.__form = 'log10'
def update_units(self, new_var_units, new_fct_units):
old_from = self.__form
self.transform(to='linear')
self.var = self.var * new_var_units
self.fct = self.fct * new_fct_units
self.transform(to=old_from)
def integrate(self) -> np.float64:
return np.trapezoid(y = self.fct, x = self.var)
@@ -154,6 +171,12 @@ class OFRSample:
else:
continue
self.aerosol_flow = self.aerosol_flow * 6E-4
self.SOA_density = self.SOA_density * 1E3
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)]
@@ -178,21 +201,21 @@ class OFRSample:
# Distributions of the relevant quantities (all relative to nanometer)
data = self.__get_data()
self.Count = Distribution(variable_array = np.log10(data[0]),
function_array = data[1],
form='log10')
self.Length = Distribution(variable_array = np.log10(data[0]),
function_array = data[0] * data[1],
form = 'log10')
self.Surface = Distribution(variable_array = np.log(data[0]),
function_array = ((math.pi) * data[0] ** 2) * data[1],
form = 'log10')
self.Volume = Distribution(variable_array = np.log10(data[0]),
function_array = ((math.pi / 6) * data[0] ** 3) * data[1],
form = 'log10')
self.Mass = Distribution(variable_array = np.log10(data[0]),
function_array = (self.SOA_density * (math.pi / 6) * data[0] ** 3) * data[1],
form='log10')
# Getting Distribution objects
self.Count = Distribution(variable_array = np.log10(data[0]), function_array = data[1], form='log10')
self.Count.update_units(new_fct_units=1E15, new_var_units=1E-9)
self.Length = Distribution(variable_array = np.log10(data[0]), function_array = data[0]*data[1], form = 'log10')
self.Length.update_units(new_fct_units=1E6, new_var_units=1E-9)
self.Surface = Distribution(variable_array = np.log(data[0]), function_array = ((math.pi)* data[0] ** 2) * data[1], form = 'log10')
self.Surface.update_units(new_fct_units=1E-3, new_var_units=1E-9)
self.Volume = Distribution(variable_array = np.log10(data[0]),function_array = ((math.pi / 6) * data[0] ** 3) * data[1], form = 'log10')
self.Volume.update_units(new_fct_units=1E-12, new_var_units=1E-9)
self.Mass = Distribution(variable_array = self.Volume.var, function_array = self.Volume.fct * self.SOA_density, form='log10')
class OFRAnalyser:
@@ -225,48 +248,53 @@ class OFRAnalyser:
def GetData(self) -> None:
# 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)
numbers_of_sample = []
number_concentrations = []
length_concentrations = []
surface_concentrations = []
volume_concentrations = []
aerosol_densities = []
mass_concentrations = []
starting_times = []
stopping_times = []
sampling_times = []
dry_air_flows = []
species_flows = []
ozone_flows = []
total_flows = None
cpc_flows = []
numbers_of_sample = [] # (-)
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)
mass_production_rate = None # (kg/s)
mass_produced = None # (kg)
cumulative_mass_produced = None # (kg)
for sample_number in self.sample_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)
number_concentrations.append(sample.Count.integrate())
length_concentrations.append(sample.Length.integrate() / 1E6)
surface_concentrations.append(sample.Surface.integrate() / 1E12)
volume_concentrations.append(sample.Volume.integrate() / 1E9)
length_concentrations.append(sample.Length.integrate())
surface_concentrations.append(sample.Surface.integrate())
volume_concentrations.append(sample.Volume.integrate())
aerosol_densities.append(sample.SOA_density)
mass_concentrations.append(sample.Mass.integrate() / 1E12)
mass_concentrations.append(sample.Mass.integrate())
if sample_number == 1:
starting_times.append(0.00)
stopping_times.append(sample.sampling_time)
elif sample_number >= 1:
starting_times.append(stopping_times[-1])
stopping_times.append(stopping_times[-1] + sample.sampling_time)
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:
j = 1
for line_counter, line in enumerate(file.readlines()):
if out_flag == True:
@@ -283,156 +311,42 @@ class OFRAnalyser:
while i < int(items[-1]):
dry_air_flows.append(float(items[0]))
species_flows.append(float(items[1]))
ozone_flows.append(float(items[2]))
cpc_flows.append(float(items[3]))
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)
i += 1
j += 1
else:
continue
total_flows = [(flow_1 + flow_2 + flow_3) * 1E3 for flow_1, flow_2, flow_3 in zip(dry_air_flows, species_flows, ozone_flows)]
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_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,
'Number (#/cm^3)': number_concentrations,
'Length (mm/cm^3)': length_concentrations,
'Surface (mm^2/cm^3)': surface_concentrations,
'Volume (μm^3/cm^3)': volume_concentrations,
'Density (pg/μm^3)': aerosol_densities,
'Mass (mg/cm^3)': mass_concentrations,
'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 (L/min)': dry_air_flows,
'Species Flow (L/min)': species_flows,
'Ozone Flow (L/min)': ozone_flows,
'Total Flow (cm^3/s)': total_flows,
'CPC Flow (L/min)': cpc_flows,
'Mass Production Rate (mg/s)': mass_production_rate,
'Mass Produced (mg)': mass_produced,
'Cumulative Mass Produced (mg)': cumulative_mass_produced})
'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,
'Mass Production Rate (kg/s)': mass_production_rate,
'Mass Produced (kg)': mass_produced,
'Cumulative Mass Produced (kg)': cumulative_mass_produced})
def GetPlottableData(self, which_data = 'Mass'):
times = []
output_list = []
match which_data:
case 'Number':
key = 'Number (#/cm^3)'
case 'Length':
key = 'Length (mm/cm^3)'
case 'Surface':
key = 'Surface (mm^2/cm^3)'
case 'Volume':
key = 'Volume (μm^3/cm^3)'
case 'Mass':
key = 'Mass (mg/cm^3)'
times.append(0.00)
output_list.append(0.00)
for i, row in self.data.iterrows():
times.append(row['Time (s)'])
times.append(times[-1])
output_list.append(output_list[-1])
output_list.append(row[key])
if i == self.max_samples - 1:
times.append(row['Time (s)'])
times.append(times[-1])
output_list.append(output_list[-1])
output_list.append(0.00)
return times, output_list
def GetFlowData(self, flow_source_file):
# For a step diagram
self.flow_source_file = flow_source_file
dry_air_flows = []
species_flows = []
ozone_flows = []
cnc_flows = []
starts = []
stops = []
smpls = []
out_flag = False
with open(self.flow_source_file, 'r', errors='ignore') as file:
i = 0
for line_counter, line in enumerate(file.readlines()):
if out_flag == True:
continue
if line_counter == 6:
items = line.split()
dry_air_flows.append(float(items[0]))
species_flows.append(float(items[1]))
ozone_flows.append(float(items[2]))
cnc_flows.append(float(items[-1]))
starts.append(0)
stops.append(float(items[3]) * float(items[4]))
smpls.append(float(items[4]))
i += 1
elif line_counter > 6 and not line.startswith('-'):
items = line.split()
dry_air_flows.append(float(items[0]))
species_flows.append(float(items[1]))
ozone_flows.append(float(items[2]))
cnc_flows.append(float(items[-1]))
starts.append(stops[i-1])
stops.append(stops[i-1] + float(items[3]) * float(items[4]))
smpls.append(float(items[4]))
i += 1
if line_counter > 6 and line.startswith('-'):
out_flag = True
else:
continue
self.flow_data = pd.DataFrame({'Dry Air (L/min)': dry_air_flows, 'Species (L/min)': species_flows, 'Ozone (L/min)': ozone_flows, 'Start (s)': starts, 'Stop (s)': stops, 'CNC Flow (L/min)': cnc_flows, 'Samples': smpls})
def GetPlottableFlowData(self, which_flow_rate='Species'):
times = []
output_list = []
match which_flow_rate:
case 'Species':
key = 'Species (L/min)'
case 'Ozone':
key = 'Ozone (L/min)'
case 'Dry Air':
key = 'Dry Air (L/min)'
for _, row in self.flow_data.iterrows():
times.append(row['Start (s)'])
times.append(row['Stop (s)'])
# Now append relevant data twice so that the data can be plotted correctly
i = 0
while i < 2:
output_list.append(row[key])
i += 1
return times, output_list
def GetMassProductionRates(self):
pass
### ----------------------------------------------------------------------------------------------------------------------------------- ###
###
+2 -6
View File
@@ -1,17 +1,13 @@
# Imports
#%%
from defs.computing import OFRSample, OFRAnalyser
import numpy as np
from pathlib import Path
# Path to the source file
source_file = Path(r"C:\Users\tschan_d\Documents\data_local\20260612_SET02_EXP01_full.txt")
flows_source_file = Path(r"C:\Users\tschan_d\Documents\data_local\20260612_SET02_EXP01_full_flows.txt")
analyser = OFRAnalyser(data_source_file=source_file, flows_source_file=flows_source_file, sample_number_list=range(1, 157))
# Code to debug
analyser = OFRAnalyser(data_source_file=source_file, flows_source_file=flows_source_file, sample_number_list=[33, 35])
analyser.GetData()
print(analyser.data)
# %%
@@ -10,7 +10,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"id": "8769766b",
"metadata": {},
"outputs": [],
@@ -85,16 +85,15 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 2,
"id": "8db19b57",
"metadata": {},
"outputs": [],
"source": [
"sample_number_list = [int(i) for i in range(1, 157)]\n",
"\n",
"analyser = OFRAnalyser(data_source_file = source_file, flows_source_file = flows_source_file, sample_number_list = sample_number_list)\n",
"analyser = OFRAnalyser(data_source_file = source_file, flows_source_file = flows_source_file, sample_number_list = [33, 35, 37])\n",
"analyser.GetData()\n",
"\n",
"analyser.data.to_csv(path_or_buf=r\"C:\\Users\\tschan_d\\Documents\\data_local\\data.csv\", index=False)"
]
},
@@ -129,7 +128,7 @@
"\n",
"ax.set_xlim([1, 156])\n",
"ax.set_ylim([0, 1.2E6])\n",
"ax_2.set_ylim([0, 15])\n",
"#ax_2.set_ylim([0, 15])\n",
"\n",
"lines_1, labels_1 = ax.get_legend_handles_labels()\n",
"lines_2, labels_2 = ax_2.get_legend_handles_labels()\n",
@@ -220,6 +219,14 @@
"\n",
"plt.savefig(\"20260723_OFR_Analysis_01_Experiment_Overview_bottom.pdf\", format='pdf', dpi=400)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c6110f8c",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {