Il est fastidieux pour l'utilisateur de spécifier chaque aspect d'un algorithme. Si l'algorithme autorise les composants imbriqués, aucun nombre fini d'options ne serait suffisant. Par conséquent, il est essentiel que les options ne remontent pas nécessairement au niveau supérieur, comme dans le cas d'arguments explicites ou de paramètres de modèle. C'est ce qu'on appelle parfois le «problème de configuration» en génie logiciel. Je crois que PETSc dispose d'un système unique et puissant pour la gestion de la configuration. Il est similaire au modèle Service Locator dans l'essai de Martin Fowler sur l'inversion de contrôle .
Le système de configuration de PETSc fonctionne grâce à une combinaison de configurations spécifiées par l'utilisateur gérées par les objets du solveur (avec des requêtes get et set) et la base de données d'options. Tout composant de la simulation peut déclarer une option de configuration, une valeur par défaut et un emplacement pour placer le résultat. Les objets imbriqués ont des préfixes qui peuvent être composés, de sorte que chaque objet nécessitant une configuration peut être adressé indépendamment. Les options elles-mêmes peuvent être lues à partir de la ligne de commande, de l'environnement, des fichiers de configuration ou du code. Lorsqu'une option est déclarée, une chaîne d'aide et une page de manuel sont spécifiées, afin que l' -help
option soit compréhensible et pour qu'une interface graphique correctement liée puisse être écrite.
L'utilisateur appelle une SetFromOptions
méthode pour configurer un objet lui-même en fonction des options de ligne de commande. L'appel de cette fonction est facultatif et peut ne pas être appelé si l'utilisateur (personne qui écrit du code qui appelle PETSc) expose les options via une autre interface. Nous recommandons fortement à l'utilisateur d'exposer la base de données d'options car cela donne à l'utilisateur final (personne exécutant l'application) beaucoup de pouvoir, mais ce n'est pas nécessaire.
Une configuration typique, appelée via
PetscObjectOptionsBegin(object); /* object has prefix and descriptive string */
PetscOptionsReal("-ts_atol", /* options database key */
"Absolute tolerance for local truncation error", /* long description */
"TSSetTolerances", /* function and man page on topic */
ts->atol, /* current/default value *?
&ts->atol, /* place to store value */
&option_set); /* TRUE if the option was set */
PetscOptionsList("-ts_type","Time stepping method","TSSetType",TSList,
defaultType,typeName,sizeof typeName,&option_set);
TSAdaptSetFromOptions(ts->adapt); /* configures adaptive controller method */
/* ... many others */
/* ... the following is only called from implicit implementations */
SNESSetFromOptions(ts->snes); /* configure nonlinear solver. */
PetscOptionsEnd();
Remarques:
PetscOptionsList()
présente à l'utilisateur un choix dans une liste dynamique. Il existe une architecture de plugin que les nouvelles implémentations peuvent utiliser pour s'exposer en tant que première classe aux appelants. (Ces implémentations peuvent être placées dans des bibliothèques partagées et utilisées comme première classe sans recompiler les programmes.)
SNESSetFromOptions()
configure récursivement les solveurs linéaires, les préconditionneurs et tous les autres composants qui nécessitent une configuration.
Comme premier point, je ferais l'algorithme ET le logiciel le plus général possible. J'ai appris ça à la dure.
Disons que vous commencez avec un cas de test simple. Vous pouvez le faire plus rapidement. Mais ensuite, si vous avez rendu le logiciel trop spécifique (trop peu de paramètres) pour ce cas initial, vous perdrez de plus en plus de temps à l'adapter à chaque fois que vous ajouterez un nouveau degré de liberté. Ce que je fais maintenant, c'est passer plus de temps au début à rendre la chose assez générale et à augmenter la variation des paramètres à mesure que j'avance.
Cela implique plus de tests depuis le début car vous aurez plus de paramètres à partir du point de départ, mais cela signifie que vous pouvez jouer beaucoup avec l'algorithme à zéro ou à un coût très faible.
Exemple: l'algorithme consiste à calculer l'intégrale de surface du produit scalaire de deux fonctions vectorielles. Ne présumez pas dès le début la taille, la géométrie et la discrétisation de la surface si vous souhaitez à l'avenir changer cela. Faites une fonction de produit scalaire, rendez la surface aussi générale que possible, calculez l'intégrale d'une manière formelle agréable. Vous pouvez tester chaque fonction que vous créez séparément.
Au début, vous pouvez et commencer à intégrer des géométries simples et à déclarer au début certains paramètres sous forme de constantes. Au fil du temps, si vous voulez changer la géométrie, vous pouvez le faire facilement. Si vous aviez fait des hypothèses au début, vous auriez à changer le code entier à chaque fois.
la source