Graphical Models and Metadata ​
A graphical Dyad model uses the same components and connections as its textual model. Metadata controls icons, port positions, labels, and connection routes. It also supplies tags and parameter-panel groups for tools and model users. Editing a diagram changes those annotations alongside the declarations and connections that define the model.
The embedded diagrams are interactive. On wide screens, code appears beside the diagram; on narrower screens, use the Diagram and Code tabs. The task excerpts show the metadata you need to change. Links to complete authored source retain every annotation needed to reproduce the examples; the viewer Code pane shows the model without those annotations.
Where metadata belongs ​
| Attachment | Form | Use |
|---|---|---|
| A definition | metadata { ... } before its final end | Icons, labels, documentation settings |
| A declaration or relation | { ... } after the element | Instance placement, connection routing |
Metadata is a JSON object with quoted keys. The Dyad namespace contains standard keys whose meaning depends on the attachment. The examples below attach icons to definitions and placement or routing to individual declarations and relations.
Choosing component and connector icons ​
An icon name selects a drawing from a definition's Dyad.icons map. Define the alternatives once, then set iconName on each instance. Components and connectors use the same mechanism.
This example gives a component two red symbols and an electrical connector two blue symbols. The assets come with Dyad, so the example needs no external library.
component MetadataTile
parameter gain::Real = 1
variable value::Real
relations
value = gain
metadata {
"Dyad": {
"icons": {
"default": "dyad://Dyad/Frame2D.svg",
"window": "dyad://Dyad/Frame3D.svg"
},
"labels": [{"label": "$(instance)", "x": 500, "y": 1200, "layer": "icon"}]
}
}
end
connector MetadataPin
potential v::Real
flow i::Real
metadata {
"Dyad": {
"icons": {
"default": "dyad://Dyad/PosElectricalPort.svg",
"outline": "dyad://Dyad/NegElectricalPort.svg"
}
}
}
endEach placement gives a bounding box with x1, y1, x2, and y2; iconName selects the drawing within that box.
The two component instances below share MetadataTile and its equation. The two connector instances share MetadataPin and its interface. Their placement selects different drawings.
The window instance selects its alternate icon with this placement:
{"Dyad": {"placement": {"diagram": {
"x1": 650, "y1": 150, "x2": 850, "y2": 350, "iconName": "window"
}}}}Choose symbols appropriate to your component's domain. Icon selection follows these rules:
iconNamenames an entry in the definition'sDyad.iconsmap.Omitting
iconNameselectsdefault. A missing requested name also falls back todefaultwhen available.Names such as
windowandoutlineare local choices made by these definitions.
Supplying your own SVGs ​
Store icons in your library's assets directory. For example, dyad://MyLibrary/resistor.svg identifies assets/resistor.svg in MyLibrary.
{
"Dyad": {
"icons": {
"default": "dyad://MyLibrary/resistor.svg",
"compact": "dyad://MyLibrary/resistor-compact.svg"
}
}
}Put this object after metadata in the definition. An instance selects "compact" with Dyad.placement.diagram.iconName in an internal schematic, or Dyad.placement.icon.iconName on its containing component's external icon. Use a consistent SVG coordinate system, such as viewBox="0 0 1000 1000", to make port and label positions easy to author.
Port and component placement ​
Placement has two layers:
iconpositions an element on its containing component's external representation, such as a public port.diagrampositions an element within the containing component's internal schematic.
This pass-through component positions each port on both layers:
connector MetadataInput
input value::Real
metadata {"Dyad": {"icons": {"default": "dyad://Dyad/RealInput.svg"}}}
end
connector MetadataOutput
output value::Real
metadata {"Dyad": {"icons": {"default": "dyad://Dyad/RealOutput.svg"}}}
end
component GraphicalPassThrough
u = MetadataInput() {"Dyad": {"placement": {
"icon": {"x1": -50, "y1": 450, "x2": 50, "y2": 550},
"diagram": {"x1": 150, "y1": 400, "x2": 350, "y2": 600}
}}}
y = MetadataOutput() {"Dyad": {"placement": {
"icon": {"x1": 950, "y1": 450, "x2": 1050, "y2": 550},
"diagram": {"x1": 650, "y1": 400, "x2": 850, "y2": 600}
}}}
relations
connect(u, y) {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}]}}
endThe central bridge below shows its public ports at the edges of its icon. Double-click bridge to enter its internal diagram: the same ports appear at their internal positions, connected by a line. Use the breadcrumb above the diagram to return to the parent.
The outer bridge instance uses this diagram placement:
{"Dyad": {"placement": {"diagram": {
"x1": 350, "y1": 350, "x2": 650, "y2": 650
}}}}Complete placement demo source.
GraphicalPassThrough puts u and y at opposite icon edges and gives each a separate position in its internal diagram.
Both layers use the same fields:
| Field | Meaning |
|---|---|
x1, y1 | First corner of the placement box |
x2, y2 | Opposite corner of the placement box |
iconName | Icon name from the element's definition; defaults to "default" |
rot | Rotation in degrees; defaults to 0 |
show | Positive integer threshold for displaying an array as individual connectors |
Use these placement conventions:
Supply all four coordinates for each placement layer. Coordinates usually run from top-left to bottom-right; reversing the horizontal coordinates mirrors the icon.
Providing only
iconplacement also uses that placement in the diagram.For connector arrays,
showsets the largest array displayed as individual connectors. Omitting it, or exceeding its threshold, displays a single connector.
This declaration excerpt assumes Pin is defined in scope:
p = Pin() {
"Dyad": {
"placement": {
"icon": {"x1": -50, "y1": 450, "x2": 50, "y2": 550},
"diagram": {"x1": 0, "y1": 100, "x2": 100, "y2": 200, "rot": 0}
}
}
}The coordinates determine the pin's graphical position. The Pin definition determines its physical interface. A visual line has electrical meaning only when the source contains the corresponding connection.
Labels ​
Definition labels are objects in Dyad.labels. Each requires label, x, and y; optional fields are rot, layer, and attrs. layer accepts "icon", "diagram", or "all". attrs maps SVG text attribute names to string values.
This metadata excerpt places the instance name below its icon:
{
"Dyad": {
"labels": [
{"label": "$(instance)", "x": 500, "y": 1100, "layer": "icon"}
]
}
}Connection colors and routes ​
Dyad.path sets SVG attributes on a connection. Use stroke for its color, stroke-width for line width, and stroke-dasharray for a dashed line. Attribute values are strings, including numeric values such as "3".
The upper connection is blue and straight. The lower connection is orange, dashed, and routed through three explicit midpoints. Both use the same signal connector types.
The orange connection sets these SVG attributes under Dyad.path:
{"stroke": "#c45b16", "stroke-width": "3", "stroke-dasharray": "8 5"}Complete colored routes source.
A connector definition can also supply default connection attributes under its definition-level Dyad.path. A connection's own path overrides those defaults.
Reading route coordinates ​
A connection's Dyad metadata can contain edges, junctions, path, and renderStyle. A route describes how to draw an existing connect(...) relation.
| Route field | Meaning |
|---|---|
S, E | Start and end of one edge |
Positive endpoint, such as 1 | Argument of connect, numbered from one |
Negative endpoint, such as -1 | Entry in junctions, numbered from one |
M | Ordered array of intermediate { "x": ..., "y": ... } points |
junctions | Shared points at which edges meet |
renderStyle | "standard" or "markers" |
For example, a three-port connection can meet at one junction:
{
"Dyad": {
"junctions": [{"x": 500, "y": 500}],
"edges": [
{"S": 1, "E": -1},
{"S": 2, "E": -1},
{"S": 3, "E": -1}
]
}
}When editing the argument order of connect, keep the route endpoint indices consistent with that order. The connection arguments establish the network; its edges describe the drawing.
Tags for components and variables ​
Dyad.tags attaches an array of strings to a component instance or variable declaration. Tags let a tool identify declarations with properties such as "calibration" or "measured". The tool gives those words meaning; the metadata schema accepts arbitrary strings.
The following model tags the state x as "measured".
component MetadataDecay
parameter tau::Real = 1
parameter gain::Real = 2
parameter x0::Real = 0
variable x::Real {"Dyad": {"tags": ["measured"]}}
relations
initial x = x0
der(x) = (gain - x) / tau
metadata {
"Dyad": {
"labels": [{"label": "$(instance)", "x": 500, "y": 1200, "layer": "icon"}]
}
}
endAn instance can carry its own tags alongside its placement. The viewer recognizes group1 through group9 as component color groups. Here, slow uses blue (group6) and fast uses orange (group2). Both instances use the same decay equations; their parameter values differ.
The slow instance combines a viewer color group with descriptive tags:
{"Dyad": {"tags": ["group6", "calibration", "thermal"]}}Complete tagged experiment source.
The viewer uses the group6 and group2 tags for tinting; calibration, thermal, and measured remain descriptive data for tools that understand them. Tagging x as measured keeps its decay equation and does not turn it into an input.
The color-group tags apply to component instances. Connector symbols get their appearance from their selected SVG, and connection lines get their color from Dyad.path.
Grouping a parameter panel ​
Group related parameters so users can find them in the parameter panel. This example places tau and gain under Dynamics → Response, and x0 under Initial conditions. The definition's Dyad.panel object specifies that layout.
To inspect the grouping:
Click an empty diagram area to clear the selection.
Click Edit Parameters, the sliders button in the toolbar.
Select Dynamics to see
tauandgainin Response, or Initial conditions to seex0.
The Response group lists its parameters in contents:
{"Response": {"control": "fieldset", "contents": ["tau", "gain"]}}Complete parameter panel source.
| Key | Meaning |
|---|---|
contents | Array of parameter or variable names at this level |
children | Object mapping section or group names to nested panel objects |
control | Optional string hint, such as "tabs" or "fieldset" |
The tree supports a root, sections, and groups within sections. A level can have both contents and children. Reference the declaration name, such as "tau"; each name must identify a parameter or variable and appear at most once in the panel.
Unlisted parameters appear at the root in declaration order. Variables appear in the panel when explicitly named in contents.
Linked metadata ​
Move large placement and routing objects into the definition's _links table to keep declarations readable. Attach {^left} to a declaration or relation to use the table's left entry. Matching the declaration name is a useful convention.
This version of the earlier pass-through moves its port placements and route into _links. It uses the same MetadataInput and MetadataOutput definitions. The same metadata mechanism applies to physical ports.
The marker connect(u, y) {^wire} selects this entry in the definition's root _links object:
{"wire": {"Dyad": {"edges": [{"S": 1, "M": [], "E": 2}]}}}Complete linked pass through source.
Each _links entry contains the entire metadata object for its element, including the namespace. _links belongs at the root of the enclosing definition's metadata, alongside Dyad and custom namespaces.
The compiler reports an invalid metadata link when a marker has no matching entry. When editing linked metadata by hand:
If you rename a declaration, keep its marker unchanged to retain the same metadata.
If you rename a marker, rename its
_linkskey to match. Update any other markers that use that key.If you delete a declaration, remove its
_linksentry only when no remaining marker uses it.
The graphical editor normally maintains these markers and objects together.
A marker such as {^slow} selects an object from _links. That object can contain Dyad.tags, the descriptive strings introduced in Tags for components and variables.
Custom namespaces ​
Use your own namespace for application-specific information.
This complete component stores a custom part identifier:
component TaggedDecay
parameter tau::Real = 1
variable x::Real
relations
initial x = 1
der(x) = -x / tau
metadata {
"ExampleCompany": {
"partNumber": "DECAY-01"
}
}
endA custom namespace stores data for tools that understand it. Adding a field does not by itself add a physical law or tell Studio how to draw it.
Other standard metadata ​
The following keys identify additional metadata you may encounter in library definitions:
| Attachment and key | Purpose |
|---|---|
Definition Dyad.doc.behavior | Boolean controlling generated behavior documentation |
Definition Dyad.experiments, Dyad.tests | Experiment and test configuration for library tooling |
Definition Dyad.deprecated | Deprecation information for users of the definition |
Root analysis Dyad.using | Julia package implementing the analysis |
This table lists the purposes of these keys; it does not define the structures for experiments, tests, or deprecations. The Custom Analyses guide explains how to use Dyad.using for analysis integration.
Graphical metadata and physical type attributes have different homes. Write units, bounds, and guesses in declarations, as described in Parameters, Variables, and Equations. Write icons, positions, and routes in metadata.
Complete authored examples ​
These sources retain the graphical annotations omitted from viewer Code panes. Use the connector and component definitions introduced with each task.
Icon gallery ​
component MetadataIconGallery
solid = MetadataTile() {
"Dyad": {"placement": {"diagram": {
"x1": 100, "y1": 150, "x2": 300, "y2": 350, "iconName": "default"
}}}
}
window = MetadataTile() {
"Dyad": {"placement": {"diagram": {
"x1": 650, "y1": 150, "x2": 850, "y2": 350, "iconName": "window"
}}}
}
filled = MetadataPin() {
"Dyad": {"placement": {"diagram": {
"x1": 160, "y1": 620, "x2": 240, "y2": 700, "iconName": "default"
}}}
}
outline = MetadataPin() {
"Dyad": {"placement": {"diagram": {
"x1": 710, "y1": 620, "x2": 790, "y2": 700, "iconName": "outline"
}}}
}
relations
connect(filled, outline)
endPlacement demo ​
component MetadataPlacementDemo
inlet = MetadataInput() {
"Dyad": {"placement": {"diagram": {"x1": 50, "y1": 450, "x2": 150, "y2": 550}}}
}
bridge = GraphicalPassThrough() {
"Dyad": {"placement": {"diagram": {"x1": 350, "y1": 350, "x2": 650, "y2": 650}}}
}
outlet = MetadataOutput() {
"Dyad": {"placement": {"diagram": {"x1": 850, "y1": 450, "x2": 950, "y2": 550}}}
}
relations
connect(inlet, bridge.u)
connect(bridge.y, outlet)
endColored routes ​
component MetadataColoredRoutes
blue_in = MetadataInput() {"Dyad": {"placement": {"diagram": {"x1": 100, "y1": 200, "x2": 200, "y2": 300}}}}
blue_out = MetadataOutput() {"Dyad": {"placement": {"diagram": {"x1": 800, "y1": 200, "x2": 900, "y2": 300}}}}
orange_in = MetadataInput() {"Dyad": {"placement": {"diagram": {"x1": 100, "y1": 650, "x2": 200, "y2": 750}}}}
orange_out = MetadataOutput() {"Dyad": {"placement": {"diagram": {"x1": 800, "y1": 650, "x2": 900, "y2": 750}}}}
relations
connect(blue_in, blue_out) {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": 2}],
"path": {"stroke": "#2457a7", "stroke-width": "3"}
}
}
connect(orange_in, orange_out) {
"Dyad": {
"edges": [{"S": 1, "M": [{"x": 450, "y": 700}, {"x": 450, "y": 500}, {"x": 850, "y": 500}], "E": 2}],
"path": {"stroke": "#c45b16", "stroke-width": "3", "stroke-dasharray": "8 5"}
}
}
endTagged experiment ​
component MetadataTaggedExperiment
slow = MetadataDecay(tau=5) {
"Dyad": {
"tags": ["group6", "calibration", "thermal"],
"placement": {"diagram": {"x1": 150, "y1": 250, "x2": 400, "y2": 500}}
}
}
fast = MetadataDecay(tau=1) {
"Dyad": {
"tags": ["group2"],
"placement": {"diagram": {"x1": 650, "y1": 250, "x2": 900, "y2": 500}}
}
}
endParameter panel ​
component MetadataPanelDemo
parameter tau::Real = 5
parameter gain::Real = 2
parameter x0::Real = 0.25
response = MetadataDecay(tau=tau, gain=gain, x0=x0) {
"Dyad": {"placement": {"diagram": {"x1": 350, "y1": 300, "x2": 650, "y2": 600}}}
}
metadata {
"Dyad": {
"panel": {
"control": "tabs",
"children": {
"Dynamics": {
"children": {
"Response": {"control": "fieldset", "contents": ["tau", "gain"]}
}
},
"Initial conditions": {"contents": ["x0"]}
}
}
}
}
endLinked pass through ​
component LinkedPassThrough
u = MetadataInput() {^u}
y = MetadataOutput() {^y}
relations
connect(u, y) {^wire}
metadata {
"_links": {
"u": {
"Dyad": {
"placement": {
"icon": {"x1": -50, "y1": 450, "x2": 50, "y2": 550},
"diagram": {"x1": 150, "y1": 400, "x2": 350, "y2": 600}
}
}
},
"y": {
"Dyad": {
"placement": {
"icon": {"x1": 950, "y1": 450, "x2": 1050, "y2": 550},
"diagram": {"x1": 650, "y1": 400, "x2": 850, "y2": 600}
}
}
},
"wire": {
"Dyad": {
"edges": [{"S": 1, "M": [], "E": 2}]
}
}
}
}
end