Skip to content

Commit d1d3d0a

Browse files
Merge pull request #13984 from KratosMultiphysics/trilinos/more-consistent-direct-solver-handling
[TrilinosApplication] Better documentation for `FastestDirectSolverList` in `TrilinosSpace` and avoid duplication in `CreateFastestAvailableDirectLinearSolver`
2 parents 7dc7c5a + 92fa3c0 commit d1d3d0a

2 files changed

Lines changed: 205 additions & 129 deletions

File tree

applications/TrilinosApplication/python_scripts/trilinos_linear_solver_factory.py

Lines changed: 178 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,66 @@
55
import KratosMultiphysics.TrilinosApplication as KratosTrilinos
66

77
"""
8-
This module provides functionality for creating a linear solver for use with the Trilinos application.
9-
It includes checking and converting deprecated solver types, checking if the desired solver is compiled, and creating the fastest available direct linear solver.
10-
11-
Importing necessary Kratos modules:
12-
- `KratosMultiphysics` as `KM`
13-
- `KratosMultiphysics.TrilinosApplication` as `KratosTrilinos`
14-
15-
Functions:
16-
- `_CheckIfTypeIsDeprecated(config)`: Checks and translates deprecated solver names to new names for backward compatibility.
17-
- `_CheckIfSolverIsCompiled(solver_type)`: Checks if the desired solver is compiled.
18-
- `ConstructSolver(configuration)`: Constructs the solver based on the given configuration.
19-
- `CreateFastestAvailableDirectLinearSolver()`: Creates the fastest available direct linear solver.
20-
21-
Example:
22-
config = KM.Parameters({
23-
"solver_type": "CGSolver"
24-
})
25-
solver = ConstructSolver(config)
8+
@package trilinos_solver_factory
9+
10+
@brief Module for creating Trilinos-based linear solvers in Kratos.
11+
12+
This module provides essential functionality for configuring and instantiating
13+
linear solvers specifically designed for the Kratos Trilinos application.
14+
It ensures robustness and compatibility by:
15+
* Checking and automatically updating deprecated solver types.
16+
* Verifying the availability of desired solvers based on the Kratos compilation.
17+
* Providing a utility to automatically select the most efficient direct solver.
18+
19+
@section Module_Functions Public and Internal Functions
20+
* @c _CheckIfTypeIsDeprecated(config): Internal function to check and translate deprecated solver names.
21+
* @c _CheckIfSolverIsCompiled(solver_type): Internal function to verify if the solver is available in the compilation.
22+
* @c ConstructSolver(configuration): Primary function to construct a solver based on user configuration.
23+
* @c CreateFastestAvailableDirectLinearSolver(): Convenience function to get the best available direct solver.
24+
25+
@par Example Usage
26+
@code
27+
import KratosMultiphysics as KM
28+
from . import trilinos_solver_factory # Assuming this module is here
29+
30+
config = KM.Parameters({
31+
"solver_type": "CGSolver",
32+
# ... other settings
33+
})
34+
solver = trilinos_solver_factory.ConstructSolver(config)
35+
@endcode
2636
"""
2737

2838
def _CheckIfTypeIsDeprecated(config):
2939
"""
40+
@brief Checks for deprecated solver types in the configuration.
41+
3042
This function checks if the given solver type in the configuration is deprecated.
31-
If the type is deprecated, a warning is printed and the solver type in the configuration is replaced with the new name.
43+
If the type is deprecated, a warning is printed to the console, and the solver type
44+
in the configuration is automatically replaced with the new, non-deprecated name
45+
to ensure forward compatibility.
46+
47+
@param config KM.Parameters: The configuration parameters object containing the solver settings.
3248
33-
Args:
34-
config (KM.Parameters): The configuration parameters for the solver.
49+
@return None
3550
36-
Example:
37-
_CheckIfTypeIsDeprecated(config)
51+
@par Example
52+
@code
53+
_CheckIfTypeIsDeprecated(config)
54+
@endcode
3855
"""
3956
solver_type = config["solver_type"].GetString()
4057

4158
old_new_name_map = {
42-
"CGSolver" : "cg",
43-
"BICGSTABSolver" : "bicgstab",
44-
"GMRESSolver" : "gmres",
45-
"AztecSolver" : "aztec",
46-
"MLSolver" : "multi_level",
47-
"MultiLevelSolver" : "multi_level",
48-
"AmgclMPISolver" : "amgcl",
59+
"CGSolver" : "cg",
60+
"BICGSTABSolver" : "bicgstab",
61+
"GMRESSolver" : "gmres",
62+
"AztecSolver" : "aztec",
63+
"MLSolver" : "multi_level",
64+
"MultiLevelSolver" : "multi_level",
65+
"AmgclMPISolver" : "amgcl",
4966
"AmgclMPISchurComplementSolver" : "amgcl_schur_complement",
50-
"AmesosSolver" : "amesos"
67+
"AmesosSolver" : "amesos"
5168
}
5269

5370
if solver_type in old_new_name_map:
@@ -60,130 +77,163 @@ def _CheckIfTypeIsDeprecated(config):
6077

6178
def _CheckIfSolverIsCompiled(solver_type):
6279
"""
63-
This function checks if the specified solver type is compiled.
80+
@brief Checks if a specific solver type is available in the KratosTrilinos compilation.
81+
82+
This function verifies whether the specified solver type, identified by a string,
83+
has been compiled and is accessible within the KratosTrilinos environment.
6484
65-
Args:
66-
solver_type (str): The type of the solver to check.
85+
@param solver_type The type of the solver to check, provided as a string.
6786
68-
Raises:
69-
Exception: If the solver type is not compiled.
87+
@return @c bool: Returns @c True if the solver is both available and compiled; otherwise, @c False.
7088
71-
Example:
72-
_CheckIfSolverIsCompiled("aztec")
89+
@exception Exception Thrown if the required solver factory, which is responsible
90+
for creating solvers of this type, has been completely disabled during the Kratos
91+
compilation process.
7392
"""
74-
if solver_type in ["aztec", "cg", "bicgstab", "gmres"] and not hasattr(KratosTrilinos, 'AztecSolver'):
75-
raise Exception('Trying to use Aztec-solver, which was disabled at compile time with "TRILINOS_EXCLUDE_AZTEC_SOLVER"!')
76-
if solver_type in ["amesos", "klu", "super_lu_dist", "mumps"]:
77-
has_amesos = hasattr(KratosTrilinos, 'AmesosSolver')
78-
has_amesos_2 = hasattr(KratosTrilinos, 'Amesos2Solver')
79-
if not has_amesos_2:
80-
if has_amesos:
81-
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Amesos2 not compiled but Amesos it is, recommended for better performance")
82-
else:
83-
raise Exception('Trying to use Amesos-solver, which was disabled at compile time with "TRILINOS_EXCLUDE_AMESOS_SOLVER"!')
84-
if solver_type in ["multi_level"] and not hasattr(KratosTrilinos, 'MultiLevelSolver'):
85-
raise Exception('Trying to use MultiLevelSolver-solver, which was diasbled at compile time with "TRILINOS_EXCLUDE_ML_SOLVER"!')
86-
87-
def ConstructSolver(configuration):
93+
# --- 1. Define Solver Groups ---
94+
aztec_solvers = {"aztec", "cg", "bicgstab", "gmres"}
95+
ml_solvers = {"multi_level"}
96+
97+
# --- 2. Check Aztec Solvers ---
98+
if solver_type in aztec_solvers:
99+
if not hasattr(KratosTrilinos, 'AztecSolver'):
100+
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Trying to use Aztec-solver, which was disabled at compile time with TRILINOS_EXCLUDE_AZTEC_SOLVER!")
101+
return False
102+
return True
103+
104+
# --- 3. Check MultiLevel Solvers ---
105+
if solver_type in ml_solvers:
106+
if not hasattr(KratosTrilinos, 'MultiLevelSolver'):
107+
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Trying to use MultiLevelSolver, which was disabled at compile time with TRILINOS_EXCLUDE_ML_SOLVER!")
108+
return False
109+
return True
110+
111+
# --- 4. Check Amesos (Direct) Solvers ---
112+
amesos_solvers = {
113+
"amesos", "amesos2", "klu", "klu2",
114+
"super_lu_dist", "super_lu_dist2",
115+
"mumps", "mumps2", "basker"
116+
}
117+
118+
if solver_type in amesos_solvers:
119+
has_amesos1 = hasattr(KratosTrilinos, 'AmesosSolver')
120+
has_amesos2 = hasattr(KratosTrilinos, 'Amesos2Solver')
121+
122+
# Case A: Neither is compiled
123+
if not has_amesos1 and not has_amesos2:
124+
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Trying to use Amesos-solver, which was disabled at compile time with TRILINOS_EXCLUDE_AMESOS_SOLVER!")
125+
return False
126+
127+
# Case B: Only Amesos1 is compiled (Warning for performance)
128+
if has_amesos1 and not has_amesos2:
129+
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Amesos2 not compiled but Amesos is. Recommended to compile Amesos2 for better performance.")
130+
# 'basker' is strictly an Amesos2 solver, so it fails here
131+
if solver_type == "basker":
132+
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "basker is strictly an Amesos2 solver")
133+
return False
134+
135+
# Case C: specific internal solver check
136+
# Map inputs to (required_factory, internal_trilinos_name)
137+
requires_amesos2 = solver_type.endswith("2") or solver_type == "basker"
138+
139+
# Check availability based on required factory
140+
if requires_amesos2:
141+
if not has_amesos2:
142+
return False
143+
144+
# Map external name -> internal Trillinos name
145+
map_amesos2 = {
146+
"mumps2": "amesos2_mumps",
147+
"super_lu_dist2": "amesos2_superludist",
148+
"klu2": "amesos2_klu2",
149+
"basker": "basker"
150+
}
151+
# If it's a generic name like "amesos2", we assume True, otherwise check specific availability
152+
internal_name = map_amesos2.get(solver_type)
153+
if internal_name and not KratosTrilinos.Amesos2Solver.HasSolver(internal_name):
154+
return False
155+
else: # requires Amesos1
156+
if not has_amesos1:
157+
return False
158+
159+
map_amesos1 = {
160+
"mumps": "Amesos_Mumps",
161+
"super_lu_dist": "Amesos_Superludist",
162+
"klu": "Amesos_Klu"
163+
}
164+
internal_name = map_amesos1.get(solver_type)
165+
if internal_name and not KratosTrilinos.AmesosSolver.HasSolver(internal_name):
166+
return False
167+
168+
return True
169+
170+
def ConstructSolver(configuration = KM.Parameters("""{}""")):
88171
"""
89-
Constructs and returns a Trilinos linear solver based on the given configuration.
172+
@brief Constructs a Trilinos linear solver instance.
173+
174+
This function takes a configuration object and uses the KratosTrilinos factory
175+
to create and return a fully configured Trilinos linear solver instance.
90176
91-
Args:
92-
configuration (KM.Parameters): The configuration parameters for the solver.
177+
@param configuration KM.Parameters: The configuration parameters object containing
178+
all necessary settings for the linear solver (e.g., type, tolerance, maximum iterations).
93179
94-
Returns:
95-
KratosTrilinos.TrilinosLinearSolverFactory().Create: A Trilinos linear solver.
180+
@return @c KratosTrilinos.TrilinosLinearSolverFactory().Create: A newly constructed
181+
Trilinos linear solver object ready for use in simulations.
96182
97-
Raises:
98-
Exception: If the input is not a Kratos Parameters object.
183+
@exception Exception Thrown if the input \p configuration is not a valid
184+
Kratos Parameters object.
99185
100-
Example:
101-
solver = ConstructSolver(config)
186+
@par Example
187+
@code
188+
solver = ConstructSolver(config)
189+
@endcode
102190
"""
103191
if not isinstance(configuration, KM.Parameters):
104192
raise Exception("input is expected to be provided as a Kratos Parameters object")
105193

194+
# Define solver priority list
195+
if not configuration.Has("solver_type"):
196+
linear_solvers_by_speed = KratosTrilinos.TrilinosSparseSpace.FastestDirectSolverList()
197+
for solver_name in linear_solvers_by_speed:
198+
if _CheckIfSolverIsCompiled(solver_name):
199+
configuration.AddEmptyValue("solver_type")
200+
configuration["solver_type"].SetString(solver_name)
201+
break
202+
106203
_CheckIfTypeIsDeprecated(configuration) # for backwards-compatibility
107-
_CheckIfSolverIsCompiled(configuration["solver_type"].GetString())
204+
if not _CheckIfSolverIsCompiled(configuration["solver_type"].GetString()):
205+
raise Exception(configuration["solver_type"].GetString() + " solver is not available")
108206

109207
return KratosTrilinos.TrilinosLinearSolverFactory().Create(configuration)
110208

111209
def CreateFastestAvailableDirectLinearSolver():
112210
"""
113-
Creates and returns the fastest available direct linear solver, based on a predefined order and availability.
114-
115-
TODO: A proper study of speed must be done, particularly depending of system size would be interesting
116-
117-
In Trilinos, the speed of direct solvers like MUMPS, SuperLU_DIST, KLU, and Basker can depend on various factors including the size and sparsity of the matrix, the architecture of the computer system, and the specific characteristics of the problem being solved. Below are some general observations about these solvers:
118-
119-
1. MUMPS (MUltifrontal Massively Parallel Sparse Direct Solver):
120-
- Parallel solver, handles large problems well.
121-
- Can be used on distributed-memory machines.
122-
- May have higher memory requirements.
123-
124-
2. SuperLU_DIST:
125-
- Parallel solver, also designed for distributed-memory machines.
126-
- Often used for large, sparse linear systems.
127-
- Has good performance on a wide range of problems.
211+
@brief Creates the fastest available direct linear solver.
128212
129-
3. KLU:
130-
- A serial solver, typically used for smaller problems.
131-
- Works well for circuit simulation problems and other problems with a similar structure.
132-
- Generally not suitable for large distributed-memory parallel computations.
213+
This function attempts to construct and return the most efficient direct linear
214+
solver available in the current compilation, following a predefined priority
215+
order (e.g., SuperLU, KLU, etc.) to ensure optimal performance.
133216
134-
4. Basker:
135-
- Also a serial solver, suitable for smaller problems.
136-
- May not perform as well on larger problems or on distributed-memory systems.
217+
@return @c ConstructSolver: The fastest direct linear solver instance found.
218+
The specific type depends on the compilation flags and availability.
137219
138-
Here are some considerations for choosing a solver:
220+
@exception Exception Thrown if, after checking all possibilities, no direct
221+
linear solver could be successfully constructed due to missing libraries or
222+
factory failures.
139223
140-
- If you are working on a distributed-memory parallel machine and dealing with large problems, you might want to consider MUMPS or SuperLU_DIST.
141-
- For smaller problems or problems with structure similar to circuit simulation problems, KLU might be a good choice.
142-
- Basker might be suitable for smaller problems where other solvers are not performing well.
143-
144-
Returns:
145-
ConstructSolver: The fastest available direct linear solver.
146-
147-
Raises:
148-
Exception: If no linear solver could be constructed.
149-
150-
Example:
151-
fast_solver = CreateFastestAvailableDirectLinearSolver()
224+
@par Example
225+
@code
226+
fast_solver = CreateFastestAvailableDirectLinearSolver()
227+
@endcode
152228
"""
153-
linear_solvers_by_speed = [
154-
"mumps", # TODO: Which first?, MUMPS of SuperLUDist?
155-
"super_lu_dist",
156-
"klu",
157-
"basker"
158-
]
159-
160-
registered_names_solvers_amesos = {
161-
"mumps" : "Amesos_Mumps",
162-
"super_lu_dist" : "Amesos_Superludist",
163-
"klu" : "Amesos_Klu"
164-
}
165-
166-
registered_names_solvers_amesos2 = {
167-
"mumps" : "amesos2_mumps",
168-
"super_lu_dist" : "amesos2_superludist",
169-
"klu" : "amesos2_klu2",
170-
"basker" : "basker"
171-
}
229+
linear_solvers_by_speed = KratosTrilinos.TrilinosSparseSpace.FastestDirectSolverList()
172230

173231
# Settings
174232
settings = KM.Parameters("""{"solver_type" : ""}""")
175233
for solver_name in linear_solvers_by_speed:
176-
if hasattr(KratosTrilinos, 'Amesos2Solver'):
177-
registered_name = registered_names_solvers_amesos2[solver_name]
178-
if KratosTrilinos.Amesos2Solver.HasSolver(registered_name):
179-
settings["solver_type"].SetString("amesos2")
180-
settings.AddEmptyValue("amesos2_solver_type")
181-
settings["amesos2_solver_type"].SetString(registered_name)
182-
return ConstructSolver(settings)
183-
elif hasattr(KratosTrilinos, 'AmesosSolver'):
184-
registered_name = registered_names_solvers_amesos[solver_name]
185-
if KratosTrilinos.AmesosSolver.HasSolver(registered_name):
186-
settings["solver_type"].SetString(solver_name)
187-
return ConstructSolver(settings)
234+
if _CheckIfSolverIsCompiled(solver_name):
235+
KM.Logger.PrintInfo("Trilinos-Linear-Solver-Factory", 'Using "' + solver_name + '" as the fastest available direct linear solver.')
236+
settings["solver_type"].SetString(solver_name)
237+
return ConstructSolver(settings)
188238

189239
raise Exception("Linear-Solver could not be constructed!")

applications/TrilinosApplication/trilinos_space.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,32 @@ class TrilinosSpace
873873
/**
874874
* @brief Returns a list of the fastest direct solvers.
875875
* @details This function returns a vector of strings representing the names of the fastest direct solvers. The order of the solvers in the list may need to be updated and reordered depending on the size of the equation system.
876+
* In Trilinos, the speed of direct solvers like MUMPS, SuperLU_DIST, KLU, and Basker can depend on various factors including the size and sparsity of the matrix, the architecture of the computer system, and the specific characteristics of the problem being solved. Below are some general observations about these solvers:
877+
*
878+
* 1. MUMPS (MUltifrontal Massively Parallel Sparse Direct Solver):
879+
* - Parallel solver, handles large problems well.
880+
* - Can be used on distributed-memory machines.
881+
* - May have higher memory requirements.
882+
*
883+
* 2. SuperLU_DIST:
884+
* - Parallel solver, also designed for distributed-memory machines.
885+
* - Often used for large, sparse linear systems.
886+
* - Has good performance on a wide range of problems.
887+
*
888+
* 3. KLU:
889+
* - A serial solver, typically used for smaller problems.
890+
* - Works well for circuit simulation problems and other problems with a similar structure.
891+
* - Generally not suitable for large distributed-memory parallel computations.
892+
* 4. Basker:
893+
* - Also a serial solver, suitable for smaller problems.
894+
* - May not perform as well on larger problems or on distributed-memory systems.
895+
*
896+
* Here are some considerations for choosing a solver:
897+
*
898+
* - If you are working on a distributed-memory parallel machine and dealing with large problems, you might want to consider MUMPS or SuperLU_DIST.
899+
* - For smaller problems or problems with structure similar to circuit simulation problems, KLU might be a good choice.
900+
* - Basker might be suitable for smaller problems where other solvers are not performing well.
901+
* @todo A proper study of speed must be done, particularly depending of system size would be interesting
876902
* @return A vector of strings containing the names of the fastest direct solvers.
877903
*/
878904
inline static std::vector<std::string> FastestDirectSolverList()
@@ -1441,4 +1467,4 @@ class TrilinosSpace
14411467

14421468
///@}
14431469

1444-
} // namespace Kratos.
1470+
} // namespace Kratos.

0 commit comments

Comments
 (0)