An idea to solve the double parsing and compile/runtime configuration problem
Similar to #107 (closed), this ticket proposes an idea which solves two problems we currently have in ExaHyPE.
Problem 1: Double Parsing problem
The ExaHyPE specification files are described by the exahype.grammar. For the Java toolkit, there is a parser generator (SableCC) which allows a neat way to create the application during parsing the specification file. But since the specification file also holds runtime constants, the ExaHyPE binary expects a valid and compatible spec file as it's first argument, ie. call with
cmd> ExaHyPE-Euler.exe ../EulerFlow.exahype
In order to parse the specfile at (C++) runtime, there is that Parser.h code. It works with regular expressions and knows nothing about exahype.grammar
. Thus we sometimes expect that the two parsers don't parse the same spec file similarly or with similar error messages when having problems.
Problem 2: No clear seperation off compiletime (ie. computing) and runtime (ie. physics) parameters
The current specfile format combines two semantically different concepts of parameters:
- Computing-related parameters, ie. project names, source code paths, architecture and compiler specific stuff, optimization parameters, all the PDE properties, all the basic properties about generated code (ie. number and names of classes of plotters)
- Run-related parameters, ie. the grid extend, parallelization parameters (number of cores, hybrid setups), plotting properties (frequencies, slicing, ...), physics/application/user parameters (ie. Initial Data, Boundary Conditions)
The user has absolutely no idea which parameter is a compile time constant (and probably even needs a rerun of the toolkit after change) and which parameter is a runtime constant, ie. can be chosen freely when submitting a job at a HPC queue.
Actually this problem is even deeper: There are further constants which can be chosen when running make
, ie. after code generation by the toolkit. These are especially options about the parallelization, ie. whether to build in TBB and MPI support or not. However, these options are also specified in the toolkit.
A seperation approach
I suggest to keep the specification files for the toolkit runs. I will demonstrate it by splitting the current EulerFlow.exahype into two files. The toolkit specfile would look like:
exahype-project Euler
peano-kernel-path = ./Peano
exahype-path = ./ExaHyPE
output-directory = ./ApplicationExamples/EulerFlow
architecture = noarch
computational-domain
dimension = 2
end computational-domain
shared-memory
identifier = dummy
end shared-memory
distributed-memory
configure = {whatever-needs-to-be-generated-here?}
end distributed-memory
solver ADER-DG MyEulerSolver
variables = rho:1,j:3,E:1
order = 3
kernel = generic::fluxes::nonlinear
language = C
plot vtk::Cartesian::cells::ascii ConservedQuantitiesWriter
variables = 5
end plot
plot vtk::Cartesian::vertices::ascii ComputeGlobalIntegrals
variables = 0
end plot
plot vtk::Cartesian::vertices::ascii PrimitivesWriter
variables = 5
end plot
plot vtk::Cartesian::vertices::ascii ExactPrimitivesWriter
variables = 5
end plot
plot probe::ascii ProbeWriter
variables = 5
end plot
end solver
end exahype-project
In contrast, we could now introduce a very simple parameter file format where an accurate parser can be written in C++ very simply and which is handed as command line argument to the ExaHyPE runs:
# Only single line comments like this
log-file = mylogfile.log
computational-domain::width = 1.0, 1.0
computational-domain::offset = 0.0, 0.0
computational-domain::end-time = 0.12
shared-memory::identifier = dummy
shared-memory::cores = 1 # NB: This should REALLY be driven by an ENV parameter like OMP_NUM_THREADS
distributed-memory::buffer-size = 64
distributed-memory::timeout = 60
optimisation::fuse-algorithmic-steps = off
# 0.0 und 0.8 sind schon mal zwei Faktoren
optimisation::fuse-algorithmic-steps-factor = 0.99
optimisation::timestep-batch-factor = 0.0
optimisation::skip-reduction-in-batched-time-steps = on
optimisation::disable-amr-if-grid-has-been-stationary-in-previous-iteration = off
optimisation::double-compression = 0.0
optimisation::spawn-double-compression-as-background-thread = off
# when this is a runtime constant...
MyEulerSolver::order = 3
# I would love to specify the maximum-mesh-level = 3 instead
MyEulerSolver::maximum-mesh-size = 0.05
# Plotter example
ConservedQuantitiesWriter::time = 0.0
ConservedQuantitiesWriter::repeat = 0.05
ConservedQuantitiesWriter::output = ./conserved
ConservedQuantitiesWriter::select = x:0.0,y:0.0
ComputeGlobalIntegrals::time = 0.0
ComputeGlobalIntegrals::repeat = 0.05
ComputeGlobalIntegrals::output = ./output/these-files-should-not-be-there
ComputeGlobalIntegrals::select = x:0.0,y:0.0
# etc.
# own parameters simple as hell
ML_CCZ4::timelevels = 3
ML_CCZ4::harmonicN = 1.0 # 1+log
ML_CCZ4::harmonicF = 2.0 # 1+log
ML_CCZ4::BetaDriver = 0.4 # ~1/M (\eta)
ML_CCZ4::advectLapse = 1
ML_CCZ4::advectShift = 1
ML_CCZ4::evolveA = 0
ML_CCZ4::evolveB = 1
ML_CCZ4::shiftGammaCoeff = 0.75
ML_CCZ4::shiftFormulation = 0 # Gamma driver
ML_CCZ4::fixAdvectionTerms = 1
ML_CCZ4::dampk1 = 0.05
ML_CCZ4::dampk2 = 0.0
ML_CCZ4::GammaShift = 0.5
ML_CCZ4::MinimumLapse = 1.0e-8
ML_CCZ4::conformalMethod = 1 # 1 for W
ML_CCZ4::dt_lapse_shift_method = "noLapseShiftAdvection"
ML_CCZ4::initial_boundary_condition = "extrapolate-gammas"
ML_CCZ4::rhs_boundary_condition = "NewRad"
Boundary::radpower = 2
ML_CCZ4::ML_log_confac_bound = "none"
ML_CCZ4::ML_metric_bound = "none"
ML_CCZ4::ML_Gamma_bound = "none"
ML_CCZ4::ML_trace_curv_bound = "none"
ML_CCZ4::ML_curv_bound = "none"
ML_CCZ4::ML_lapse_bound = "none"
ML_CCZ4::ML_dtlapse_bound = "none"
ML_CCZ4::ML_shift_bound = "none"
ML_CCZ4::ML_dtshift_bound = "none"
AHFinderDirect::N_horizons = 1
AHFinderDirect::find_every = 64
AHFinderDirect::output_h_every = 0
AHFinderDirect::max_Newton_iterations__initial = 50
AHFinderDirect::max_Newton_iterations__subsequent = 50
AHFinderDirect::max_allowable_Theta_growth_iterations = 10
AHFinderDirect::max_allowable_Theta_nonshrink_iterations = 10
AHFinderDirect::geometry_interpolator_name = "Lagrange polynomial interpolation"
AHFinderDirect::geometry_interpolator_pars = "order=4"
AHFinderDirect::surface_interpolator_name = "Lagrange polynomial interpolation"
AHFinderDirect::surface_interpolator_pars = "order=4"
AHFinderDirect::verbose_level = "physics details"
AHFinderDirect::move_origins = "yes"
AHFinderDirect::origin_x[1] = 0.0
AHFinderDirect::initial_guess__coord_sphere__x_center[1] = 0.0
AHFinderDirect::initial_guess__coord_sphere__y_center[1] = 0.0
AHFinderDirect::initial_guess__coord_sphere__z_center[1] = 0.0
AHFinderDirect::initial_guess__coord_sphere__radius[1] = 3.0
AHFinderDirect::which_surface_to_store_info[1] = 0
AHFinderDirect::set_mask_for_individual_horizon[1] = "no"
AHFinderDirect::reset_horizon_after_not_finding[1] = "no"
AHFinderDirect::find_after_individual_time[1] = 2000.0
AHFinderDirect::max_allowable_horizon_radius[1] = 5.0
# ...