Es ist für den Benutzer umständlich, jeden Aspekt eines Algorithmus anzugeben. Wenn der Algorithmus verschachtelte Komponenten zulässt, ist keine endliche Anzahl von Optionen ausreichend. Daher ist es wichtig, dass Optionen nicht unbedingt auf die oberste Ebene "aufsprudeln", wie dies bei expliziten Argumenten oder Vorlagenparametern der Fall ist. Dies wird in der Softwareentwicklung manchmal als "Konfigurationsproblem" bezeichnet. Ich glaube, PETSc verfügt über ein einzigartig leistungsfähiges System für das Konfigurationsmanagement. Es ähnelt dem Service Locator-Muster in Martin Fowlers Aufsatz über die Umkehrung der Kontrolle .
Das Konfigurationssystem von PETSc basiert auf einer Kombination aus benutzerdefinierten Konfigurationen, die von den Solver-Objekten (mit Abfragen zum Abrufen und Festlegen) und der Optionsdatenbank verwaltet werden. Jede Komponente der Simulation kann eine Konfigurationsoption, einen Standardwert und einen Ort für das Ergebnis deklarieren. Verschachtelte Objekte haben Präfixe, die zusammengesetzt werden können, sodass jedes Objekt, das konfiguriert werden muss, unabhängig adressiert werden kann. Die Optionen selbst können über die Befehlszeile, die Umgebung, die Konfigurationsdateien oder über Code gelesen werden. Wenn eine Option deklariert wird, werden eine Hilfezeichenfolge und eine Manpage angegeben, damit die -help
Option verständlich ist und eine ordnungsgemäß verknüpfte GUI geschrieben werden kann.
Der Benutzer ruft eine SetFromOptions
Methode auf, mit der sich ein Objekt anhand der Befehlszeilenoptionen selbst konfigurieren kann. Der Aufruf dieser Funktion ist optional und kann möglicherweise nicht aufgerufen werden, wenn der Benutzer (der Code schreibt, der PETSc aufruft) die Optionen über eine andere Schnittstelle bereitstellt. Es wird dringend empfohlen, dass der Benutzer die Optionsdatenbank verfügbar macht, da dies dem Endbenutzer (der die Anwendung ausführt) viel Macht verleiht, dies ist jedoch nicht erforderlich.
Eine typische Konfiguration, die über aufgerufen wird
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();
Anmerkungen:
PetscOptionsList()
präsentiert dem Benutzer eine Auswahl aus einer dynamischen Liste. Es gibt eine Plugin-Architektur, mit der sich neue Implementierungen als erstklassig für Aufrufer ausgeben können. (Diese Implementierungen können in gemeinsam genutzten Bibliotheken abgelegt und als erstklassig verwendet werden, ohne dass Programme neu kompiliert werden müssen.)
SNESSetFromOptions()
Konfiguriert rekursiv die linearen Löser, Vorkonditionierer und alle anderen Komponenten, die konfiguriert werden müssen.
Als erstes würde ich den Algorithmus und die Software so allgemein wie möglich machen. Ich habe das auf die harte Tour gelernt.
Angenommen, Sie beginnen mit einem einfachen Testfall. Das geht schneller. Wenn Sie jedoch die Software für diesen ersten Fall zu spezifisch (zu wenige Parameter) gemacht haben, verlieren Sie immer mehr Zeit, sie jedes Mal anzupassen, wenn Sie einen neuen Freiheitsgrad hinzufügen. Was ich jetzt tue, ist, dass ich am Anfang mehr Zeit damit verbringe, das Ding ziemlich allgemein zu machen und die Variation der Parameter zu erhöhen, während ich mich vorwärts bewege.
Dies erfordert von Anfang an mehr Tests, da Sie vom Startpunkt an mehr Parameter haben. Dies bedeutet jedoch, dass Sie mit dem Algorithmus bei Null oder sehr niedrigen Kosten viel spielen können.
Beispiel: Der Algorithmus berechnet das Oberflächenintegral des Skalarprodukts zweier Vektorfunktionen. Nehmen Sie nicht von Anfang an die Größe, Geometrie und Diskretisierung der Oberfläche an, wenn Sie dies in Zukunft ändern möchten. Machen Sie eine Skalarproduktfunktion, machen Sie die Oberfläche so allgemein wie möglich, berechnen Sie das Integral auf formale Weise. Sie können jede Funktion einzeln testen.
Zu Beginn können Sie beginnen, über einfache Geometrien zu integrieren, und am Anfang können Sie Parameter als Konstanten deklarieren. Wenn Sie die Geometrie im Laufe der Zeit ändern möchten, können Sie dies problemlos tun. Hätten Sie zu Beginn Annahmen getroffen, müssten Sie den gesamten Code jedes Mal ändern.
quelle