diff --git a/aeolis/constants.py b/aeolis/constants.py index eae691bc..dc9fb70e 100644 --- a/aeolis/constants.py +++ b/aeolis/constants.py @@ -24,6 +24,7 @@ ''' +# Question; what are the different purposes of INITIAL_STATE and MODEL_STATE? #: Aeolis model state variables INITIAL_STATE = { @@ -154,18 +155,65 @@ #: AeoLiS model default configuration DEFAULT_CONFIG = { + + # --- Grid files (convention *.grd) --- # + 'xgrid_file' : None, # Filename of ASCII file with x-coordinates of grid cells + 'ygrid_file' : None, # Filename of ASCII file with y-coordinates of grid cells + 'bed_file' : None, # Filename of ASCII file with bed level heights of grid cells + 'ne_file' : None, # Filename of ASCII file with non-erodible layer + 'veg_file' : None, # Filename of ASCII file with initial vegetation density + + # --- Model, grid and time settings --- # + 'tstart' : 0., # [s] Start time of simulation + 'tstop' : 3600., # [s] End time of simulation + 'dt' : 60., # [s] Time step size + 'restart' : None, # [s] Interval for which to write restart files + 'refdate' : '2020-01-01 00:00', # [-] Reference datetime in netCDF output + 'callback' : None, # Reference to callback function (e.g. example/callback.py':callback) + 'wind_convention' : 'nautical', # Convention used for the wind direction in the input files (cartesian or nautical) + 'alfa' : 0, # [deg] Real-world grid cell orientation wrt the North (clockwise) + 'nx' : 0, # [-] Number of grid cells in x-dimension + 'ny' : 0, # [-] Number of grid cells in y-dimension + + # --- Input Timeseries --- # + 'wind_file' : None, # Filename of ASCII file with time series of wind velocity and direction + 'tide_file' : None, # Filename of ASCII file with time series of water levels + 'wave_file' : None, # Filename of ASCII file with time series of wave heights + 'meteo_file' : None, # Filename of ASCII file with time series of meteorlogical conditions + + # --- Boundary conditions --- # + 'boundary_lateral' : 'flux', # Name of lateral boundary conditions (circular, flux or constant) + 'boundary_offshore' : 'flux', # Name of offshore boundary conditions (circular, flux or constant) + 'boundary_onshore' : 'flux', # Name of onshore boundary conditions (circular, flux or constant) + 'offshore_flux' : 1., # [-] Factor to determine offshore boundary flux as a function of Cu (= 1 for saturated, = 0 for noflux) + 'onshore_flux' : 1., # [-] Factor to determine onshore boundary flux as a function of Cu (= 1 for saturated, = 0 for noflux) + 'lateral_flux' : 1., # [-] Factor to determine lateral boundary flux as a function of Cu (= 1 for saturated, = 0 for noflux) + + # --- Sediment fractions and layers --- # + 'grain_size' : [225e-6], # [m] Average grain size of each sediment fraction + 'grain_dist' : [1.], # [-] Initial distribution of sediment fractions + 'nlayers' : 3, # [-] Number of bed layers + 'layer_thickness' : .01, # [m] Thickness of bed layers + + # --- Output (and coupling) settings --- # + 'visualization' : False, # Boolean for visualization of model interpretation before and just after initialization + 'output_times' : 60., # [s] Output interval in seconds of simulation time + 'output_file' : None, # Filename of netCDF4 output file + 'output_types' : None, # Names of statistical parameters to be included in output (avg, sum, var, min or max) + 'output_vars' : ['zb', 'zs', + 'Ct', 'Cu', + 'uw', 'udir', + 'uth', 'mass' + 'pickup', 'w'], # Names of spatial grids to be included in output + 'external_vars' : None, # Names of variables that are overwritten by an external (coupling) model, i.e. CoCoNuT + 'output_sedtrails' : False, # NEW! [T/F] Boolean to see whether additional output for SedTRAILS should be generated + 'nfraction_sedtrails' : 0, # [-] Index of selected fraction for SedTRAILS (0 if only one fraction) + + # --- Process Booleans (True/False) --- # 'process_wind' : True, # Enable the process of wind 'process_transport' : True, # Enable the process of transport 'process_bedupdate' : True, # Enable the process of bed updating 'process_threshold' : True, # Enable the process of threshold - 'th_grainsize' : True, # Enable wind velocity threshold based on grainsize - 'th_bedslope' : False, # Enable wind velocity threshold based on bedslope - 'th_moisture' : False, # Enable wind velocity threshold based on moisture - 'th_drylayer' : False, # Enable threshold based on drying of layer - 'th_humidity' : False, # Enable wind velocity threshold based on humidity - 'th_salt' : False, # Enable wind velocity threshold based on salt - 'th_sheltering' : False, # Enable wind velocity threshold based on sheltering by roughness elements - 'th_nelayer' : False, # Enable wind velocity threshold based on a non-erodible layer 'process_avalanche' : False, # Enable the process of avalanching 'process_shear' : False, # Enable the process of wind shear 'process_tide' : False, # Enable the process of tides @@ -182,56 +230,45 @@ 'process_inertia' : False, # NEW 'process_separation' : False, # Enable the including of separation bubble 'process_vegetation' : False, # Enable the process of vegetation + 'process_vegetation_leeside' : False, # Enable the process of leeside vegetation effects on shear stress 'process_fences' : False, # Enable the process of sand fencing 'process_dune_erosion' : False, # Enable the process of wave-driven dune erosion 'process_seepage_face' : False, # Enable the process of groundwater seepage (NB. only applicable to positive beach slopes) - 'visualization' : False, # Boolean for visualization of model interpretation before and just after initialization - 'output_sedtrails' : False, # NEW! [T/F] Boolean to see whether additional output for SedTRAILS should be generated - 'nfraction_sedtrails' : 0, # [-] Index of selected fraction for SedTRAILS (0 if only one fraction) - 'xgrid_file' : None, # Filename of ASCII file with x-coordinates of grid cells - 'ygrid_file' : None, # Filename of ASCII file with y-coordinates of grid cells - 'bed_file' : None, # Filename of ASCII file with bed level heights of grid cells - 'wind_file' : None, # Filename of ASCII file with time series of wind velocity and direction - 'tide_file' : None, # Filename of ASCII file with time series of water levels - 'wave_file' : None, # Filename of ASCII file with time series of wave heights - 'meteo_file' : None, # Filename of ASCII file with time series of meteorlogical conditions + 'process_bedinteraction' : False, # Enable the process of bed interaction in the advection equation + + # --- Threshold Booleans (True/False) --- # + 'th_grainsize' : True, # Enable wind velocity threshold based on grainsize + 'th_bedslope' : False, # Enable wind velocity threshold based on bedslope + 'th_moisture' : False, # Enable wind velocity threshold based on moisture + 'th_drylayer' : False, # Enable threshold based on drying of layer + 'th_humidity' : False, # Enable wind velocity threshold based on humidity + 'th_salt' : False, # Enable wind velocity threshold based on salt + 'th_sheltering' : False, # Enable wind velocity threshold based on sheltering by roughness elements + 'th_nelayer' : False, # Enable wind velocity threshold based on a non-erodible layer + + # --- Other spatial files / masks --- # 'bedcomp_file' : None, # Filename of ASCII file with initial bed composition 'threshold_file' : None, # Filename of ASCII file with shear velocity threshold 'fence_file' : None, # Filename of ASCII file with sand fence location/height (above the bed) - 'ne_file' : None, # Filename of ASCII file with non-erodible layer - 'veg_file' : None, # Filename of ASCII file with initial vegetation density 'supply_file' : None, # Filename of ASCII file with a manual definition of sediment supply (mainly used in academic cases) 'wave_mask' : None, # Filename of ASCII file with mask for wave height 'tide_mask' : None, # Filename of ASCII file with mask for tidal elevation 'runup_mask' : None, # Filename of ASCII file with mask for run-up 'threshold_mask' : None, # Filename of ASCII file with mask for the shear velocity threshold 'gw_mask' : None, # Filename of ASCII file with mask for the groundwater level - 'vver_mask' : None, #NEWBvW # Filename of ASCII file with mask for the vertical vegetation growth - 'nx' : 0, # [-] Number of grid cells in x-dimension - 'ny' : 0, # [-] Number of grid cells in y-dimension - 'dt' : 60., # [s] Time step size - 'dx' : 1., - 'dy' : 1., + 'vver_mask' : None, # Filename of ASCII file with mask for the vertical vegetation growth + + # --- Nummerical Solver --- # + 'T' : 1., # [s] Adaptation time scale in advection equation 'CFL' : 1., # [-] CFL number to determine time step in explicit scheme 'accfac' : 1., # [-] Numerical acceleration factor 'max_bedlevel_change' : 999., # [m] Maximum bedlevel change after one timestep. Next timestep dt will be modified (use 999. if not used) - 'tstart' : 0., # [s] Start time of simulation - 'tstop' : 3600., # [s] End time of simulation - 'restart' : None, # [s] Interval for which to write restart files - 'dzb_interval' : 86400, # [s] Interval used for calcuation of vegetation growth - 'output_times' : 60., # [s] Output interval in seconds of simulation time - 'output_file' : None, # Filename of netCDF4 output file - 'output_vars' : ['zb', 'zs', - 'Ct', 'Cu', - 'uw', 'udir', - 'uth', 'mass' - 'pickup', 'w'], # Names of spatial grids to be included in output - 'output_types' : [], # Names of statistical parameters to be included in output (avg, sum, var, min or max) - 'external_vars' : [], # Names of variables that are overwritten by an external (coupling) model, i.e. CoCoNuT - 'grain_size' : [225e-6], # [m] Average grain size of each sediment fraction - 'grain_dist' : [1.], # [-] Initial distribution of sediment fractions - 'nlayers' : 3, # [-] Number of bed layers - 'layer_thickness' : .01, # [m] Thickness of bed layers + 'max_error' : 1e-8, # [-] Maximum error at which to quit iterative solution in implicit numerical schemes + 'max_iter' : 1000, # [-] Maximum number of iterations at which to quit iterative solution in implicit numerical schemes + 'solver' : 'steadystate', # Name of the solver (steadystate, euler_backward, euler_forward) + + # --- General physical constants and model parameters --- # + 'method_roughness' : 'constant', # Name of method to compute the roughness height z0, note that here the z0 = k 'g' : 9.81, # [m/s^2] Gravitational constant 'v' : 0.000015, # [m^2/s] Air viscosity 'rhoa' : 1.225, # [kg/m^3] Air density @@ -242,34 +279,41 @@ 'z' : 10., # [m] Measurement height of wind velocity 'h' : None, # [m] Representative height of saltation layer 'k' : 0.001, # [m] Bed roughness + 'kappa' : 0.41, # [-] Von Kármán constant + + # --- Shear / Perturbation / Topographic steering --- # + 'method_shear' : 'fft', # Name of method to compute topographic effects on wind shear stress (fft, quasi2d, duna2d (experimental)) + 'dx' : 1., + 'dy' : 1., 'L' : 100., # [m] Typical length scale of dune feature (perturbation) 'l' : 10., # [m] Inner layer height (perturbation) - 'c_b' : 0.2, # [-] Slope at the leeside of the separation bubble # c = 0.2 according to Durán 2010 (Sauermann 2001: c = 0.25 for 14 degrees) - 'mu_b' : 30, # [deg] Minimum required slope for the start of flow separation + + # --- Flow separation bubble (OLD) --- # 'buffer_width' : 10, # [m] Width of the bufferzone around the rotational grid for wind perturbation 'sep_filter_iterations' : 0, # [-] Number of filtering iterations on the sep-bubble (0 = no filtering) 'zsep_y_filter' : False, # [-] Boolean for turning on/off the filtering of the separation bubble in y-direction + + # --- Sediment transport formulations --- # + 'method_transport' : 'bagnold', # Name of method to compute equilibrium sediment transport rate + 'method_grainspeed' : 'windspeed', # Name of method to assume/compute grainspeed (windspeed, duran, constant) 'Cb' : 1.5, # [-] Constant in bagnold formulation for equilibrium sediment concentration 'Ck' : 2.78, # [-] Constant in kawamura formulation for equilibrium sediment concentration 'Cl' : 6.7, # [-] Constant in lettau formulation for equilibrium sediment concentration 'Cdk' : 5., # [-] Constant in DK formulation for equilibrium sediment concentration - # 'm' : 0.5, # [-] Factor to account for difference between average and maximum shear stress -# 'alpha' : 0.4, # [-] Relation of vertical component of ejection velocity and horizontal velocity difference between impact and ejection - 'kappa' : 0.41, # [-] Von Kármán constant 'sigma' : 4.2, # [-] Ratio between basal area and frontal area of roughness elements 'beta' : 130., # [-] Ratio between drag coefficient of roughness elements and bare surface - 'bi' : 1., # [-] Bed interaction factor - 'T' : 1., # [s] Adaptation time scale in advection equation - 'Tdry' : 3600.*1.5, # [s] Adaptation time scale for soil drying - 'Tsalt' : 3600.*24.*30., # [s] Adaptation time scale for salinitation + 'bi' : 1., # [-] Bed interaction factor for sediment fractions + + # --- Bed update parameters --- # 'Tbedreset' : 86400., # [s] - 'eps' : 1e-3, # [m] Minimum water depth to consider a cell "flooded" - 'gamma' : .5, # [-] Maximum wave height over depth ratio - 'xi' : .3, # [-] Surf similarity parameter - 'facDOD' : .1, # [-] Ratio between depth of disturbance and local wave height - 'csalt' : 35e-3, # [-] Maximum salt concentration in bed surface layer - 'cpair' : 1.0035e-3, # [MJ/kg/oC] Specific heat capacity air + + # --- Moisture parameters --- # + 'method_moist_threshold' : 'belly_johnson', # Name of method to compute wind velocity threshold based on soil moisture content + 'method_moist_process' : 'infiltration', # Name of method to compute soil moisture content(infiltration or surface_moisture) + 'Tdry' : 3600.*1.5, # [s] Adaptation time scale for soil drying + # --- Moisture / Groundwater (Hallin) --- # + 'boundary_gw' : 'no_flow', # Landward groundwater boundary, dGw/dx = 0 (or 'static') 'fc' : 0.11, # [-] Moisture content at field capacity (volumetric) 'w1_5' : 0.02, # [-] Moisture content at wilting point (gravimetric) 'resw_moist' : 0.01, # [-] Residual soil moisture content (volumetric) @@ -292,8 +336,20 @@ 'GW_stat' : 1, # [m] Landward static groundwater boundary (if static boundary is defined) 'max_moist' : 10., # NEWCH # [%] Moisture content (volumetric in percent) above which the threshold shear velocity is set to infinity (no transport, default value Delgado-Fernandez, 2010) 'max_moist' : 10., # [%] Moisture content (volumetric in percent) above which the threshold shear velocity is set to infinity (no transport, default value Delgado-Fernandez, 2010) + + # --- Avalanching parameters --- # 'theta_dyn' : 33., # [degrees] Initial Dynamic angle of repose, critical dynamic slope for avalanching 'theta_stat' : 34., # [degrees] Initial Static angle of repose, critical static slope for avalanching + 'max_iter_ava' : 1000, # [-] Maximum number of iterations at which to quit iterative solution in avalanching calculation + + # --- Hydro and waves --- # + 'eps' : 1e-3, # [m] Minimum water depth to consider a cell "flooded" + 'gamma' : .5, # [-] Maximum wave height over depth ratio + 'xi' : .3, # [-] Surf similarity parameter + 'facDOD' : .1, # [-] Ratio between depth of disturbance and local wave height + + # --- Vegetation (OLD) --- # + 'method_vegetation' : 'duran', # Name of method to compute vegetation: duran (original) or grass (new framework) 'avg_time' : 86400., # [s] Indication of the time period over which the bed level change is averaged for vegetation growth 'gamma_vegshear' : 16., # [-] Roughness factor for the shear stress reduction by vegetation 'hveg_max' : 1., # [m] Max height of vegetation @@ -303,34 +359,6 @@ 'lateral' : 0., # [1/year] Posibility of lateral expension per year 'veg_gamma' : 1., # [-] Constant on influence of sediment burial 'veg_sigma' : 0., # [-] Sigma in gaussian distrubtion of vegetation cover filter - 'sedimentinput' : 0., # [-] Constant boundary sediment influx (only used in solve_pieter) - 'scheme' : 'euler_backward', # Name of numerical scheme (euler_forward, euler_backward or crank_nicolson) - 'solver' : 'trunk', # Name of the solver (trunk, pieter, steadystate,steadystatepieter) - 'boundary_lateral' : 'circular', # Name of lateral boundary conditions (circular, constant ==noflux) - 'boundary_offshore' : 'constant', # Name of offshore boundary conditions (flux, constant, uniform, gradient) - 'boundary_onshore' : 'gradient', # Name of onshore boundary conditions (flux, constant, uniform, gradient) - 'boundary_gw' : 'no_flow', # Landward groundwater boundary, dGw/dx = 0 (or 'static') - 'method_moist_threshold' : 'belly_johnson', # Name of method to compute wind velocity threshold based on soil moisture content - 'method_moist_process' : 'infiltration', # Name of method to compute soil moisture content(infiltration or surface_moisture) - 'offshore_flux' : 0., # [-] Factor to determine offshore boundary flux as a function of Q0 (= 1 for saturated flux , = 0 for noflux) - 'constant_offshore_flux' : 0., # [kg/m/s] Constant input flux at offshore boundary - 'onshore_flux' : 0., # [-] Factor to determine onshore boundary flux as a function of Q0 (= 1 for saturated flux , = 0 for noflux) - 'constant_onshore_flux' : 0., # [kg/m/s] Constant input flux at offshore boundary - 'lateral_flux' : 0., # [-] Factor to determine lateral boundary flux as a function of Q0 (= 1 for saturated flux , = 0 for noflux) - 'method_transport' : 'bagnold', # Name of method to compute equilibrium sediment transport rate - 'method_roughness' : 'constant', # Name of method to compute the roughness height z0, note that here the z0 = k, which does not follow the definition of Nikuradse where z0 = k/30. - 'method_grainspeed' : 'windspeed', # Name of method to assume/compute grainspeed (windspeed, duran, constant) - 'method_shear' : 'fft', # Name of method to compute topographic effects on wind shear stress (fft, quasi2d, duna2d (experimental)) - 'max_error' : 1e-6, # [-] Maximum error at which to quit iterative solution in implicit numerical schemes - 'max_iter' : 1000, # [-] Maximum number of iterations at which to quit iterative solution in implicit numerical schemes - 'max_iter_ava' : 1000, # [-] Maximum number of iterations at which to quit iterative solution in avalanching calculation - 'refdate' : '2020-01-01 00:00', # [-] Reference datetime in netCDF output - 'callback' : None, # Reference to callback function (e.g. example/callback.py':callback) - 'wind_convention' : 'nautical', # Convention used for the wind direction in the input files (cartesian or nautical) - 'alfa' : 0, # [deg] Real-world grid cell orientation wrt the North (clockwise) - 'dune_toe_elevation' : 3, # Choose dune toe elevation, only used in the PH12 dune erosion solver - 'beach_slope' : 0.1, # Define the beach slope, only used in the PH12 dune erosion solver - 'veg_min_elevation' : -10., # Minimum elevation (m) where vegetation can grow; default -10 disables restriction (allows vegetation everywhere). Set to a higher value to enforce a minimum elevation for vegetation growth. 'vegshear_type' : 'raupach', # Choose the Raupach grid based solver (1D or 2D) or the Okin approach (1D only) 'okin_c1_veg' : 0.48, #x/h spatial reduction factor in Okin model for use with vegetation 'okin_c1_fence' : 0.48, #x/h spatial reduction factor in Okin model for use with sand fence module @@ -340,6 +368,83 @@ 'rhoveg_max' : 0.5, #maximum vegetation density, only used in duran and moore 14 formulation 't_veg' : 3, #time scale of vegetation growth (days), only used in duran and moore 14 formulation 'v_gam' : 1, # only used in duran and moore 14 formulation + + # --- Dune erosion parameters --- # + 'dune_toe_elevation' : 3, # Choose dune toe elevation, only used in the PH12 dune erosion solver + 'beach_slope' : 0.1, # Define the beach slope, only used in the PH12 dune erosion solver + 'veg_min_elevation' : -10., # Minimum elevation (m) where vegetation can grow; default -10 disables restriction. + + # --- Bed interaction in advection equation (new process) --- # + 'zeta_base' : 1.0, # [-] Base value for bed interaction parameter in advection equation + 'zeta_sheltering' : False, # [-] Include sheltering effect of roughness elements on bed interaction parameter + 'p_zeta_moist' : 0.8, # [-] Exponent parameter for computing zeta from moisture + 'a_weibull' : 1.0, # [-] Shape parameter k of Weibull function for bed interaction parameter zeta + 'b_weibull' : 0.5, # [m] Scale parameter lambda of Weibull function for bed interaction parameter zeta + 'bounce' : [0.75], # [-] Fraction of sediment skimming over vegetation canopy (species-specific) + 'alpha_lift' : 0.2, # [-] Vegetation-induced upward lift (0-1) of transport-layer centroid + + # --- Grass vegetation model (new vegetation framework) --- # + 'method_vegetation' : 'duran', # ['duran' | 'grass'] Vegetation formulation + 'veg_res_factor' : 5, # [-] Vegetation subgrid refinement factor (dx_veg = dx / factor) + 'dt_veg' : 86400., # [s] Time step for vegetation growth calculations + 'species_names' : ['marram'], # [-] Name(s) of vegetation species + 'hveg_file' : None, # Filename of ASCII file with initial vegetation height (shape: ny * nx * nspecies) + 'Nt_file' : None, # Filename of ASCII file with initial tiller density (shape: ny * nx * nspecies) + + 'd_tiller' : [0.006], # [m] Mean tiller diameter + 'r_stem' : [0.2], # [-] Fraction of rigid (non-bending) stem height + 'alpha_uw' : [-0.0412], # [s/m] Wind-speed sensitivity of vegetation bending + 'alpha_Nt' : [1.95e-4], # [m^2] Tiller-density sensitivity of vegetation bending + 'alpha_0' : [0.9445], # [-] Baseline bending factor (no wind, sparse vegetation) + + 'G_h' : [1.0], # [m/yr] Intrinsic vertical vegetation growth rate + 'G_c' : [2.5], # [tillers/tiller/yr] Intrinsic clonal tiller production rate + 'G_s' : [0.01], # [tillers/tiller/yr] Intrinsic seedling establishment rate + 'Hveg' : [0.8], # [m] Maximum attainable vegetation height + 'phi_h' : [1.0], # [-] Saturation exponent for height growth + + 'Nt_max' : [900.0], # [1/m^2] Maximum attainable tiller density + 'R_cov' : [1.2], # [m] Radius for neighbourhood density averaging + + 'lmax_c' : [0.9], # [m] Maximum clonal dispersal distance + 'mu_c' : [2.5], # [-] Shape parameter of clonal dispersal kernel + 'alpha_s' : [4.0], # [m^2] Scale parameter of seed dispersal kernel + 'nu_s' : [2.5], # [-] Tail-heaviness of seed dispersal kernel + + 'T_burial' : 86400.*30., # [s] Time scale for sediment burial effect on vegetation growth (replaces avg_time) + 'gamma_h' : [1.0], # [-] Sensitivity of vertical growth to burial (1 / dzb_tol_h) + 'dzb_tol_c' : [1.0], # [m/yr] Tolerance burial range for clonal expansion + 'dzb_tol_s' : [0.1], # [m/yr] Tolerance burial range for seed establishment + 'dzb_opt_h' : [0.5], # [m/yr] Optimal burial rate for vertical growth + 'dzb_opt_c' : [0.5], # [m/yr] Optimal burial rate for clonal expansion + 'dzb_opt_s' : [0.025], # [m/yr] Optimal burial rate for seed establishment + + 'beta_veg' : [120.0], # [-] Vegetation momentum-extraction efficiency (Raupach) + 'm_veg' : [0.4], # [-] Shear non-uniformity correction factor + 'c1_okin' : [0.48], # [-] Downwind decay coefficient in Okin shear reduction + + 'veg_sigma' : 0., # [-] Sigma in gaussian distrubtion of vegetation cover filter + 'zeta_sigma' : 0., # [-] Standard deviation for smoothing vegetation bed interaction parameter + + 'alpha_comp' : [0.], # [-] Lotka–Volterra competition coefficients + # shape: nspecies * nspecies (flattened) + # alpha_comp[k,l] = effect of species l on species k + + 'T_flood' : 7200., # [s] Time scale for vegetation flood stress mortality (half-life under constant inundation) + 'gamma_Nt_decay' : 0., # [-] Sensitivity of tiller density decay to relative reduction in hveg + + + # --- Separation bubble parameters --- # + 'sep_look_dist' : 50., # [m] Flow separation: Look-ahead distance for upward curvature anticipation + 'sep_k_press_up' : 0.05, # [-] Flow separation: Press-up curvature + 'sep_k_crit_down' : 0.18, # [1/m] Flow separation: Maximum downward curvature + 'sep_s_crit' : 0.18, # [-] Flow separation: Critical bed slope below which reattachment is forced + 'sep_s_leeside' : 0.25, # [-] Maximum downward leeside slope of the streamline + + # --- Other --- # + 'Tsalt' : 3600.*24.*30., # [s] Adaptation time scale for salinitation + 'csalt' : 35e-3, # [-] Maximum salt concentration in bed surface layer + 'cpair' : 1.0035e-3, # [MJ/kg/oC] Specific heat capacity air } REQUIRED_CONFIG = ['nx', 'ny'] diff --git a/aeolis/gui/application.py b/aeolis/gui/application.py index b1840bfe..e75aa3d3 100644 --- a/aeolis/gui/application.py +++ b/aeolis/gui/application.py @@ -210,10 +210,19 @@ def create_input_file_tab(self, tab_control): command=self.browse_save_location) save_browse_button.grid(row=3, column=2, sticky=W, pady=5, padx=5) - # Save button + # Save button (diffs only) save_config_button = ttk.Button(file_ops_frame, text="Save Configuration", - command=self.save_config_file) + command=self.save_config_file) save_config_button.grid(row=4, column=1, sticky=W, pady=10, padx=10) + save_config_desc = ttk.Label(file_ops_frame, text="Writes only parameters that differ from defaults.") + save_config_desc.grid(row=4, column=2, sticky=W, pady=10, padx=5) + + # Save full button (all params) + save_full_config_button = ttk.Button(file_ops_frame, text="Save Full Configuration", + command=self.save_full_config_file) + save_full_config_button.grid(row=5, column=1, sticky=W, pady=5, padx=10) + save_full_config_desc = ttk.Label(file_ops_frame, text="Writes every parameter, including defaults.") + save_full_config_desc.grid(row=5, column=2, sticky=W, pady=5, padx=5) def create_domain_tab(self, tab_control): # Create the 'Domain' tab @@ -445,7 +454,9 @@ def save_config_file(self): try: # Update dictionary with current entry values for field, entry in self.entries.items(): - self.dic[field] = entry.get() + value = entry.get() + # Convert empty strings and whitespace-only strings to None + self.dic[field] = None if value.strip() == '' else value # Write the configuration file aeolis.inout.write_configfile(save_path, self.dic) @@ -458,6 +469,31 @@ def save_config_file(self): messagebox.showerror("Error", error_msg) print(error_msg) + def save_full_config_file(self): + """Save the full configuration (including defaults) to a file""" + save_path = self.save_config_entry.get() + + if not save_path: + messagebox.showwarning("Warning", "Please specify a file path to save the configuration.") + return + + try: + # Update dictionary with current entry values + for field, entry in self.entries.items(): + value = entry.get() + self.dic[field] = None if value.strip() == '' else value + + # Write the full configuration file + aeolis.inout.write_configfile(save_path, self.dic, include_defaults=True) + + messagebox.showinfo("Success", f"Full configuration saved to:\n{save_path}") + + except Exception as e: + import traceback + error_msg = f"Failed to save full config file: {str(e)}\n\n{traceback.format_exc()}" + messagebox.showerror("Error", error_msg) + print(error_msg) + def toggle_color_limits(self): """Enable or disable colorbar limit entries based on auto limits checkbox""" if self.auto_limits_var.get(): diff --git a/aeolis/hydro.py b/aeolis/hydro.py index 6ac42541..b948797a 100644 --- a/aeolis/hydro.py +++ b/aeolis/hydro.py @@ -67,7 +67,8 @@ def interpolate(s, p, t): if p['process_tide']: # Check if SWL or zs are not provided by some external model # In that case, skip initialization - if ('zs' not in p['external_vars']) : + if not p['external_vars'] or ('zs' not in p['external_vars']) : + if p['tide_file'] is not None: s['SWL'][:,:] = interp_circular(t, p['tide_file'][:,0], @@ -101,7 +102,7 @@ def interpolate(s, p, t): # Check if Hs or Tp are not provided by some external model # In that case, skip initialization - if ('Hs' not in p['external_vars']) and ('Tp' not in p['external_vars']): + if not p['external_vars'] or (('Hs' not in p['external_vars']) and ('Tp' not in p['external_vars'])): if p['process_wave'] and p['wave_file'] is not None: @@ -158,7 +159,7 @@ def interpolate(s, p, t): if p['process_runup']: ny = p['ny'] - if ('Hs' in p['external_vars']): + if p['external_vars'] and ('Hs' in p['external_vars']): eta, sigma_s, R = calc_runup_stockdon(s['Hs'], s['Tp'], p['beach_slope']) s['R'][:] = R diff --git a/aeolis/inout.py b/aeolis/inout.py index b4741677..7b891030 100644 --- a/aeolis/inout.py +++ b/aeolis/inout.py @@ -121,11 +121,12 @@ def read_configfile(configfile, parse_files=True, load_defaults=True): return p -def write_configfile(configfile, p=None): +def write_configfile(configfile, p=None, include_defaults=False): '''Write model configuration file Writes model configuration to file. If no model configuration is - given, the default configuration is written to file. Any + given, the default configuration is written to file. Preserves + the structure and organization from DEFAULT_CONFIG. Any parameters with a name ending with `_file` and holding a matrix are treated as separate files. The matrix is then written to an ASCII file using the ``numpy.savetxt`` function and the parameter @@ -137,6 +138,9 @@ def write_configfile(configfile, p=None): Model configuration file p : dict, optional Dictionary with model configuration parameters + include_defaults : bool, optional + If True, write all parameters including defaults; if False, skip + parameters equal to the default values Returns ------- @@ -153,24 +157,140 @@ def write_configfile(configfile, p=None): if p is None: p = DEFAULT_CONFIG.copy() - fmt = '%%%ds = %%s\n' % np.max([len(k) for k in p.keys()]) + # Helper: safely determine if a value equals the default without broadcasting errors + def _is_default_value(key, value): + if key not in DEFAULT_CONFIG: + return False + + default = DEFAULT_CONFIG[key] + + try: + return np.array_equal(np.asarray(value, dtype=object), + np.asarray(default, dtype=object)) + except Exception: + try: + return value == default + except Exception: + return False + + # Parse constants.py to extract section headers, order, and comments + import aeolis.constants + constants_file = aeolis.constants.__file__ + + section_headers = [] + section_order = {} + comments = {} + + with open(constants_file, 'r', encoding='utf-8') as f: + lines = f.readlines() + + # Find DEFAULT_CONFIG definition + in_default_config = False + current_section = None + current_keys = [] + + for line in lines: + # Check if we're entering DEFAULT_CONFIG + if 'DEFAULT_CONFIG' in line and '=' in line and '{' in line: + in_default_config = True + continue + # Check if we're exiting DEFAULT_CONFIG + if in_default_config and line.strip().startswith('}'): + # Save last section + if current_section and current_keys: + section_order[current_section] = current_keys + break + + if in_default_config: + # Check for section header (starts with # ---) + if line.strip().startswith('# ---') and len(line.strip()) > 10: + # Save previous section + if current_section and current_keys: + section_order[current_section] = current_keys + current_keys = [] + + # Extract new section name (between # --- and ---) + text = line.strip()[5:].strip().rstrip('- #') + current_section = text + section_headers.append(current_section) + + # Check for parameter definition (contains ':' and not a comment-only line) + elif ':' in line and "'" in line and not line.strip().startswith('#'): + # Extract parameter name + param_match = re.search(r"'([^']+)'", line) + if param_match: + param_name = param_match.group(1) + current_keys.append(param_name) + + # Extract comment (after #) + if '#' in line.split(':')[1]: + comment_part = line.split('#')[1].strip() + comments[param_name] = comment_part + + # Determine column widths for formatting + max_key_len = max(len(k) for k in p.keys()) if p else 30 + with open(configfile, 'w') as fp: - + # Write header fp.write('%s\n' % ('%' * 70)) fp.write('%%%% %-64s %%%%\n' % 'AeoLiS model configuration') fp.write('%%%% Date: %-58s %%%%\n' % time.strftime('%Y-%m-%d %H:%M:%S')) fp.write('%s\n' % ('%' * 70)) fp.write('\n') - for k, v in sorted(p.items()): - if k.endswith('_file') and isiterable(v): - fname = '%s.txt' % k.replace('_file', '') - backup(fname) - np.savetxt(fname, v) - fp.write(fmt % (k, fname)) - else: - fp.write(fmt % (k, print_value(v, fill=''))) + # Write each section + for section in section_headers: + if section not in section_order: + continue + + keys_in_section = section_order[section] + section_keys = [k for k in keys_in_section if k in p] + + if not section_keys: + continue + + # Write section header + fp.write('%% %s %s %s %% \n' % ('-' * 15, section, '-' * 15)) + # fp.write('%% %s\n' % ('-' * 70)) + + # Write each key in this section + for key in section_keys: + value = p[key] + + # Skip this key if its value matches the default and skipping is allowed + if not include_defaults and _is_default_value(key, value): + continue + + comment = comments.get(key, '') + + # Format the value + formatted_value = print_value(value, fill='None') + + # Write the line with proper formatting + fp.write('{:<{width}} = {:<20} % {}\n'.format( + key, formatted_value, comment, width=max_key_len + )) + + fp.write('\n') # Blank line between sections + + # Write any remaining keys not in the section order + remaining_keys = [k for k in p.keys() if k not in sum(section_order.values(), [])] + if remaining_keys: + fp.write('%% %s %s\n' % ('-' * 15, 'Additional Parameters')) + # fp.write('%% %s\n' % ('-' * 70)) + for key in sorted(remaining_keys): + value = p[key] + + # Skip this key if its value matches the default and skipping is allowed + if not include_defaults and _is_default_value(key, value): + continue + + comment = comments.get(key, '') + formatted_value = print_value(value, fill='None') + fp.write('{:<{width}} = {:<20} %% {}\n'.format( + key, formatted_value, comment, width=max_key_len + )) def check_configuration(p): @@ -281,7 +401,11 @@ def parse_value(val, parse_files=True, force_list=False): val = val.strip() - if ' ' in val or force_list: + # Check for datetime patterns (YYYY-MM-DD HH:MM:SS or similar) + # Treat as string if it matches datetime format + if re.match(r'^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}(:\d{2})?', val): + return val + elif ' ' in val or force_list: return np.asarray([parse_value(x) for x in val.split(' ')]) elif re.match('^[TF]$', val): return val == 'T' diff --git a/aeolis/utils.py b/aeolis/utils.py index cc7d7c2b..87840f30 100644 --- a/aeolis/utils.py +++ b/aeolis/utils.py @@ -255,7 +255,7 @@ def print_value(val, fill=''): if isiterable(val): return ' '.join([print_value(x) for x in val]) - elif val is None: + elif val is None or val == '': return fill elif isinstance(val, bool): return 'T' if val else 'F'