thliebig / openEMS

openEMS is a free and open-source electromagnetic field solver using the EC-FDTD method.
http://openEMS.de
GNU General Public License v3.0
413 stars 146 forks source link

Completely Rewrite Command-Line Argument Parsing, and Support Passing All Arguments via Python #140

Open biergaizi opened 2 months ago

biergaizi commented 2 months ago

Completely Rewrite Command-Line Argument Parsing

Currently, the openEMS command-line argument parsing has two limitations.

  1. If a new module is added into openEMS and that module accepts runtime options, one must change the main openems.cpp to add one new API per option, decreasing code modularity - especially if an option is only used by a single module. This is unsustainable in the long run. The in-development Tiling engine is expected to accept 3 to 4 options.

  2. When we're running as a shared library (used by Python binding), if one wants to pass runtime options from Python, one must change the main openems.cpp to add one new API per option. This is again unsustainable. For example, currently there's no way to change the simulation engine via Python because of the lack of APIs.

To overcome these limitations, this commit completely rewrites the command-line argument parsing logic, and a new API SetLibraryArguments() which can be used to set almost all options supported by the main openEMS executable.

The new logic works in the following way:

  1. If a class accepts options, it provides a method optionDesc() that returns a options_description. This data structure contains a list of supported options, their text descriptions. Optional callback functions can also be registered, which may be used to set the internal state of a class. During early initialization, the main program openems.cpp collects these options_description from multiple classes, then registers them to g_settings by calling Global::appendOptionDesc(). The combined structure is stored as m_optionDesc. Boost can automatically validate arguments, generate help messages, provide default values, etc.

  2. Before starting simulation, if we're running as an executable, parseCommandLineArguments() is called. If we're running as a shared library, parseOption() is called (by the user). Both functions calls Boost's program_options library to parse supplied string options automatically. Registered Callback functions are also executed at this moment, so the variables and states within a class is updated automatically.

  3. The parsed options is also globally stored as a variables_map in m_options of g_settings, which can be accessed globally via Global::hasOption() and Global::getOption(). This is useful when a class is only newed after parsing all options (such as an engine-specific option), as callbacks can't be used.

  4. A new API SetLibraryArguments() is provided for setting command-line arguments when running as a shared library:

    void SetLibraryArguments(std::vector allOptions);

python: use new SetLibraryArguments() API

In the rewritten command-line argument parsing logic, a new API SetLibraryArguments() is provied, which can be used to set almost all options supported by the main openEMS executable by passing options as strings.

This commit change FDTD.Run() to use the new API. As a result, all command-line options, such as engine types and debugging options, are supported by the Python binding.

Outcomes

  1. New help screen, automatically generated by Boost's program_options library:
 ---------------------------------------------------------------------- 
 | openEMS 64bit -- version v0.0.36-17-gd56ff99
 | (C) 2010-2023 Thorsten Liebig <thorsten.liebig@gmx.de>  GPL license
 ---------------------------------------------------------------------- 
    Used external libraries:
        CSXCAD -- Version: v0.6.3-2-gc6a1587
        hdf5   -- Version: 1.14.2
                  compiled against: HDF5 library version: 1.14.2
        tinyxml -- compiled against: 2.6.2
        fparser
        boost  -- compiled against: 1_83
        vtk -- Version: 9.2.6
               compiled against: 9.2.6

 Usage: openEMS <FDTD_XML_FILE> [<options>...]

Options:
  -h [ --help ]                Show this help message and exit
  --disable-dumps              Disable all field dumps for faster simulation
  --debug-material             Dump material distribution to a vtk file for 
                               debugging
  --debug-PEC                  Dump metal distribution to a vtk file for 
                               debugging
  --debug-operator             Dump operator to vtk file for debugging
  --debug-boxes                Dump e.g. probe boxes to vtk file for debugging
  --debug-CSX                  Write CSX geometry file to debugCSX.xml
  --engine arg (=fastest)      Choose engine type 

                                 fastest: fastest available engine (default)
                                 basic: basic FDTD engine
                                 sse: engine using SSE vector extensions
                                 sse-compressed: engine using compressed 
                                                 operator + sse vector 
                                                 extensions
                                 multithreaded: engine using compressed 
                                                operator + sse vector 
                                                extensions + multithreading

  --numThreads arg (=0)        Force use n threads for multithreaded engine 
                               (needs: --engine=multithreaded)
  --no-simulation              only run preprocessing; do not simulate
  --dump-statistics            dump simulation statistics to 
                               'openEMS_run_stats.txt' and 'openEMS_stats.txt'

Additional global arguments:
  --showProbeDiscretization    Show probe discretization information
  --nativeFieldDumps           Dump all fields using the native field 
                               components
  -v [ --verbose ] [=arg(=1)]  Verbose level, select debug level 1 to 3, also 
                               accept -v, -vv, -vvv
  1. All program options are now supported by Python

e.g.

FDTD.Run(path, engine="basic")

produces:

openEMS - enabled basic engine                                                                                                                                
 ----------------------------------------------------------------------                                                                                       
 | openEMS 64bit -- version v0.0.36-17-gd56ff99                                                                                                               
 | (C) 2010-2023 Thorsten Liebig <thorsten.liebig@gmx.de>  GPL license                                                                                        
 ----------------------------------------------------------------------                                                                                       
        Used external libraries:                                                                                                                              
                CSXCAD -- Version: v0.6.3-2-gc6a1587                                                                                                          
                hdf5   -- Version: 1.14.2                                                                                                                     
                          compiled against: HDF5 library version: 1.14.2                                                                                      
                tinyxml -- compiled against: 2.6.2                                                                                                            
                fparser                                                                                                                                       
                boost  -- compiled against: 1_83                                                                                                              
                vtk -- Version: 9.2.6                                                                                                                         
                       compiled against: 9.2.6                                                                                                                

Create FDTD operator                                                                                 
  1. It's no longer necessary to change the APIs inside main openEMS class after adding new modules and new options. Only a small change is necessary here:
void openEMS::collectCommandLineArguments()
{
    // register our supported options to g_settings
    g_settings.appendOptionDesc(optionDesc());
    g_settings.appendOptionDesc(g_settings.optionDesc());
}