transentis consulting

Eine Python Bibliothek für System Dynamics

System Dynamics Simulationen Interaktiv in Jupyter mit Python erstellen
  • Oliver Grasl
    Oliver Grasl
    Monday, October 1, 2018
Ein Überblick über die Erstellung eines System Dynamics Modells unter Verwendung des BPTK-Py-Frameworks

System Dynamics Simulationen interaktiv in Jupyter mit Python erstellen

Wir lieben es, Computermodelle zu erstellen und unsere Lieblingsumgebungen für diese Art von explorativer analytischer Arbeit sind Jupyter und Python.
Um die Computational Modeling zu erleichtern, entwickeln wir ein einfaches Framework, das derzeit System Dynamics und Agentbasierte Modeling unterstützt.
Wir haben das BPTK-Py-Framework zum ersten Mal in unserem letzten Blogpost Computational Essays auf Basis von Simulationsmodellen schreiben vorgestellt.
Seitdem haben wir einige neue Funktionen erstellt, mit denen Sie System Dynamics Modelle und agentenbasierte Modelle interaktiv in Jupyter mit Python erstellen können. Um die Modellerstellung so einfach wie möglich zu gestalten, haben wir eine einfache, domänenspezifische Sprache (DSL) erstellt, die sowohl System Dynamics als auch agentenbasierte Modellierung unterstützt und einen Großteil der zugrunde liegenden Komplexität von Computational Models verbirgt.
Mit dieser Sprache können Sie nicht nur System Dynamics Modelle und agentenbasierte Modelle erstellen, sondern Sie können sogar beide mischen, um "hybride" Simulationsmodelle zu erstellen.
Eine solche DSL zu haben, ist aus mehreren Gründen nützlich:
  • Erstellen Sie Modelle interaktiv in Jupyter, wodurch der Modellierungsprozess sehr effektiv wird.
  • Python-Anfänger können sich auf die Modellierung konzentrieren, ohne dass sie viel über Python wissen müssen.
  • Python-Experten können ihre Modelle mit anderen analytischen Frameworks mischen, z. B. mit Machine Learning Toolkits.
Natürlich fügt sich die neue Funktionalität problemlos in den Rest des BPTK-Py-Frameworks ein, sodass Sie die Vorteile aller High-Level-Szenario-Management- und Diagramm-Funktionen, die Teil des Frameworks sind, nutzen können.
In diesem Beitrag konzentriere ich mich auf die Erstellung eines System Dynamics Modells unter Verwendung des Frameworks, ich werde in einem späteren Beitrag einen Blick auf die agentenbasierte Modellierung werfen.
Der Beitrag ist auch als Jupyter-Notebook in unserem BPTK-Py-Tutorial verfügbar, das hier zum Download bereitsteht.

Ein einfaches Modell zur Demonstration der Bibliothek

Zur Veranschaulichung der DSL werden wir das einfache Projektmanagementmodell aufbauen, das wir in unserem Schritt-für-Schritt-Tutorial in System Dynamics vorgestellt haben.
Das Projektmanagementmodell ist wirklich einfach und enthält nur ein paar Bestände, Flüsse und Konverter, wie Sie im folgenden Diagramm sehen können:
Selbst wenn Sie also das Modell nicht kennen, sollten Sie diesem Beitrag sehr leicht folgen können.
Um loszulegen, müssen wir zunächst die Bibliothek und insbesondere die SD Funktionsbibliothek in unser Notebook importieren.
from BPTK_Py import Model from BPTK_Py import sd_functions as sd
Die SD Funktionsbibliothek enthält die Funktionen und Operatoren, die zur Definition von Modellgleichungen benötigt werden (dies sind die Built-Ins, die Sie aus Ihren visuellen Modellierungsumgebungen wie Stella oder Vensim kennen). Da die Bibliothek Funktionen wie min und max enthält, die auch Teil der Python-Standardbibliothek sind, importieren wir die SD Funktionsbibliothek mit dem Präfix sd, um Namenskonflikte zu vermeiden.
Als nächstes erstellen wir ein Modell mit der Klasse Model. Unser Modell wird alle unsere Modellelemente wie Bestände, Flüsse, Konverter und Konstanten enthalten.
model =
Model(starttime=0,stoptime=120,dt=1,name='SimpleProjectManagament')
Das Erstellen von Modellelementen ist wirklich einfach:
openTasks = model.stock("openTasks")
Wie Sie sehen können, besteht unsere Konvention darin, die für die Modellelemente die Camel Case-Namenskonvention zu verwenden und für Python Variablen für die gleichnamigen Elemente zu erstellen.
Sobald ein Modellelement auf diese Weise definiert wurde, brauchen wir uns nur noch auf die Python Variable zu beziehen und müssen nicht mehr auf das Modellelement verweisen (d. h. wir können openTasks in unseren Gleichungen verwenden, im Gegensatz zur Verwendung von model.stock("openTasks")). Dies spart eine Menge Tipparbeit.
Lassen Sie uns nun auch die anderen Modellelemente und Variablen definieren, damit wir uns anschließend auf die Gleichungen konzentrieren können:
closedTasks = model.stock("closedTasks") staff = model.stock("staff") completionRate = model.flow("completionRate") currentTime = model.converter("currentTime") remainingTime = model.converter("remainingTime") schedulePressure = model.converter("schedulePressure") productivity = model.converter("productivity") deadline = model.constant("deadline") effortPerTask = model.constant("effortPerTask") initialStaff = model.constant("initialStaff") initialOpenTasks = model.constant("initialOpenTasks")
Beachten Sie, dass wir in unseren Modellen zwischen Konstanten und Konvertern unterscheiden - dies ist aus Sicht von System Dynamics nicht unbedingt notwendig, erleichtert aber die Überprüfung des Modells auf Fehler.
Nun wollen wir unsere Bestände initialisieren - dazu müssen wir nur die Eigenschaft initial_value der Bestände setzen. Diese Eigenschaft kann entweder eine numerische Konstante oder ein konstantes Element sein.
closedTasks.initial_value = 0 staff.initial_value = initialStaff openTasks.initial_value = initialOpenTasks
Die Definition der Modellgleichungen ist wirklich einfach: Jede Modellvariable hat eine Gleichungseigenschaft, die Gleichung selbst wird ähnlich wie in einer visuellen Modellierungsumgebung geschrieben, wobei die anderen Modellvariablen nach Bedarf verwendet werden.
Die Definition von Konstanten ist besonders einfach:
deadline.equation = 100 effortPerTask.equation = 1 initialStaff.equation = 1 initialOpenTasks.equation = 100
Die Variable currentTime verfolgt die Simulationszeit, die von der Funktion time in der SD Funktionsbibliothek erfasst wird.
currentTime.equation=sd.time()
Die remainingTime ist gleich der Differenz zwischen der deadline und der currentTime:
remainingTime.equation = deadline - currentTime
Sie sehen also, dank der DSL ist das Definieren von Gleichungen sehr intuitiv!
Die Gleichungen für die Bestände sind ebenfalls sehr einfach - sie enthalten nur die Einflüsse (mit einem positiven Zeichen) und die Abflüsse (mit einem negativen Zeichen).
Ein kurzer Blick auf das obige Diagramm zeigt uns, dass die openTasks nur einen Abfluss (definiert durch die completionRate) und die closedTasks nur einen Einfluss (ebenfalls definiert durch die completionRate) haben:
openTasks.equation = -completionRate closedTasks.equation = completionRate
Der schedulePressure ist ein dimensionsloses Verhältnis, das den erforderlichen Aufwand zur Erledigung aller verbleibenden offenen Aufgaben mit der verbleibenden Arbeitskapazität vergleicht.
Wir verwenden die Funktionen min und max aus der SD Funktionsbibliothek, um sicherzustellen, dass keine Division durch Null erfolgt und dass der Zeitplandruck auf 2,5 begrenzt wird:
schedulePressure.equation = sd.min( (openTasks*effortPerTask)/ (staff*sd.max(remainingTime,1)), 2.5 )
Wir definieren die Produktivität in unserem Modell über eine nicht lineare Beziehung (abhängig vom Zeitplandruck). Wir erfassen diese Beziehung in einer Look-up-Tabelle, die wir in der points Eigenschaft des Modells speichern (unter Verwendung einer Python Liste):
model.points["productivity"] = [ [0,0.4], [0.25,0.444], [0.5,0.506], [0.75,0.594], [1,1], [1.25,1.119], [1.5,1.1625], [1.75,1.2125], [2,1.2375], [2.25,1.245], [2.5,1.25] ]
Wir können die Look-up-Tabelle einfach darstellen, um zu sehen, ob sie die richtige Form hat:
model.plot_lookup("productivity")
Die Produktivitätsgleichung wird dann über eine Lookup-Funktion definiert - in unserem Fall hängt die productivity nichtlinear von schedulePressure ab, wie in der Lookup-Tabelle definiert:
productivity.equation = sd.lookup(schedulePressure,"productivity")
Die letzte Gleichung, die wir definieren müssen, ist die completionRate - die Fertigstellungsrate ist definiert durch die Anzahl der am Projekt arbeitenden Mitarbeiter geteilt durch den Aufwand pro Aufgabe. Dies multiplizieren wir dann mit der (durchschnittlichen) Produktivität der Mitarbeiter. Die Fertigstellungsrate darf nie größer sein als die Anzahl der openTasks, daher schränken wir sie mit der Funktion min ein.
completionRate.equation = sd.max(0, sd.min(openTasks, staff*(productivity/effortPerTask) ) )
Nachdem wir nun alle notwendigen Gleichungen definiert haben, sind wir bereit, das Modell auszuführen. Am einfachsten ist es, eine Modellvariable zu einem bestimmten Zeitschritt auszuwerten - dieser Ansatz ist besonders nützlich, wenn Sie das Modell interaktiv erstellen (z. B. in einem Jupyter-Notebook) und Zwischenergebnisse testen.
closedTasks(80), closedTasks(100), closedTasks(120) (80.0, 100.0, 100.0)
Lassen Sie uns mit verschiedenen Einstellungen für die Frist spielen:
deadline.equation = 120 closedTasks(80), closedTasks(100), closedTasks(120) (63.33020661244643, 81.06644489208418, 99.99777243819346)
deadline.equation=80 closedTasks(80), closedTasks(100), closedTasks(120) (92.6853060260874, 100.00000000000004, 100.00000000000004)
Natürlich können wir die Variablen auch direkt in einem Diagramm darstellen, indem wir die plot() Methode des Elements verwenden.
closedTasks.plot()
Jetzt, da wir ein Modell haben, können wir das leistungsfähige Szenario-Management nutzen, die in das BPTK-Py-Framework eingebaut ist.
import BPTK_Py bptk = BPTK_Py.bptk()
Dann richten wir einen Szenario-Manager mit einem Python Dictionary ein. Der Szenario-Manager identifiziert die Basiskonstanten des Modells:
scenario_manager = { "smSimpleProjectManagementDSL":{ "model": model, "base_constants": { "deadline": 100, "initialStaff": 1, "effortPerTask": 1, "initialOpenTasks": 100, }, "base_points":{ "productivity": [ [0,0.4], [0.25,0.444], [0.5,0.506], [0.75,0.594], [1,1], [1.25,1.119], [1.5,1.1625], [1.75,1.2125], [2,1.2375], [2.25,1.245], [2.5,1.25] ] } } }
Der Szenario-Manager muss wie folgt registriert werden:
bptk.register_scenario_manager(scenario_manager)
Sobald wir dies haben, können wir (ein oder mehrere) Szenarien wie folgt definieren und registrieren:
bptk.register_scenarios( scenarios = { "scenario80": { "constants": { "initialOpenTasks": 80 } } } , scenario_manager="smSimpleProjectManagementDSL")
Wir können dann das Szenario wie folgt darstellen:
bptk.plot_scenarios( scenarios="scenario80", scenario_managers="smSimpleProjectManagementDSL", equations="openTasks")
Lassen Sie uns ein paar weitere Szenarien registrieren:
bptk.register_scenarios( scenarios = { "scenario100": { }, "scenario120": { "constants": { "initialOpenTasks" : 120 } } }, scenario_manager="smSimpleProjectManagementDSL")
scenario100 entspricht den Basiseinstellungen, daher müssen wir keine Einstellungen dafür definieren.
Jetzt können wir die Szenarien leicht vergleichen:
bptk.plot_scenarios( scenarios="scenario80,scenario100,scenario120", scenario_managers="smSimpleProjectManagementDSL", equations="openTasks", series_names={ "smSimpleProjectManagementDSL_scenario80_openTasks":"scenario80", "smSimpleProjectManagementDSL_scenario100_openTasks":"scenario100", "smSimpleProjectManagementDSL_scenario120_openTasks":"scenario120" } )
Damit ist unser kurzer Rundgang durch die SD DSL innerhalb des Business Prototyping Toolkits abgeschlossen. Das BPTK-Framework ist unter der MIT-Lizenz auf PyPi verfügbar, sodass Sie es sofort einsetzen können.
Sie können ein Tutorial (das diesen Blogbeitrag als Jupyter-Notebook enthält) auf der BPTK-Produkt-Homepage herunterladen.
Das Tutorial zeigt auch einige fortgeschrittenere Techniken, insbesondere auch, wie Sie die SD DSL in Python nutzen können, ohne Jupyter zu verwenden.

Fazit

In diesem Beitrag wurde eine einfache domänenspezifische Sprache für System Dynamics vorgestellt, die in Python implementiert ist. Mit ihr können Sie System Dynamics in Python erstellen und die interaktive Modellierung in Jupyter unterstützen.
Das Erstellen von System Dynamics Modellen direkt in Python ist besonders nützlich, wenn Sie Ihre SD Modelle mit eigenen SD Funktionen erweitern oder Ihre Modelle mit anderen Computermodellen wie agentenbasierten Modellen oder mathematischen Modellen kombinieren möchten.
Upcoming Events

Training AI to Play The Beer Distribution Game

2 hours - 2021-10-27

Mastering The Complexity of Digital Transformation

2 hours - 2021-10-28

Modeling Growth Strategies For Professional Service Firms

2 hours - 2021-11-18

Go to event overview

Recent Blogposts

Meetup: Agile Transformation Tools

Olga Medvedeva - 2021-09-27

Meetup: Understanding The Beer Distribution Game

Olga Medvedeva - 2021-09-01

Introduction to The Business Prototyping Toolkit

Oliver Grasl - 2021-08-17

Go to blog overview

Email newsletter

Subscribe to our newsletter and stay up to date about the latest trends and news.

Name
Email
Subscribe to our newsletter and stay up to date about the latest trends and news.
  • transentis
Contact us

transentis consulting

Geisbergstraße 9

10777 Berlin


+49 30 9203833320

info@transentis.com