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.
Panel grouping preserves parameter values: tau remains 5 and passes that value to response.
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. Renaming or deleting a declaration should therefore preserve or update its metadata reference as appropriate. The graphical editor normally maintains these markers and objects together.
A _links marker such as {^slow} is a metadata reference, whereas Dyad.tags is data inside the referenced object. These two uses of “tag” serve different purposes.
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
Other supported metadata has specific purposes:
| 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 |
Experiments, tests, and deprecations have their own structured data requirements; consult the relevant library tooling before adding them. The Custom Analyses guide explains 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