@memoarchitect/ontology 0.6.4 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/examples/gpca-pump/analysis/Samples/01-model-overview.ipynb +148 -0
- package/examples/gpca-pump/analysis/Samples/02-architecture-hotspots.ipynb +301 -0
- package/examples/gpca-pump/analysis/Samples/03-model-quality.ipynb +229 -0
- package/examples/gpca-pump/analysis/Samples/04-change-impact-explorer.ipynb +200 -0
- package/examples/gpca-pump/analysis/Samples/05-model-charts.ipynb +188 -0
- package/examples/gpca-pump/analysis/Samples/06-ownership-graph.ipynb +173 -0
- package/examples/gpca-pump/analysis/Samples/07-model-inventory-table.ipynb +180 -0
- package/examples/gpca-pump/analysis/Samples/README.md +37 -0
- package/examples/gpca-pump/model/catalog/gpca_architecture.sysml +21 -11
- package/examples/gpca-pump/model/catalog/gpca_behavior_modes.sysml +7 -1
- package/examples/gpca-pump/model/catalog/gpca_interfaces.sysml +34 -5
- package/examples/gpca-pump/model/catalog/gpca_risk.sysml +9 -0
- package/examples/gpca-pump/model/catalog/gpca_trace.sysml +32 -8
- package/examples/gpca-pump/model/views/logical_device_interconnect_view.sysml +3 -3
- package/package.json +2 -2
- package/src/rules/ontology/ontology_invariants.sysml +13 -0
- package/src/viewpoints/definitions/memo_viewpoint_definitions.sysml +2 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "memo-1",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# Model quality review\n",
|
|
9
|
+
"\n",
|
|
10
|
+
"Review diagnostics, unnamed elements, repeated names, and empty definitions.\n",
|
|
11
|
+
"\n",
|
|
12
|
+
"This sample uses only standard SysML v2 concepts and automatically discovers the project's `model/` or `src/` directory."
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"cell_type": "code",
|
|
17
|
+
"execution_count": 1,
|
|
18
|
+
"id": "memo-2",
|
|
19
|
+
"metadata": {
|
|
20
|
+
"execution": {
|
|
21
|
+
"iopub.execute_input": "2026-07-29T22:31:50.589541Z",
|
|
22
|
+
"iopub.status.busy": "2026-07-29T22:31:50.589403Z",
|
|
23
|
+
"iopub.status.idle": "2026-07-29T22:31:50.863275Z",
|
|
24
|
+
"shell.execute_reply": "2026-07-29T22:31:50.862876Z"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"outputs": [
|
|
28
|
+
{
|
|
29
|
+
"name": "stdout",
|
|
30
|
+
"output_type": "stream",
|
|
31
|
+
"text": [
|
|
32
|
+
"Loaded 42 SysML files from /Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model\n"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"source": [
|
|
37
|
+
"from pathlib import Path\n",
|
|
38
|
+
"from collections import Counter, defaultdict\n",
|
|
39
|
+
"import syside\n",
|
|
40
|
+
"\n",
|
|
41
|
+
"def find_sysml_root(start=Path.cwd()):\n",
|
|
42
|
+
" \"\"\"Find the nearest model/ or src/ folder containing textual SysML.\"\"\"\n",
|
|
43
|
+
" for directory in (start, *start.parents):\n",
|
|
44
|
+
" for folder_name in ('model', 'src'):\n",
|
|
45
|
+
" candidate = directory / folder_name\n",
|
|
46
|
+
" if candidate.is_dir() and next(candidate.rglob('*.sysml'), None):\n",
|
|
47
|
+
" return candidate\n",
|
|
48
|
+
" raise FileNotFoundError('No model/ or src/ directory containing .sysml files was found')\n",
|
|
49
|
+
"\n",
|
|
50
|
+
"SYSML_ROOT = find_sysml_root()\n",
|
|
51
|
+
"SYSML_FILES = sorted(SYSML_ROOT.rglob('*.sysml'))\n",
|
|
52
|
+
"model, diagnostics = syside.try_load_model([str(path) for path in SYSML_FILES])\n",
|
|
53
|
+
"print(f'Loaded {len(SYSML_FILES)} SysML files from {SYSML_ROOT}')"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"cell_type": "code",
|
|
58
|
+
"execution_count": 2,
|
|
59
|
+
"id": "memo-3",
|
|
60
|
+
"metadata": {
|
|
61
|
+
"execution": {
|
|
62
|
+
"iopub.execute_input": "2026-07-29T22:31:50.864878Z",
|
|
63
|
+
"iopub.status.busy": "2026-07-29T22:31:50.864782Z",
|
|
64
|
+
"iopub.status.idle": "2026-07-29T22:31:50.874497Z",
|
|
65
|
+
"shell.execute_reply": "2026-07-29T22:31:50.874144Z"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"outputs": [
|
|
69
|
+
{
|
|
70
|
+
"data": {
|
|
71
|
+
"text/plain": [
|
|
72
|
+
"{'parser/semantic errors': 8009,\n",
|
|
73
|
+
" 'warnings': 0,\n",
|
|
74
|
+
" 'unnamed usages or definitions': 5978,\n",
|
|
75
|
+
" 'repeated qualified names': 5,\n",
|
|
76
|
+
" 'definitions with no owned usages/definitions': 1}"
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
"metadata": {},
|
|
80
|
+
"output_type": "display_data"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "stdout",
|
|
84
|
+
"output_type": "stream",
|
|
85
|
+
"text": [
|
|
86
|
+
"Repeated qualified names (first 30)\n"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"data": {
|
|
91
|
+
"text/plain": [
|
|
92
|
+
"[('sensorStatus', 2, ['ReferenceUsage']),\n",
|
|
93
|
+
" ('alarmSignal', 2, ['ReferenceUsage']),\n",
|
|
94
|
+
" ('authorizedBolus', 2, ['ReferenceUsage']),\n",
|
|
95
|
+
" ('flowCommand', 2, ['ReferenceUsage']),\n",
|
|
96
|
+
" ('actuation', 2, ['ReferenceUsage'])]"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"metadata": {},
|
|
100
|
+
"output_type": "display_data"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "stdout",
|
|
104
|
+
"output_type": "stream",
|
|
105
|
+
"text": [
|
|
106
|
+
"Empty definition candidates (first 30)\n"
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"data": {
|
|
111
|
+
"text/plain": [
|
|
112
|
+
"[('ConjugatedPortDefinition',\n",
|
|
113
|
+
" _syside.core.QualifiedName(['memo_examples_gpca_pump_model_catalog_gpca_interfaces', 'SensorInputPort', '~SensorInputPort']))]"
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
"execution_count": 2,
|
|
117
|
+
"metadata": {},
|
|
118
|
+
"output_type": "execute_result"
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"source": [
|
|
122
|
+
"from IPython.display import display\n",
|
|
123
|
+
"\n",
|
|
124
|
+
"usages = list(model.elements(syside.Usage, include_subtypes=True))\n",
|
|
125
|
+
"definitions = list(model.elements(syside.Definition, include_subtypes=True))\n",
|
|
126
|
+
"semantic_elements = usages + definitions\n",
|
|
127
|
+
"\n",
|
|
128
|
+
"unnamed = [item for item in semantic_elements if not (item.name or item.declared_name)]\n",
|
|
129
|
+
"by_name = defaultdict(list)\n",
|
|
130
|
+
"for item in semantic_elements:\n",
|
|
131
|
+
" value = item.qualified_name or item.name or item.declared_name\n",
|
|
132
|
+
" name = str(value) if value else None\n",
|
|
133
|
+
" if name:\n",
|
|
134
|
+
" by_name[name].append(item)\n",
|
|
135
|
+
"duplicates = [(name, len(items), sorted({type(item).__name__ for item in items})) for name, items in by_name.items() if len(items) > 1]\n",
|
|
136
|
+
"empty_definitions = [\n",
|
|
137
|
+
" item for item in definitions\n",
|
|
138
|
+
" if not any(isinstance(child, (syside.Usage, syside.Definition)) for child in item.owned_elements)\n",
|
|
139
|
+
"]\n",
|
|
140
|
+
"\n",
|
|
141
|
+
"display({\n",
|
|
142
|
+
" 'parser/semantic errors': len(list(diagnostics.errors)),\n",
|
|
143
|
+
" 'warnings': len(list(diagnostics.warnings)),\n",
|
|
144
|
+
" 'unnamed usages or definitions': len(unnamed),\n",
|
|
145
|
+
" 'repeated qualified names': len(duplicates),\n",
|
|
146
|
+
" 'definitions with no owned usages/definitions': len(empty_definitions),\n",
|
|
147
|
+
"})\n",
|
|
148
|
+
"\n",
|
|
149
|
+
"print('Repeated qualified names (first 30)')\n",
|
|
150
|
+
"display(sorted(duplicates, key=lambda item: item[1], reverse=True)[:30])\n",
|
|
151
|
+
"\n",
|
|
152
|
+
"print('Empty definition candidates (first 30)')\n",
|
|
153
|
+
"[(type(item).__name__, item.qualified_name or item.name) for item in empty_definitions[:30]]"
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"cell_type": "code",
|
|
158
|
+
"execution_count": 3,
|
|
159
|
+
"id": "memo-4",
|
|
160
|
+
"metadata": {
|
|
161
|
+
"execution": {
|
|
162
|
+
"iopub.execute_input": "2026-07-29T22:31:50.875513Z",
|
|
163
|
+
"iopub.status.busy": "2026-07-29T22:31:50.875441Z",
|
|
164
|
+
"iopub.status.idle": "2026-07-29T22:31:50.877529Z",
|
|
165
|
+
"shell.execute_reply": "2026-07-29T22:31:50.877175Z"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"outputs": [
|
|
169
|
+
{
|
|
170
|
+
"name": "stdout",
|
|
171
|
+
"output_type": "stream",
|
|
172
|
+
"text": [
|
|
173
|
+
"First diagnostics to investigate\n",
|
|
174
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_cybersecurity.sysml:159:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
175
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:19:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
176
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:27:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
177
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:35:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
178
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:43:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
179
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:51:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
180
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:63:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
181
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:75:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
182
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:86:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
183
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:96:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
184
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:108:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
185
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:118:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
186
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:128:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
187
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:138:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
188
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_interfaces.sysml:148:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
189
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_operational.sysml:180:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
190
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_operational.sysml:181:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
191
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_operational.sysml:182:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
192
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_trace.sysml:54:5: error (connector-related-features): A concrete connector must have at least two related elements\n",
|
|
193
|
+
"/Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model/catalog/gpca_trace.sysml:77:5: error (connector-related-features): A concrete connector must have at least two related elements\n"
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
"source": [
|
|
198
|
+
"print('First diagnostics to investigate')\n",
|
|
199
|
+
"for diagnostic in list(diagnostics.errors)[:20] + list(diagnostics.warnings)[:20]:\n",
|
|
200
|
+
" print(diagnostic)"
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
"metadata": {
|
|
205
|
+
"kernelspec": {
|
|
206
|
+
"display_name": "Python 3 (ipykernel)",
|
|
207
|
+
"language": "python",
|
|
208
|
+
"name": "python3"
|
|
209
|
+
},
|
|
210
|
+
"language_info": {
|
|
211
|
+
"codemirror_mode": {
|
|
212
|
+
"name": "ipython",
|
|
213
|
+
"version": 3
|
|
214
|
+
},
|
|
215
|
+
"file_extension": ".py",
|
|
216
|
+
"mimetype": "text/x-python",
|
|
217
|
+
"name": "python",
|
|
218
|
+
"nbconvert_exporter": "python",
|
|
219
|
+
"pygments_lexer": "ipython3",
|
|
220
|
+
"version": "3.12.13"
|
|
221
|
+
},
|
|
222
|
+
"memo": {
|
|
223
|
+
"description": "Review diagnostics, unnamed elements, repeated names, and empty definitions.",
|
|
224
|
+
"starter": true
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"nbformat": 4,
|
|
228
|
+
"nbformat_minor": 5
|
|
229
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "memo-1",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# Change-impact explorer\n",
|
|
9
|
+
"\n",
|
|
10
|
+
"Search any element and inspect its owner, children, and directly related elements.\n",
|
|
11
|
+
"\n",
|
|
12
|
+
"This sample uses only standard SysML v2 concepts and automatically discovers the project's `model/` or `src/` directory."
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"cell_type": "code",
|
|
17
|
+
"execution_count": 1,
|
|
18
|
+
"id": "memo-2",
|
|
19
|
+
"metadata": {
|
|
20
|
+
"execution": {
|
|
21
|
+
"iopub.execute_input": "2026-07-29T22:31:52.751165Z",
|
|
22
|
+
"iopub.status.busy": "2026-07-29T22:31:52.750921Z",
|
|
23
|
+
"iopub.status.idle": "2026-07-29T22:31:53.037061Z",
|
|
24
|
+
"shell.execute_reply": "2026-07-29T22:31:53.036612Z"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"outputs": [
|
|
28
|
+
{
|
|
29
|
+
"name": "stdout",
|
|
30
|
+
"output_type": "stream",
|
|
31
|
+
"text": [
|
|
32
|
+
"Loaded 42 SysML files from /Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model\n"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"source": [
|
|
37
|
+
"from pathlib import Path\n",
|
|
38
|
+
"from collections import Counter, defaultdict\n",
|
|
39
|
+
"import syside\n",
|
|
40
|
+
"\n",
|
|
41
|
+
"def find_sysml_root(start=Path.cwd()):\n",
|
|
42
|
+
" \"\"\"Find the nearest model/ or src/ folder containing textual SysML.\"\"\"\n",
|
|
43
|
+
" for directory in (start, *start.parents):\n",
|
|
44
|
+
" for folder_name in ('model', 'src'):\n",
|
|
45
|
+
" candidate = directory / folder_name\n",
|
|
46
|
+
" if candidate.is_dir() and next(candidate.rglob('*.sysml'), None):\n",
|
|
47
|
+
" return candidate\n",
|
|
48
|
+
" raise FileNotFoundError('No model/ or src/ directory containing .sysml files was found')\n",
|
|
49
|
+
"\n",
|
|
50
|
+
"SYSML_ROOT = find_sysml_root()\n",
|
|
51
|
+
"SYSML_FILES = sorted(SYSML_ROOT.rglob('*.sysml'))\n",
|
|
52
|
+
"model, diagnostics = syside.try_load_model([str(path) for path in SYSML_FILES])\n",
|
|
53
|
+
"print(f'Loaded {len(SYSML_FILES)} SysML files from {SYSML_ROOT}')"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"cell_type": "code",
|
|
58
|
+
"execution_count": 2,
|
|
59
|
+
"id": "memo-3",
|
|
60
|
+
"metadata": {
|
|
61
|
+
"execution": {
|
|
62
|
+
"iopub.execute_input": "2026-07-29T22:31:53.038369Z",
|
|
63
|
+
"iopub.status.busy": "2026-07-29T22:31:53.038278Z",
|
|
64
|
+
"iopub.status.idle": "2026-07-29T22:31:53.044194Z",
|
|
65
|
+
"shell.execute_reply": "2026-07-29T22:31:53.043828Z"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"outputs": [
|
|
69
|
+
{
|
|
70
|
+
"name": "stdout",
|
|
71
|
+
"output_type": "stream",
|
|
72
|
+
"text": [
|
|
73
|
+
"Set QUERY to a name fragment. Suggestions:\n",
|
|
74
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fmeaDelivery\n",
|
|
75
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fmMotorRunaway\n",
|
|
76
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fmAirSensorSilent\n",
|
|
77
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fmOcclusionDrift\n",
|
|
78
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fmStuckBolusButton\n",
|
|
79
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fmBatteryEarlyDepletion\n",
|
|
80
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::feOverdose\n",
|
|
81
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::feAirDelivered\n",
|
|
82
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::feTherapyInterrupted\n",
|
|
83
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fcMotorDriverFault\n",
|
|
84
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fcTransducerAging\n",
|
|
85
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fcPendantAbuse\n",
|
|
86
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::dmFlowCrossCheck\n",
|
|
87
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::dmPostSensorCheck\n",
|
|
88
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::dmStuckInputMonitor\n",
|
|
89
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fmeaActionAirSensor\n",
|
|
90
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::ftaOverdose\n",
|
|
91
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fteOverdoseTop\n",
|
|
92
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fteMotorRunaway\n",
|
|
93
|
+
" - memo_examples_gpca_pump_model_catalog_gpca_analysis::fteCrossCheckFails\n"
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"data": {
|
|
98
|
+
"text/plain": [
|
|
99
|
+
"[]"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"execution_count": 2,
|
|
103
|
+
"metadata": {},
|
|
104
|
+
"output_type": "execute_result"
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"source": [
|
|
108
|
+
"QUERY = '' # Example: 'pump', 'controller', 'safety', or a requirement name\n",
|
|
109
|
+
"\n",
|
|
110
|
+
"semantic_elements = (\n",
|
|
111
|
+
" list(model.elements(syside.Usage, include_subtypes=True))\n",
|
|
112
|
+
" + list(model.elements(syside.Definition, include_subtypes=True))\n",
|
|
113
|
+
")\n",
|
|
114
|
+
"\n",
|
|
115
|
+
"def label(element):\n",
|
|
116
|
+
" value = element.qualified_name or element.name or element.declared_name\n",
|
|
117
|
+
" return str(value) if value else '<unnamed>'\n",
|
|
118
|
+
"\n",
|
|
119
|
+
"if not QUERY:\n",
|
|
120
|
+
" print('Set QUERY to a name fragment. Suggestions:')\n",
|
|
121
|
+
" for suggestion in [label(item) for item in semantic_elements if item.name][:20]:\n",
|
|
122
|
+
" print(' -', suggestion)\n",
|
|
123
|
+
" matches = []\n",
|
|
124
|
+
"else:\n",
|
|
125
|
+
" matches = [item for item in semantic_elements if QUERY.casefold() in label(item).casefold()]\n",
|
|
126
|
+
" print(f'{len(matches)} match(es) for {QUERY!r}')\n",
|
|
127
|
+
"\n",
|
|
128
|
+
"[(type(item).__name__, label(item)) for item in matches[:100]]"
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"cell_type": "code",
|
|
133
|
+
"execution_count": 3,
|
|
134
|
+
"id": "memo-4",
|
|
135
|
+
"metadata": {
|
|
136
|
+
"execution": {
|
|
137
|
+
"iopub.execute_input": "2026-07-29T22:31:53.045051Z",
|
|
138
|
+
"iopub.status.busy": "2026-07-29T22:31:53.045000Z",
|
|
139
|
+
"iopub.status.idle": "2026-07-29T22:31:53.047642Z",
|
|
140
|
+
"shell.execute_reply": "2026-07-29T22:31:53.047288Z"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"outputs": [
|
|
144
|
+
{
|
|
145
|
+
"data": {
|
|
146
|
+
"text/plain": [
|
|
147
|
+
"[]"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
"execution_count": 3,
|
|
151
|
+
"metadata": {},
|
|
152
|
+
"output_type": "execute_result"
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
"source": [
|
|
156
|
+
"def neighborhood(element):\n",
|
|
157
|
+
" owner = getattr(element, 'owner', None)\n",
|
|
158
|
+
" children = [child for child in element.owned_elements if isinstance(child, (syside.Usage, syside.Definition))]\n",
|
|
159
|
+
" related = []\n",
|
|
160
|
+
" for relationship in element.owned_relationships:\n",
|
|
161
|
+
" for candidate in relationship.related_elements:\n",
|
|
162
|
+
" if candidate is not element:\n",
|
|
163
|
+
" related.append(candidate)\n",
|
|
164
|
+
" return {\n",
|
|
165
|
+
" 'selected': (type(element).__name__, label(element)),\n",
|
|
166
|
+
" 'owner': None if owner is None else (type(owner).__name__, label(owner)),\n",
|
|
167
|
+
" 'children': [(type(child).__name__, label(child)) for child in children[:50]],\n",
|
|
168
|
+
" 'directly related': [(type(item).__name__, label(item)) for item in related[:50]],\n",
|
|
169
|
+
" }\n",
|
|
170
|
+
"\n",
|
|
171
|
+
"[neighborhood(item) for item in matches[:10]]"
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
],
|
|
175
|
+
"metadata": {
|
|
176
|
+
"kernelspec": {
|
|
177
|
+
"display_name": "Python 3 (ipykernel)",
|
|
178
|
+
"language": "python",
|
|
179
|
+
"name": "python3"
|
|
180
|
+
},
|
|
181
|
+
"language_info": {
|
|
182
|
+
"codemirror_mode": {
|
|
183
|
+
"name": "ipython",
|
|
184
|
+
"version": 3
|
|
185
|
+
},
|
|
186
|
+
"file_extension": ".py",
|
|
187
|
+
"mimetype": "text/x-python",
|
|
188
|
+
"name": "python",
|
|
189
|
+
"nbconvert_exporter": "python",
|
|
190
|
+
"pygments_lexer": "ipython3",
|
|
191
|
+
"version": "3.12.13"
|
|
192
|
+
},
|
|
193
|
+
"memo": {
|
|
194
|
+
"description": "Search any element and inspect its owner, children, and directly related elements.",
|
|
195
|
+
"starter": true
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
"nbformat": 4,
|
|
199
|
+
"nbformat_minor": 5
|
|
200
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "memo-1",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# Model charts\n",
|
|
9
|
+
"\n",
|
|
10
|
+
"Render semantic composition and diagnostic charts without external plotting packages.\n",
|
|
11
|
+
"\n",
|
|
12
|
+
"This sample uses only standard SysML v2 concepts and automatically discovers the project's `model/` or `src/` directory."
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"cell_type": "code",
|
|
17
|
+
"execution_count": 1,
|
|
18
|
+
"id": "memo-2",
|
|
19
|
+
"metadata": {
|
|
20
|
+
"execution": {
|
|
21
|
+
"iopub.execute_input": "2026-07-29T22:35:00.883178Z",
|
|
22
|
+
"iopub.status.busy": "2026-07-29T22:35:00.882949Z",
|
|
23
|
+
"iopub.status.idle": "2026-07-29T22:35:01.265420Z",
|
|
24
|
+
"shell.execute_reply": "2026-07-29T22:35:01.265083Z"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"outputs": [
|
|
28
|
+
{
|
|
29
|
+
"name": "stdout",
|
|
30
|
+
"output_type": "stream",
|
|
31
|
+
"text": [
|
|
32
|
+
"Loaded 42 SysML files from /Users/poojakashyap/sandbox/memo-meta/memo/examples/gpca-pump/model\n"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"source": [
|
|
37
|
+
"from pathlib import Path\n",
|
|
38
|
+
"from collections import Counter, defaultdict\n",
|
|
39
|
+
"import syside\n",
|
|
40
|
+
"\n",
|
|
41
|
+
"def find_sysml_root(start=Path.cwd()):\n",
|
|
42
|
+
" \"\"\"Find the nearest model/ or src/ folder containing textual SysML.\"\"\"\n",
|
|
43
|
+
" for directory in (start, *start.parents):\n",
|
|
44
|
+
" for folder_name in ('model', 'src'):\n",
|
|
45
|
+
" candidate = directory / folder_name\n",
|
|
46
|
+
" if candidate.is_dir() and next(candidate.rglob('*.sysml'), None):\n",
|
|
47
|
+
" return candidate\n",
|
|
48
|
+
" raise FileNotFoundError('No model/ or src/ directory containing .sysml files was found')\n",
|
|
49
|
+
"\n",
|
|
50
|
+
"SYSML_ROOT = find_sysml_root()\n",
|
|
51
|
+
"SYSML_FILES = sorted(SYSML_ROOT.rglob('*.sysml'))\n",
|
|
52
|
+
"model, diagnostics = syside.try_load_model([str(path) for path in SYSML_FILES])\n",
|
|
53
|
+
"print(f'Loaded {len(SYSML_FILES)} SysML files from {SYSML_ROOT}')"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"cell_type": "code",
|
|
58
|
+
"execution_count": 2,
|
|
59
|
+
"id": "memo-3",
|
|
60
|
+
"metadata": {
|
|
61
|
+
"execution": {
|
|
62
|
+
"iopub.execute_input": "2026-07-29T22:35:01.266692Z",
|
|
63
|
+
"iopub.status.busy": "2026-07-29T22:35:01.266622Z",
|
|
64
|
+
"iopub.status.idle": "2026-07-29T22:35:01.282674Z",
|
|
65
|
+
"shell.execute_reply": "2026-07-29T22:35:01.282322Z"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"outputs": [
|
|
69
|
+
{
|
|
70
|
+
"data": {
|
|
71
|
+
"text/html": [
|
|
72
|
+
"<div style=\"font-family:system-ui;max-width:900px\">\n",
|
|
73
|
+
" <h2>Semantic composition</h2><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
74
|
+
" <div>AttributeUsage</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:100.0%;height:20px;background:#2d8d70\"></div></div><strong>5,056</strong>\n",
|
|
75
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
76
|
+
" <div>ConnectionUsage</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:13.7%;height:20px;background:#3887c7\"></div></div><strong>691</strong>\n",
|
|
77
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
78
|
+
" <div>PartUsage</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:8.4%;height:20px;background:#e58a2b\"></div></div><strong>425</strong>\n",
|
|
79
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
80
|
+
" <div>RequirementUsage</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:2.1%;height:20px;background:#8357b6\"></div></div><strong>105</strong>\n",
|
|
81
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
82
|
+
" <div>ItemUsage</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:1.0%;height:20px;background:#cf5c62\"></div></div><strong>49</strong>\n",
|
|
83
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
84
|
+
" <div>ActionUsage</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:1.0%;height:20px;background:#4f9d9d\"></div></div><strong>44</strong>\n",
|
|
85
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
86
|
+
" <div>StateUsage</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:1.0%;height:20px;background:#2d8d70\"></div></div><strong>10</strong>\n",
|
|
87
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
88
|
+
" <div>ActionDefinition</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:1.0%;height:20px;background:#3887c7\"></div></div><strong>6</strong>\n",
|
|
89
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
90
|
+
" <div>PortDefinition</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:1.0%;height:20px;background:#e58a2b\"></div></div><strong>1</strong>\n",
|
|
91
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
92
|
+
" <div>PortUsage</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:1.0%;height:20px;background:#8357b6\"></div></div><strong>0</strong>\n",
|
|
93
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
94
|
+
" <div>PartDefinition</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:1.0%;height:20px;background:#cf5c62\"></div></div><strong>0</strong>\n",
|
|
95
|
+
" </div><div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
96
|
+
" <div>RequirementDefinition</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:1.0%;height:20px;background:#4f9d9d\"></div></div><strong>0</strong>\n",
|
|
97
|
+
" </div>\n",
|
|
98
|
+
" <h2 style=\"margin-top:28px\">Diagnostics</h2>\n",
|
|
99
|
+
" <div style=\"display:flex;align-items:center;gap:24px\">\n",
|
|
100
|
+
" <div style=\"width:150px;height:150px;border-radius:50%;background:conic-gradient(#cf5c62 0 100.0%, #e5a52b 100.0% 100.0%, #3887c7 100.0% 100%);position:relative\">\n",
|
|
101
|
+
" <div style=\"position:absolute;inset:28px;background:white;border-radius:50%;display:grid;place-items:center;font-size:24px;font-weight:700\">8009</div>\n",
|
|
102
|
+
" </div>\n",
|
|
103
|
+
" <table><tr><th>Severity</th><th>Count</th></tr><tr><td>Errors</td><td>8009</td></tr><tr><td>Warnings</td><td>0</td></tr><tr><td>Information</td><td>0</td></tr></table>\n",
|
|
104
|
+
" </div>\n",
|
|
105
|
+
"</div>"
|
|
106
|
+
],
|
|
107
|
+
"text/plain": [
|
|
108
|
+
"<IPython.core.display.HTML object>"
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"metadata": {},
|
|
112
|
+
"output_type": "display_data"
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"source": [
|
|
116
|
+
"from html import escape\n",
|
|
117
|
+
"from IPython.display import HTML, display\n",
|
|
118
|
+
"\n",
|
|
119
|
+
"type_names = [\n",
|
|
120
|
+
" 'PartUsage', 'RequirementUsage', 'ActionUsage', 'PortUsage',\n",
|
|
121
|
+
" 'ConnectionUsage', 'StateUsage', 'AttributeUsage', 'ItemUsage',\n",
|
|
122
|
+
" 'PartDefinition', 'RequirementDefinition', 'ActionDefinition', 'PortDefinition'\n",
|
|
123
|
+
"]\n",
|
|
124
|
+
"counts = {}\n",
|
|
125
|
+
"for type_name in type_names:\n",
|
|
126
|
+
" node_type = getattr(syside, type_name, None)\n",
|
|
127
|
+
" if node_type is not None:\n",
|
|
128
|
+
" counts[type_name] = len(list(model.elements(node_type)))\n",
|
|
129
|
+
"\n",
|
|
130
|
+
"palette = ['#2d8d70', '#3887c7', '#e58a2b', '#8357b6', '#cf5c62', '#4f9d9d']\n",
|
|
131
|
+
"largest = max(counts.values(), default=1)\n",
|
|
132
|
+
"bars = []\n",
|
|
133
|
+
"for index, (name, count) in enumerate(sorted(counts.items(), key=lambda item: item[1], reverse=True)):\n",
|
|
134
|
+
" width = max(1, count * 100 / largest)\n",
|
|
135
|
+
" color = palette[index % len(palette)]\n",
|
|
136
|
+
" bars.append(f'''<div style=\"display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0\">\n",
|
|
137
|
+
" <div>{escape(name)}</div><div style=\"background:#edf2f4;border-radius:6px;overflow:hidden\"><div style=\"width:{width:.1f}%;height:20px;background:{color}\"></div></div><strong>{count:,}</strong>\n",
|
|
138
|
+
" </div>''')\n",
|
|
139
|
+
"\n",
|
|
140
|
+
"error_count = len(list(diagnostics.errors))\n",
|
|
141
|
+
"warning_count = len(list(diagnostics.warnings))\n",
|
|
142
|
+
"info_count = len(list(diagnostics.infos))\n",
|
|
143
|
+
"total_diagnostics = max(1, error_count + warning_count + info_count)\n",
|
|
144
|
+
"diagnostic_gradient = (\n",
|
|
145
|
+
" f'#cf5c62 0 {error_count / total_diagnostics * 100:.1f}%, '\n",
|
|
146
|
+
" f'#e5a52b {error_count / total_diagnostics * 100:.1f}% {(error_count + warning_count) / total_diagnostics * 100:.1f}%, '\n",
|
|
147
|
+
" f'#3887c7 {(error_count + warning_count) / total_diagnostics * 100:.1f}% 100%'\n",
|
|
148
|
+
")\n",
|
|
149
|
+
"\n",
|
|
150
|
+
"display(HTML(f'''<div style=\"font-family:system-ui;max-width:900px\">\n",
|
|
151
|
+
" <h2>Semantic composition</h2>{''.join(bars)}\n",
|
|
152
|
+
" <h2 style=\"margin-top:28px\">Diagnostics</h2>\n",
|
|
153
|
+
" <div style=\"display:flex;align-items:center;gap:24px\">\n",
|
|
154
|
+
" <div style=\"width:150px;height:150px;border-radius:50%;background:conic-gradient({diagnostic_gradient});position:relative\">\n",
|
|
155
|
+
" <div style=\"position:absolute;inset:28px;background:white;border-radius:50%;display:grid;place-items:center;font-size:24px;font-weight:700\">{error_count + warning_count + info_count}</div>\n",
|
|
156
|
+
" </div>\n",
|
|
157
|
+
" <table><tr><th>Severity</th><th>Count</th></tr><tr><td>Errors</td><td>{error_count}</td></tr><tr><td>Warnings</td><td>{warning_count}</td></tr><tr><td>Information</td><td>{info_count}</td></tr></table>\n",
|
|
158
|
+
" </div>\n",
|
|
159
|
+
"</div>'''))"
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
"metadata": {
|
|
164
|
+
"kernelspec": {
|
|
165
|
+
"display_name": "Python 3 (ipykernel)",
|
|
166
|
+
"language": "python",
|
|
167
|
+
"name": "python3"
|
|
168
|
+
},
|
|
169
|
+
"language_info": {
|
|
170
|
+
"codemirror_mode": {
|
|
171
|
+
"name": "ipython",
|
|
172
|
+
"version": 3
|
|
173
|
+
},
|
|
174
|
+
"file_extension": ".py",
|
|
175
|
+
"mimetype": "text/x-python",
|
|
176
|
+
"name": "python",
|
|
177
|
+
"nbconvert_exporter": "python",
|
|
178
|
+
"pygments_lexer": "ipython3",
|
|
179
|
+
"version": "3.12.13"
|
|
180
|
+
},
|
|
181
|
+
"memo": {
|
|
182
|
+
"description": "Render semantic composition and diagnostic charts without external plotting packages.",
|
|
183
|
+
"starter": true
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"nbformat": 4,
|
|
187
|
+
"nbformat_minor": 5
|
|
188
|
+
}
|