From 4cc8efe948885be5c5262cf97fd597df3095242f Mon Sep 17 00:00:00 2001 From: "Hock, Martin" <martin.hock@fst.tu-darmstadt.de> Date: Thu, 2 Mar 2023 06:19:40 +0100 Subject: [PATCH] Finish "minimal example". --- ausarbeitung.ipynb | 310 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 245 insertions(+), 65 deletions(-) diff --git a/ausarbeitung.ipynb b/ausarbeitung.ipynb index 264e2c7..b3e1f05 100644 --- a/ausarbeitung.ipynb +++ b/ausarbeitung.ipynb @@ -37,11 +37,22 @@ "## Eigene Module und Minimalbeispiel\n", "Für die Ausarbeitung nutzen wir zwei eigene Module (Python-Dateien), die im Ordner `functions` gespeichert sind.\n", "Das Modul `calculation_rules` erweitern Sie während der Ausarbeitung. Um die Änderungen zu nutzen, müssen Sie das Notebook neu starten.\n", - "Im Modul `classes` befinden sich die Klassen `LegoComponent, LegoAssembly, AggergationLayer, KPIEncoder` und die Funktion `print_assembly_tree`.\n", - "`LegoComponent` bildet einzelne Komponenten ab, während `LegoAssembly` zusammengesetzte Aggregationsebenen abdeckt, also Bauteil, Baugruppe und System. Zur Unterscheidung dient die Klasse Aggregationslayer, diese ist für `LegoComponent` immer `Component`, muss für `LegoAssembly` aber entsprechend auf `SYSTEM, ASSEMBLY` oder `SUBASSEMBLY` gesetzt werden.\n", + "Im Modul `classes` befinden sich die komplette Klassen und Funktionen zur Verwendung.\n", "\n", + "Mit einem Minimalbeispiel wird Ihnen gezeigt, wie sie die Module nutzen. " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "6d3310be", + "metadata": {}, + "source": [ + "### Modul classes\n", + "`LegoComponent, LegoAssembly, AggergationLayer, KPIEncoder` und die Funktion `print_assembly_tree`.\n", + "`LegoComponent` bildet einzelne Komponenten ab, während `LegoAssembly` zusammengesetzte Aggregationsebenen abdeckt, also Bauteil, Baugruppe und System. Zur Unterscheidung dient die Klasse Aggregationslayer, diese ist für `LegoComponent` immer `Component`, muss für `LegoAssembly` entsprechend auf `SYSTEM, ASSEMBLY` oder `SUBASSEMBLY` gesetzt werden.\n", "\n", - "Mit einem Minimalbeispiel wird Ihnen gezeigt, wie sie die Module nutzen. Dabei wird nur aus Achse, Rahmen und Reifen ein Tretroller gebaut." + "Wir bauen aus Achse, Rahmen und Reifen einen Tretroller zusammen." ] }, { @@ -49,33 +60,48 @@ "execution_count": 1, "id": "b2778dee", "metadata": {}, + "outputs": [], + "source": [ + "# import modules \n", + "import json\n", + "import pprint\n", + "from functions import calculation_rules\n", + "\n", + "# Importing all modules one by one to provide an overview\n", + "# The next commented line would provide the same result in one line\n", + "# from functions.classes import *\n", + "from functions.classes import LegoComponent\n", + "from functions.classes import LegoAssembly\n", + "from functions.classes import AggregationLayer\n", + "from functions.classes import KPIEncoder\n", + "from functions.classes import print_assembly_tree" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0b1f9aff", + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'item number.1': 32073, 'item description': 'Axle 5 studs', 'category': 'axle', 'price [Euro]': 0.001, 'mass [g]': 0.66, 'delivery time [days]': 3, 'Abmessung [studs]': 5}\n" - ] - }, - { - "ename": "KeyError", - "evalue": "'name'", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[1], line 18\u001b[0m\n\u001b[0;32m 16\u001b[0m front_axle \u001b[39m=\u001b[39m classes\u001b[39m.\u001b[39mLegoComponent(\u001b[39m'\u001b[39m\u001b[39mfront axle\u001b[39m\u001b[39m'\u001b[39m,axles[\u001b[39m'\u001b[39m\u001b[39m32073\u001b[39m\u001b[39m'\u001b[39m])\n\u001b[0;32m 17\u001b[0m \u001b[39m# Both name and the data dict are optional parameters. You can view, add or edit the properties just any dict:\u001b[39;00m\n\u001b[1;32m---> 18\u001b[0m \u001b[39mprint\u001b[39m(front_axle\u001b[39m.\u001b[39;49mproperties[\u001b[39m'\u001b[39;49m\u001b[39mname\u001b[39;49m\u001b[39m'\u001b[39;49m])\n\u001b[0;32m 19\u001b[0m front_axle\u001b[39m.\u001b[39mproperties[\u001b[39m'\u001b[39m\u001b[39mcolor\u001b[39m\u001b[39m'\u001b[39m]\u001b[39m=\u001b[39m \u001b[39m'\u001b[39m\u001b[39mgrey\u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 20\u001b[0m \u001b[39m# Viewing dicts in one line is not easy to read, a better output comes with pretty print (pprint): \u001b[39;00m\n", - "\u001b[1;31mKeyError\u001b[0m: 'name'" + "{'item number': 32073, 'item description': 'Axle 5 studs', 'category': 'axle', 'price [Euro]': 0.001, 'mass [g]': 0.66, 'delivery time [days]': 3, 'Abmessung [studs]': 5}\n", + "front axle\n", + "{'Abmessung [studs]': 5,\n", + " 'category': 'axle',\n", + " 'color': 'grey',\n", + " 'delivery time [days]': 3,\n", + " 'item description': 'Axle 5 studs',\n", + " 'item number': 32073,\n", + " 'label': 'back axle',\n", + " 'mass [g]': 0.66,\n", + " 'price [Euro]': 0.001}\n" ] } ], "source": [ - "# import modules\n", - "import json\n", - "import pprint\n", - "from functions import calculation_rules\n", - "from functions import classes\n", - "\n", "## ## Create the wheels and axles as single components first\n", "# Look up the specific item you want from the provided json files, we can load the full file into a dict\n", "with open('datasheets/axles.json') as json_file:\n", @@ -83,26 +109,27 @@ "# Pick a specific axle via its 'item number'\n", "print(axles['32073'])\n", "\n", - "\n", "# Create the component with the dict:\n", - "front_axle = classes.LegoComponent('front axle',axles['32073'])\n", + "front_axle = LegoComponent('front axle',axles['32073'])\n", "# Both label and the data dict are optional parameters. You can view, add or edit the properties just any dict:\n", "print(front_axle.properties['label'])\n", "front_axle.properties['color']= 'grey'\n", - "# Viewing dicts in one line is not easy to read, a better output comes with pretty print (pprint): \n", - "pprint.pprint(front_axle.properties)\n", + "\n", "\n", "# Create the second axle\n", - "back_axle = classes.LegoComponent()\n", + "back_axle = LegoComponent()\n", "back_axle.properties['label'] = 'back axle'\n", "back_axle.properties.update(axles['32073']) \n", "# Do not use = here, otherwise you'd overwrite existing properties.\n", - "back_axle.properties['color'] = 'grey'\n" + "# Instead use update to have all entries added to properties\n", + "back_axle.properties['color'] = 'grey'\n", + "# Viewing dicts in one line is not easy to read, a better output comes with pretty print (pprint): \n", + "pprint.pprint(back_axle.properties)" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 3, "id": "b4a8e8c8", "metadata": {}, "outputs": [ @@ -115,9 +142,9 @@ " 'delivery time [days]': 5,\n", " 'diameter [mm]': 81.6,\n", " 'item description': 'wheel 81,6',\n", - " 'item number.1': 2903,\n", + " 'item number': 2903,\n", + " 'label': 'front wheel',\n", " 'mass [g]': 30.0,\n", - " 'name': 'front wheel',\n", " 'paint': 'glossy',\n", " 'price [Euro]': 1.31,\n", " 'related items': 2902,\n", @@ -126,91 +153,244 @@ } ], "source": [ - "\n", "# Now wheels\n", "with open('datasheets/wheels.json') as json_file:\n", " wheels = json.load(json_file)\n", "# Adding the color here already as dict, and the surface as key-value argument.\n", - "# Multiple of both are supported, but only in this \n", - "#front_wheel = classes.LegoComponent('front wheel', wheels['2903'],{'color':'yellow'},winter='true')\n", + "# Multiple of both parameters are supported, but only in this order.\n", + "#front_wheel = LegoComponent('front wheel', wheels['2903'],{'color':'yellow'},winter='true')\n", "\n", - "front_wheel = classes.LegoComponent('front wheel', wheels['2903'],surface='rough', paint = 'glossy')\n", - "# front_\n", + "front_wheel = LegoComponent('front wheel', wheels['2903'],surface='rough', paint = 'glossy')\n", "pprint.pprint(front_wheel.properties)\n", "\n", - "# We included a clone function, so you can easily create multiple items:\n", - "back_wheel = front_wheel.clone()\n", + "# We included a clone function to both Lego classes, so you can easily create duplicate objects:\n", + "back_wheel = front_wheel.clone('back wheel') # Passing the new label is optional" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "25bd06c5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "front axle\n" + ] + } + ], + "source": [ + "## Create subassemblies and add the wheels and axles\n", "\n", + "# The AggregationLayer must be used, passing the label, or additional properties is optional\n", + "front_wheel_assembly = LegoAssembly(AggregationLayer.SUBASSEMBLY, 'front wheel assembly', \n", + " assembly_method='stick together like lego blocks')\n", + "# Add LegoComponents to the LegoAssembly, single or as list\n", + "front_wheel_assembly.add([front_wheel,front_axle])\n", + "# You can access the components of an assembly like this:\n", + "print(front_wheel_assembly.components[1].properties['label'])\n", "\n", + "# Assemblies can be cloned as well (including their children), but don't forget to adjust \n", + "# labels or you might be stuck with a 'front wheel' in your 'back wheel assembly'\n", "\n", - "\n" + "# Stick together back wheel parts\n", + "back_wheel_assembly = LegoAssembly(AggregationLayer.SUBASSEMBLY, 'back wheel assembly')" ] }, { "cell_type": "code", - "execution_count": null, - "id": "bd3c7a6b", + "execution_count": 5, + "id": "2b6648e1", "metadata": {}, "outputs": [], "source": [ + "## Create frame component and assemble the system\n", "\n", + "with open('datasheets/frame.json') as json_file:\n", + " frame = json.load(json_file)\n", + "scooter_frame = LegoComponent('scooter frame', frame['3702'], {'color' : 'red'})\n", "\n", + "# The scooter is our system level assembly, you can choose the AggregationLayer\n", + "# But the hiercarchy (SYSTEM > ASSEMBLY > SUBASSEMBLY) must be in order.\n", + "# Components can be added to all LegoAssembly objects\n", "\n", + "scooter = LegoAssembly(AggregationLayer.SYSTEM,'scooter', manufacturer='FST', comment='Faster! Harder! Scooter!')\n", + "# add frame and subassemblies\n", + "scooter.add([scooter_frame, front_wheel_assembly, back_wheel_assembly])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "71324895", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "LegoAssembly faster, harder, scooter [faaf8065-7a4b-4d5b-8ede-72f051cca87f]\n", + "├── LegoAssembly front wheel assembly [97a43799-b91b-49b5-8d78-38bb72257931]\n", + "│ ├── LegoComponent front wheel [9cfcfc76-7ce1-4066-a8ab-ebf30f59683d]\n", + "│ └── LegoComponent front axle [010ba850-ca1b-41e2-90b9-13a3528c49e8]\n", + "├── LegoAssembly back wheel assembly [82f000db-da62-44bd-993d-b8042baf03d3]\n", + "└── LegoComponent scooter frame [87489434-ba11-42d2-91b2-bbf9735b7091]\n" + ] + } + ], + "source": [ + "## Look at the assembly\n", "\n", - "\n", - "\n", - "\n", - "# Also need a frame\n", - "with open('datasheets/Gestell.json') as json_file:\n", - " Gestelle = json.load(json_file)\n", - "Gestellbeschreibung = \"Technic, Brick 1 x 8 with Holes\"\n", - "Gestell = Gestelle[Gestellbeschreibung]" + "# We can get all LegoComponents from this assembly. \n", + "# Without parameter 'max_depth' only direct children will be listed.\n", + "scooter.get_component_list(5)" ] }, { + "attachments": {}, "cell_type": "markdown", - "id": "3b69752c", + "id": "001f1c77", "metadata": {}, "source": [ - "### Modul calculation_results\n", - "Sie können die unterschiedlichen Funktionen (Berechnungsvorschriften) aufrufen. Beachten Sie dabei die Übergabe- und Rückgabewerte." + "### Modul calculation_rules\n", + "\n", + "Um für unser System \"Tretroller\" ein KPI für das Gesamtgewicht zu erzeugen, wurde in `functions.calculation_rules` die Funktion `kpi_sum` definiert. Zusammen mit den Hilfsfunktionen der Klasse können wir nun das KPI Gewicht für das System hinzufügen. Die Massen der einzelnen Komponenten sind in den Datenblättern unter `mass [g]` enthalten." ] }, { "cell_type": "code", - "execution_count": null, - "id": "294c680b", + "execution_count": 21, + "id": "7b60d0fb", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You called the test function.\n" + ] + } + ], "source": [ "# test the import\n", "calculation_rules.test_function()" ] }, { - "cell_type": "markdown", - "id": "a2c87a3c", + "cell_type": "code", + "execution_count": 23, + "id": "fe4edad6", "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gesamtgewicht: 33.51 g\n" + ] + } + ], "source": [ - "### Modul classes\n", - "Wir bauen ein Auto zusammen." + "## Add mass to assemblies\n", + "# List all components' mass\n", + "combined_weight = 0\n", + "for c in scooter.get_component_list(100):\n", + " combined_weight += c.properties['mass [g]']\n", + "print(\"Gesamtgewicht: \",combined_weight,\"g\")\n", + "# Add KPI to system\n", + "scooter.properties['mass [g]'] = combined_weight\n", + "\n", + "# We can also add the mass to the subassemblies\n", + "# children() returns a dict with a list added parts\n", + "for a in scooter.children()['assemblies']:\n", + " a_mass = 0\n", + " for c in a.get_component_list(99):\n", + " a_mass += c.properties['mass [g]']\n", + " a.properties['mass [g]'] = a_mass\n", + " \n" ] }, { "cell_type": "code", - "execution_count": null, - "id": "8db386db", + "execution_count": 15, + "id": "4d56419f", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "LegoAssembly faster, harder, scooter [faaf8065-7a4b-4d5b-8ede-72f051cca87f]\n", + "├── LegoAssembly front wheel assembly [97a43799-b91b-49b5-8d78-38bb72257931]\n", + "│ ├── LegoComponent front wheel [9cfcfc76-7ce1-4066-a8ab-ebf30f59683d]\n", + "│ └── LegoComponent front axle [010ba850-ca1b-41e2-90b9-13a3528c49e8]\n", + "├── LegoAssembly back wheel assembly [82f000db-da62-44bd-993d-b8042baf03d3]\n", + "└── LegoComponent scooter frame [87489434-ba11-42d2-91b2-bbf9735b7091]\n", + "LegoAssembly front wheel assembly [97a43799-b91b-49b5-8d78-38bb72257931]\n", + "LegoAssembly faster, harder, scooter [faaf8065-7a4b-4d5b-8ede-72f051cca87f]\n" + ] + } + ], + "source": [ + "## Look at the full assembly with KPI\n", + "# Print the full assembly with all levels\n", + "print_assembly_tree(scooter)" + ] }, { "cell_type": "code", - "execution_count": null, - "id": "8f2ae9f4", + "execution_count": 27, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# Dump to json file with to_dict() and KPIEncoder\n", + "with open(\"scooter.json\", \"w\") as fp:\n", + " json.dump(scooter.to_dict(), fp, cls=KPIEncoder)\n", + "# full view is too big for Juypter (try it)\n", + "# pprint.pprint(scooter.to_dict())" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "53793ae8", + "metadata": {}, + "source": [ + "In dieser exportierten json-Datei ('scooter.json') lassen sind die Werte maschinen- und menschenlesbar hinterlegt.\n", + "Zusammen mit der Berechnungsvorschrift in `calculation_rules` ist auch die Entstehung des KPI nachvollziehbar und wiederverwendbar dokumentiert und damit 'FAIR'." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "b91fed73", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Child: LegoAssembly front wheel assembly [97a43799-b91b-49b5-8d78-38bb72257931]\n", + "Parent: LegoAssembly faster, harder, scooter [faaf8065-7a4b-4d5b-8ede-72f051cca87f]\n", + "Top parent: LegoAssembly faster, harder, scooter [faaf8065-7a4b-4d5b-8ede-72f051cca87f]\n" + ] + } + ], + "source": [ + "## Additional details\n", + "# Each child knows its parent\n", + "first_child = scooter.children()['assemblies'][0]\n", + "print(\"Child:\", first_child)\n", + "print(\"Parent:\", first_child.parent)\n", + "# Also we can access the \"top\" parent\n", + "latest_child = first_child.children()['components'][0]\n", + "print(\"Top parent: \",latest_child.get_root_assembly())\n", + "\n", + "# Each part created has a unique identifier (the long number in [])\n", + "# Don't try add the identical part again." + ] }, { "cell_type": "markdown", -- GitLab