Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,53 @@
import KratosMultiphysics.TrilinosApplication as KratosTrilinos

"""
This module provides functionality for creating a linear solver for use with the Trilinos application.
It includes checking and converting deprecated solver types, checking if the desired solver is compiled, and creating the fastest available direct linear solver.

Importing necessary Kratos modules:
- `KratosMultiphysics` as `KM`
- `KratosMultiphysics.TrilinosApplication` as `KratosTrilinos`

Functions:
- `_CheckIfTypeIsDeprecated(config)`: Checks and translates deprecated solver names to new names for backward compatibility.
- `_CheckIfSolverIsCompiled(solver_type)`: Checks if the desired solver is compiled.
- `ConstructSolver(configuration)`: Constructs the solver based on the given configuration.
- `CreateFastestAvailableDirectLinearSolver()`: Creates the fastest available direct linear solver.

Example:
config = KM.Parameters({
"solver_type": "CGSolver"
})
solver = ConstructSolver(config)
@package trilinos_solver_factory

@brief Module for creating Trilinos-based linear solvers in Kratos.

This module provides essential functionality for configuring and instantiating
linear solvers specifically designed for the Kratos Trilinos application.
It ensures robustness and compatibility by:
* Checking and automatically updating deprecated solver types.
* Verifying the availability of desired solvers based on the Kratos compilation.
* Providing a utility to automatically select the most efficient direct solver.

@section Module_Functions Public and Internal Functions
* @c _CheckIfTypeIsDeprecated(config): Internal function to check and translate deprecated solver names.
* @c _CheckIfSolverIsCompiled(solver_type): Internal function to verify if the solver is available in the compilation.
* @c ConstructSolver(configuration): Primary function to construct a solver based on user configuration.
* @c CreateFastestAvailableDirectLinearSolver(): Convenience function to get the best available direct solver.

@par Example Usage
@code
import KratosMultiphysics as KM
from . import trilinos_solver_factory # Assuming this module is here

config = KM.Parameters({
"solver_type": "CGSolver",
# ... other settings
})
solver = trilinos_solver_factory.ConstructSolver(config)
@endcode
"""

def _CheckIfTypeIsDeprecated(config):
"""
@brief Checks for deprecated solver types in the configuration.

This function checks if the given solver type in the configuration is deprecated.
If the type is deprecated, a warning is printed and the solver type in the configuration is replaced with the new name.
If the type is deprecated, a warning is printed to the console, and the solver type
in the configuration is automatically replaced with the new, non-deprecated name
to ensure forward compatibility.

@param config KM.Parameters: The configuration parameters object containing the solver settings.

Args:
config (KM.Parameters): The configuration parameters for the solver.
@return None

Example:
_CheckIfTypeIsDeprecated(config)
@par Example
@code
_CheckIfTypeIsDeprecated(config)
@endcode
"""
solver_type = config["solver_type"].GetString()

Expand All @@ -60,130 +77,155 @@ def _CheckIfTypeIsDeprecated(config):

def _CheckIfSolverIsCompiled(solver_type):
"""
This function checks if the specified solver type is compiled.
@brief Checks if a specific solver type is available in the KratosTrilinos compilation.

This function verifies whether the specified solver type, identified by a string,
has been compiled and is accessible within the KratosTrilinos environment.

Args:
solver_type (str): The type of the solver to check.
@param solver_type The type of the solver to check, provided as a string.

Raises:
Exception: If the solver type is not compiled.
@return @c bool: Returns @c True if the solver is both available and compiled; otherwise, @c False.

Example:
_CheckIfSolverIsCompiled("aztec")
@exception Exception Thrown if the required solver factory, which is responsible
for creating solvers of this type, has been completely disabled during the Kratos
compilation process.
"""
if solver_type in ["aztec", "cg", "bicgstab", "gmres"] and not hasattr(KratosTrilinos, 'AztecSolver'):
raise Exception('Trying to use Aztec-solver, which was disabled at compile time with "TRILINOS_EXCLUDE_AZTEC_SOLVER"!')
if solver_type in ["amesos", "klu", "super_lu_dist", "mumps"]:
has_amesos = hasattr(KratosTrilinos, 'AmesosSolver')
has_amesos_2 = hasattr(KratosTrilinos, 'Amesos2Solver')
if not has_amesos_2:
if has_amesos:
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Amesos2 not compiled but Amesos it is, recommended for better performance")
else:
raise Exception('Trying to use Amesos-solver, which was disabled at compile time with "TRILINOS_EXCLUDE_AMESOS_SOLVER"!')
if solver_type in ["multi_level"] and not hasattr(KratosTrilinos, 'MultiLevelSolver'):
raise Exception('Trying to use MultiLevelSolver-solver, which was diasbled at compile time with "TRILINOS_EXCLUDE_ML_SOLVER"!')
# --- 1. Define Solver Groups ---
aztec_solvers = {"aztec", "cg", "bicgstab", "gmres"}
ml_solvers = {"multi_level"}

# --- 2. Check Aztec Solvers ---
if solver_type in aztec_solvers:
if not hasattr(KratosTrilinos, 'AztecSolver'):
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Trying to use Aztec-solver, which was disabled at compile time with TRILINOS_EXCLUDE_AZTEC_SOLVER!")
return False
return True

# --- 3. Check MultiLevel Solvers ---
if solver_type in ml_solvers:
if not hasattr(KratosTrilinos, 'MultiLevelSolver'):
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Trying to use MultiLevelSolver, which was disabled at compile time with TRILINOS_EXCLUDE_ML_SOLVER!")
return False
return True

# --- 4. Check Amesos (Direct) Solvers ---
# This set includes generic names and specific implementation names
amesos_solvers = {
"amesos", "amesos2", "klu", "klu2",
"super_lu_dist", "super_lu_dist2",
"mumps", "mumps2", "basker"
}

if solver_type in amesos_solvers:
has_amesos1 = hasattr(KratosTrilinos, 'AmesosSolver')
has_amesos2 = hasattr(KratosTrilinos, 'Amesos2Solver')

# Case A: Neither is compiled
if not has_amesos1 and not has_amesos2:
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Trying to use Amesos-solver, which was disabled at compile time with TRILINOS_EXCLUDE_AMESOS_SOLVER!")
return False

# Case B: Only Amesos1 is compiled (Warning for performance)
if has_amesos1 and not has_amesos2:
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "Amesos2 not compiled but Amesos is. Recommended to compile Amesos2 for better performance.")
# 'basker' is strictly an Amesos2 solver, so it fails here
if solver_type == "basker":
KM.Logger.PrintWarning("Trilinos-Linear-Solver-Factory", "basker is strictly an Amesos2 solver")
return False

# Case C: specific internal solver check
# Map inputs to (required_factory, internal_trilinos_name)
requires_amesos2 = solver_type.endswith("2") or solver_type == "basker"

# Check availability based on required factory
if requires_amesos2:
if not has_amesos2:
return False

# Map external name -> internal Trillinos name
map_amesos2 = {
"mumps2": "amesos2_mumps",
"super_lu_dist2": "amesos2_superludist",
"klu2": "amesos2_klu2",
"basker": "basker"
}
# If it's a generic name like "amesos2", we assume True, otherwise check specific availability
internal_name = map_amesos2.get(solver_type)
if internal_name and not KratosTrilinos.Amesos2Solver.HasSolver(internal_name):
return False
else: # requires Amesos1
if not has_amesos1:
return False

map_amesos1 = {
"mumps": "Amesos_Mumps",
"super_lu_dist": "Amesos_Superludist",
"klu": "Amesos_Klu"
}
internal_name = map_amesos1.get(solver_type)
if internal_name and not KratosTrilinos.AmesosSolver.HasSolver(internal_name):
return False

return True

def ConstructSolver(configuration):
"""
Constructs and returns a Trilinos linear solver based on the given configuration.
@brief Constructs a Trilinos linear solver instance.

This function takes a configuration object and uses the KratosTrilinos factory
to create and return a fully configured Trilinos linear solver instance.

Args:
configuration (KM.Parameters): The configuration parameters for the solver.
@param configuration KM.Parameters: The configuration parameters object containing
all necessary settings for the linear solver (e.g., type, tolerance, maximum iterations).

Returns:
KratosTrilinos.TrilinosLinearSolverFactory().Create: A Trilinos linear solver.
@return @c KratosTrilinos.TrilinosLinearSolverFactory().Create: A newly constructed
Trilinos linear solver object ready for use in simulations.

Raises:
Exception: If the input is not a Kratos Parameters object.
@exception Exception Thrown if the input \p configuration is not a valid
Kratos Parameters object.

Example:
solver = ConstructSolver(config)
@par Example
@code
solver = ConstructSolver(config)
@endcode
"""
if not isinstance(configuration, KM.Parameters):
raise Exception("input is expected to be provided as a Kratos Parameters object")

_CheckIfTypeIsDeprecated(configuration) # for backwards-compatibility
_CheckIfSolverIsCompiled(configuration["solver_type"].GetString())
if not _CheckIfSolverIsCompiled(configuration["solver_type"].GetString()):
raise Exception(configuration["solver_type"].GetString() + " solver is not available")

return KratosTrilinos.TrilinosLinearSolverFactory().Create(configuration)

def CreateFastestAvailableDirectLinearSolver():
"""
Creates and returns the fastest available direct linear solver, based on a predefined order and availability.

TODO: A proper study of speed must be done, particularly depending of system size would be interesting

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:

1. MUMPS (MUltifrontal Massively Parallel Sparse Direct Solver):
- Parallel solver, handles large problems well.
- Can be used on distributed-memory machines.
- May have higher memory requirements.

2. SuperLU_DIST:
- Parallel solver, also designed for distributed-memory machines.
- Often used for large, sparse linear systems.
- Has good performance on a wide range of problems.

3. KLU:
- A serial solver, typically used for smaller problems.
- Works well for circuit simulation problems and other problems with a similar structure.
- Generally not suitable for large distributed-memory parallel computations.
@brief Creates the fastest available direct linear solver.

4. Basker:
- Also a serial solver, suitable for smaller problems.
- May not perform as well on larger problems or on distributed-memory systems.
This function attempts to construct and return the most efficient direct linear
solver available in the current compilation, following a predefined priority
order (e.g., SuperLU, KLU, etc.) to ensure optimal performance.

Here are some considerations for choosing a solver:
@return @c ConstructSolver: The fastest direct linear solver instance found.
The specific type depends on the compilation flags and availability.

- If you are working on a distributed-memory parallel machine and dealing with large problems, you might want to consider MUMPS or SuperLU_DIST.
- For smaller problems or problems with structure similar to circuit simulation problems, KLU might be a good choice.
- Basker might be suitable for smaller problems where other solvers are not performing well.
@exception Exception Thrown if, after checking all possibilities, no direct
linear solver could be successfully constructed due to missing libraries or
factory failures.

Returns:
ConstructSolver: The fastest available direct linear solver.

Raises:
Exception: If no linear solver could be constructed.

Example:
fast_solver = CreateFastestAvailableDirectLinearSolver()
@par Example
@code
fast_solver = CreateFastestAvailableDirectLinearSolver()
@endcode
"""
linear_solvers_by_speed = [
"mumps", # TODO: Which first?, MUMPS of SuperLUDist?
"super_lu_dist",
"klu",
"basker"
]

registered_names_solvers_amesos = {
"mumps" : "Amesos_Mumps",
"super_lu_dist" : "Amesos_Superludist",
"klu" : "Amesos_Klu"
}

registered_names_solvers_amesos2 = {
"mumps" : "amesos2_mumps",
"super_lu_dist" : "amesos2_superludist",
"klu" : "amesos2_klu2",
"basker" : "basker"
}
linear_solvers_by_speed = KratosTrilinos.TrilinosSparseSpace.FastestDirectSolverList()

# Settings
settings = KM.Parameters("""{"solver_type" : ""}""")
for solver_name in linear_solvers_by_speed:
if hasattr(KratosTrilinos, 'Amesos2Solver'):
registered_name = registered_names_solvers_amesos2[solver_name]
if KratosTrilinos.Amesos2Solver.HasSolver(registered_name):
settings["solver_type"].SetString("amesos2")
settings.AddEmptyValue("amesos2_solver_type")
settings["amesos2_solver_type"].SetString(registered_name)
return ConstructSolver(settings)
elif hasattr(KratosTrilinos, 'AmesosSolver'):
registered_name = registered_names_solvers_amesos[solver_name]
if KratosTrilinos.AmesosSolver.HasSolver(registered_name):
settings["solver_type"].SetString(solver_name)
return ConstructSolver(settings)
if _CheckIfSolverIsCompiled(solver_name):
KM.Logger.PrintInfo("Trilinos-Linear-Solver-Factory", 'Using "' + solver_name + '" as the fastest available direct linear solver.')
settings["solver_type"].SetString(solver_name)
return ConstructSolver(settings)

raise Exception("Linear-Solver could not be constructed!")
28 changes: 27 additions & 1 deletion applications/TrilinosApplication/trilinos_space.h
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,32 @@ class TrilinosSpace
/**
* @brief Returns a list of the fastest direct solvers.
* @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.
* 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:
*
* 1. MUMPS (MUltifrontal Massively Parallel Sparse Direct Solver):
* - Parallel solver, handles large problems well.
* - Can be used on distributed-memory machines.
* - May have higher memory requirements.
*
* 2. SuperLU_DIST:
* - Parallel solver, also designed for distributed-memory machines.
* - Often used for large, sparse linear systems.
* - Has good performance on a wide range of problems.
*
* 3. KLU:
* - A serial solver, typically used for smaller problems.
* - Works well for circuit simulation problems and other problems with a similar structure.
* - Generally not suitable for large distributed-memory parallel computations.
* 4. Basker:
* - Also a serial solver, suitable for smaller problems.
* - May not perform as well on larger problems or on distributed-memory systems.
*
* Here are some considerations for choosing a solver:
*
* - If you are working on a distributed-memory parallel machine and dealing with large problems, you might want to consider MUMPS or SuperLU_DIST.
* - For smaller problems or problems with structure similar to circuit simulation problems, KLU might be a good choice.
* - Basker might be suitable for smaller problems where other solvers are not performing well.
* @todo A proper study of speed must be done, particularly depending of system size would be interesting
* @return A vector of strings containing the names of the fastest direct solvers.
*/
inline static std::vector<std::string> FastestDirectSolverList()
Expand Down Expand Up @@ -1441,4 +1467,4 @@ class TrilinosSpace

///@}

} // namespace Kratos.
} // namespace Kratos.
Loading