Source code for FREQCOR_Main

# -*- coding: utf-8 -*-

"""Main routine entry point for running the FREQCOR processing chain."""

# Custom exception class for FREQCOR_Main errors
from FREQCOR_Compute import FREQCOR_Compute
from FREQCOR_Flux import FREQCOR_Flux
from FREQCOR_Read_EP import FREQCOR_Read_EP
from FREQCOR_Read_TOF import FREQCOR_Read_TOF
from FREQCOR_Sel_general import FREQCOR_Sel_general
from FREQCOR_validate import print_validation_results, validate_config
from FREQCOR_write_outputs import FREQCOR_write_outputs
from datetime import datetime
import numpy as np
import os
import pandas as pd

[docs] class FREQCORMainError(Exception): """Exception raised for errors in the FREQCOR_Main function. Attributes: message -- explanation of the error """
[docs] def __init__(self, message): self.message = message super().__init__(self.message)
[docs] def FREQCOR_Main(config): """ Run the full FREQCOR processing chain for one configuration. Parameters ---------- config : configparser.ConfigParser Parsed configuration. Returns ------- LUTcof : dict or None Look-up table of cut-off frequencies, with two-levels sorting. LUTCF_u : dict or None Look-up table of correction factors for unstable conditions, with two-levels sorting. LUTCF_s : dict or None Look-up table of correction factors for stable conditions, with two-levels sorting. Raises ------ FREQCORMainError If an unrecoverable error occurs during processing. """ # %% Validate configuration parameters valid, errors = validate_config(config) print_validation_results(valid, errors) if not valid: print("Configuration validation failed. Please fix the errors and try again.") return None, None, None # %% Variables extraction jsite = config['EC_SETUP']['jsite'] gss = int(config['PROCEDURE_OPTIONS']['gss']) sps = int(config['PROCEDURE_OPTIONS']['sps']) zmeas = float(config['EC_SETUP']['zmeas']) disph = float(config['EC_SETUP']['disph']) rsz = float(config['EC_SETUP']['rsz']) rseast = float(config['EC_SETUP']['rseast']) rsnorth = float(config['EC_SETUP']['rsnorth']) hl_cof_min = float(config['USER_LIMITS']['hl_cof_min']) hl_cof_max = float(config['USER_LIMITS']['hl_cof_max']) fcl_cof_min = float(config['USER_LIMITS']['fcl_cof_min']) fcl_cof_max = float(config['USER_LIMITS']['fcl_cof_max']) lel_cof_min = float(config['USER_LIMITS']['lel_cof_min']) lel_cof_max = float(config['USER_LIMITS']['lel_cof_max']) cospec_abs_limit = float(config['USER_LIMITS']['cospec_abs_limit']) hl_cf_u_min = float(config['USER_LIMITS']['hl_cf_u_min']) hl_cf_s_max = float(config['USER_LIMITS']['hl_cf_s_max']) hl_cf_abs_max = float(config['USER_LIMITS']['hl_cf_abs_max']) FVM = int(config['USER_LIMITS']['fvm']) FF = int(config['USER_LIMITS']['ff']) jtmin_hz = float(config['USER_LIMITS']['jtmin']) jtmax_hz = float(config['USER_LIMITS']['jtmax']) varclim = float(config['USER_LIMITS']['varclim']) tfmin_hz = float(config['USER_LIMITS']['tfmin']) tfmax_hz = float(config['USER_LIMITS']['tfmax']) plot = [ int(config['PROCEDURE_OPTIONS']['plot_hh']), int(config['PROCEDURE_OPTIONS']['plot_main']), int(config['PROCEDURE_OPTIONS']['plot_aux']), int(config['PROCEDURE_OPTIONS']['plot_save']), ] outputpath = config['IO']['output_path'] gas_map = {1: 'co2', 2: 'h2o', 3: 'o3', 4: 'ch4', 5: 'n2o'} gas_tag = gas_map.get(gss, f'gss{gss}') method_tag = 'cosp' if sps == 1 else 'sp' run_dt = datetime.now().strftime('%y%m%dT%H%M') window_tag = 'all' if 'TIME_WINDOW' in config and 'enable_time_window' in config['TIME_WINDOW']: try: if int(config['TIME_WINDOW']['enable_time_window']) == 1: start_d = datetime.strptime(config['TIME_WINDOW']['start_datetime'], '%Y-%m-%d %H:%M') end_d = datetime.strptime(config['TIME_WINDOW']['end_datetime'], '%Y-%m-%d %H:%M') window_tag = f"{start_d.strftime('%y%m%d')}-{end_d.strftime('%y%m%d')}" except Exception: window_tag = 'all' run_tag = f"{jsite}__{gas_tag}__{method_tag}__{window_tag}__{run_dt}" tf_sonic = int(config['PROCEDURE_OPTIONS']['tf_sonic']) tf_peltola = int(config['PROCEDURE_OPTIONS']['tf_peltola']) eq = int(config['PROCEDURE_OPTIONS']['eq']) read_routine = config['IO'].get('read_routine', None) if not read_routine: read_routine = config['PROCEDURE_OPTIONS'].get('read_routine', 'FREQCOR_Read_EP') if gss==2: classnum_first=int(config['PROCEDURE_OPTIONS']['classnumrh']) else: classnum_first=int(config['PROCEDURE_OPTIONS']['classnumwd']) if classnum_first==1: classnum=int(config['PROCEDURE_OPTIONS']['classnumcofws']) classnumCF_u=int(config['PROCEDURE_OPTIONS']['classnumcf_u']) classnumCF_s=int(config['PROCEDURE_OPTIONS']['classnumcf_s']) else: classnum = 4 classnumCF_u = 4 classnumCF_s = 4 print(f"run_tag={run_tag}") print(f"read_routine={read_routine}") # %% Read datas if read_routine == 'FREQCOR_Read_EP': print("\n=== Reading ===") print(" Reading inputs") nspec, Icon_raw, Rcon_raw, Icosp_raw, nfreq, freq, freqn, freqcount, meteo_df, WS, \ WD, Ustar, Zeta, FlagF_H, FlagF_g, FlagVM_w, FlagVM_T, FlagVM_g, flag_wd, \ massman_coef = FREQCOR_Read_EP(config, jsite) elif read_routine == 'FREQCOR_Read_TOF': print("\n=== Reading ===") print(" Reading inputs") nspec, Icon_raw, Rcon_raw, Icosp_raw, nfreq, freq, freqn, meteo_df, WS, WD, \ Ustar, Zeta, FlagF_H, FlagF_g, FlagVM_w, FlagVM_T, FlagVM_g, massman_coef = \ FREQCOR_Read_TOF(config, jsite) freqcount = pd.Series(320 * np.asarray(freq), index=getattr(freq, 'index', None)) flag_wd = pd.Series(np.nan, index=FlagF_H.index) else: raise FREQCORMainError(f"Unknown read_routine: {read_routine}") print(f" Read complete: nspec={nspec}, nfreq={nfreq}") # %% Filter data print("\n=== Filtering ===") print(" Filtering data") Icon_sel, Rcon_sel, Icosp_sel, meteo_df, WS, WD, Ustar, Zeta, FlagF_H, FlagF_g, \ FlagVM_T, FlagVM_w, FlagVM_g = FREQCOR_Sel_general(Icon_raw, Rcon_raw, Icosp_raw, meteo_df, WS, WD, Ustar, Zeta, FlagF_H,FlagF_g,FlagVM_T, FlagVM_w,FlagVM_g, flag_wd, config) # %%% Compute: cut-off frequency and correction factors print("\n=== Computing cof/CF ===") print(" Computing cut-off frequencies and correction factors") LUT_cof, classize, LUTCF_u, matsortCF_u, classizeCF_u, LUTCF_s, matsortCF_s, classizeCF_s=\ FREQCOR_Compute(Icon_sel, Rcon_sel, Icosp_sel, nspec, nfreq, gss, sps, classnum_first, classnum, classnumCF_u, classnumCF_s, WS, WD, Zeta, meteo_df, hl_cof_min, fcl_cof_min, lel_cof_min, hl_cf_u_min, hl_cf_s_max, hl_cof_max, fcl_cof_max, lel_cof_max, hl_cf_abs_max, FlagVM_T, FlagVM_g, FlagVM_w, FlagF_H, FlagF_g, FVM, FF, jtmin_hz, jtmax_hz, varclim, freq, freqn, freqcount, tfmin_hz, tfmax_hz, plot, outputpath, massman_coef, tf_sonic, tf_peltola, cospec_abs_limit, run_tag) # %% Flux correction print("\n=== Flux correction ===") print(" Computing corrected fluxes") FccorL, FccorG, CFHL, CF_L, CF_G = \ FREQCOR_Flux(nspec, sps, gss, meteo_df,WS,WD,Zeta,LUTCF_u, classnumCF_u, LUTCF_s, classnumCF_s, zmeas, disph,rsz, rseast,rsnorth,eq) # %% Write outputs to files print("\n=== Writing outputs ===") print(" Writing outputs") FREQCOR_write_outputs(gss, LUT_cof, LUTCF_u, LUTCF_s, outputpath, meteo_df, FccorL, FccorG, freqn, WS, WD, Zeta, sps, CF_L, CF_G, CFHL, config, run_tag) print("\n=== Done ===") print(f"Summary: run_tag={run_tag}; nspec={nspec}; nfreq={nfreq}") print(f"output_path: {outputpath}") print("Outputs: 4_LUT_cof__run_tag.csv") print("Outputs: 5_LUT_CF__run_tag.csv") print("Outputs: 6_flux_out__run_tag.csv") print("Outputs: 0_run_input__run_tag.ini") return LUT_cof, LUTCF_u, LUTCF_s