boutproject / BOUT-dev

BOUT++: Plasma fluid finite-difference simulation code in curvilinear coordinate systems
http://boutproject.github.io/
GNU Lesser General Public License v3.0
179 stars 92 forks source link

More user-friendly options for Laplacian solvers #1835

Open johnomotani opened 4 years ago

johnomotani commented 4 years ago

Using flags to set many of the options for Laplacian solvers is cryptic, and not consistent with the way options are handled everywhere else. Could we replace them with something nicer? For example:

// Global options
bool zero_dc = false; /// Zero the DC (constant in Z) component of the solution
bool start_new = false; /// Iterative method start from solution=0. Has no effect for direct solvers
bool kx_zero = false; /// Zero the kx=0, n = 0 component

// Boundary options
std::string inner_boundary_condition; /// possible values "dirichlet", "neumann", "laplacian" or "cylinder"
std::string inner_boundary_condition_dc; /// default to same as inner_boundary_condition, possible values "dirichlet", "neumann", "laplacian", "cylinder", "gradpar", "gradparinv"
std::string inner_set; /// "zero", "initial_guess" or "rhs" [no flag, INVERT_SET or INVERT_RHS respectively]
bool inner_one_point = false; /// Only use one boundary point
... same for outer_* [except no "cylinder" option, as this is only needed for the inner boundary that could be the axis of the cylinder]

See also #91.

ZedThree commented 4 years ago

I am absolutely for this.

What's the best type for the *_boundary_condition options? Could users in principle define their own boundary conditions to be applied? If so, string makes sense. If they're things that needed to be implemented in the library, then an enum class might be better.

If lots of these options can be combined, bit flags might make sense, as opposed to taking lots of bools for instance.

johnomotani commented 4 years ago

I don't think there's a sensible way for users to define their own boundary conditions (without some major refactor that's beyond my imagination now anyway), so enum class probably makes most sense.

I don't think any of the options that have gone into *_boundary_condition/*_boundary_condition_dc can be combined, and I'm sure the values in *_set can't be.

I don't think the *_one_point option makes sense - for example it's inconsistent with allowing MXG to have different values because if MXG changes and *_one_point == true then the boundary condition applied in the Laplacian solver changes. So I'd be in favour of removing that option [updated list of suggestions in the post at the top to add this].

If we remove kx_zero and *_one_point there're only two bools left - that's not too many.

johnomotani commented 4 years ago

Just to make a note while I remember: If we use an enum class (defined in the Laplacian namespace) like enum class BoundaryCondition {dirichlet, neumann, laplacian, cylinder, gradpar, gradparinv}; and include a function BoundaryCondition BoundaryConditionFromString(std::string s); then we can read the setting from a string option with something like

inner_boundary_condition = BoundaryConditionFromString(
                               options["inner_boundary_condition"]
                                   .withDefault("dirichlet")
                                   .doc("The inner boundary condition for the Laplacian solver."));

and this should throw an exception if the value passed is not supported, as long as we implement like e.g. https://github.com/boutproject/BOUT-dev/blob/123e774f865d4c98d378b909b89accd7efd56dc3/src/sys/bout_types.cxx#L149-L156

Another possibility would be to have a different enum class BoundaryCondition for each implementation, which would contain only the supported options, rather than having to check separately, but this probably is not so useful because the FFT-based solvers should all share the same set, which are implemented in tridagMatrix.