Skip to content

A Complete Worked Example

This chapter is one report, whole: a telescope mass report exercising most of what the previous chapters covered — descendant collection, type and property filters, ordering, projection, query-invokes-query, relationship traversal for satisfy and verify, sections, static and inline-run paragraphs with a link and a cross-reference, plain and grouped tables, a numbered list, and two diagrams (a declared view and a plain element with a stated kind and direction).

Both files below are committed in this repository and kept in lockstep by a test that re-renders the source and compares against the committed output — so what you read here is exactly what the current binary produces.

Render it yourself from the repository root:

$ sysml docs/manual/examples/observatory.sysml \
    -render-document Observatory::MassReport -o observatory.md

or as a PDF (how inline runs and anchors render):

$ sysml docs/manual/examples/observatory.sysml \
    -render-document Observatory::MassReport \
    -doc-form pdf -pdf-title-page -pdf-toc -pdf-number-sections \
    -o observatory.pdf

The full source

package Observatory {
    private import DocumentQueries::*;
    private import KerML::Root::Element;
    private import ScalarValues::*;
    private import Views::*;

    // ---- The system model the report is generated from ----

    part def Subsystem {
        attribute mass : Real;
        attribute zone : String;
    }

    part telescope {
        part optics : Subsystem {
            attribute redefines mass = 8.5;
            attribute redefines zone = "payload";
        }
        part segmentControl : Subsystem {
            attribute redefines mass = 20.0;
            attribute redefines zone = "payload";
        }
        part mount : Subsystem {
            attribute redefines mass = 15.0;
            attribute redefines zone = "support";
        }
    }

    port def DataPort;

    part def Camera {
        port output : DataPort;
    }

    part def Recorder {
        port input : DataPort;
    }

    part imagingChain {
        part camera : Camera;
        part recorder : Recorder;
        connection link connect camera.output to recorder.input;
    }

    requirement massRequirement;
    part observatory {
        satisfy massRequirement by telescope;
    }

    verification def MassTest;
    verification massVerification : MassTest {
        objective {
            verify massRequirement;
        }
    }

    view interconnectView {
        expose imagingChain;
        render asInterconnectionDiagram;
    }

    // ---- Document queries ----

    calc def Subsystems :> Query {
        in root : Element;
        WhereType(
            source = Descendants(source = root, maxDepth = 3),
            type = "PartUsage"
        )
    }

    calc def SubsystemTable :> Query {
        in root : Element;
        Project(
            source = OrderBy(
                source = Subsystems(root = root),
                property = "name",
                direction = "ascending",
                missing = "last",
                multiple = "error"
            ),
            properties = ("name", "mass")
        )
    }

    calc def HeavySubsystemNames :> Query {
        in root : Element;
        in threshold : String;
        Project(
            source = OrderBy(
                source = WhereFeature(
                    source = Subsystems(root = root),
                    'feature' = "mass",
                    operator = ">=",
                    value = threshold
                ),
                property = "name",
                direction = "ascending",
                missing = "last",
                multiple = "error"
            ),
            properties = ("name")
        )
    }

    calc def ZonedSubsystems :> Query {
        in root : Element;
        Project(
            source = OrderBy(
                source = Subsystems(root = root),
                property = "name",
                direction = "ascending",
                missing = "last",
                multiple = "error"
            ),
            properties = ("zone", "name", "mass")
        )
    }

    calc def SatisfyingParts :> Query {
        in req : Element;
        Project(
            source = RelatedElements(
                source = req,
                relationshipKind = "satisfaction",
                direction = "incoming",
                maxDepth = 1
            ),
            properties = ("name", "qualifiedName")
        )
    }

    calc def VerifyingElements :> Query {
        in req : Element;
        Project(
            source = RelatedElements(
                source = req,
                relationshipKind = "verification",
                direction = "incoming",
                maxDepth = 1
            ),
            properties = ("qualifiedName")
        )
    }

    // ---- The document definition ----

    part def MassReport :> Document {
        attribute redefines title = "Telescope Mass Report";

        part intro : Paragraph {
            attribute redefines text = "Mass rollup and requirement status for the telescope assembly.";
        }

        part guide : Paragraph {
            part lead : Span {
                attribute redefines text = "This report is";
            }
            part generated : Span {
                attribute redefines text = "generated";
                attribute redefines style = "emphasis";
            }
            part sourceNote : Span {
                attribute redefines text = "from the model by";
            }
            part tool : Span {
                attribute redefines text = "sysml -render-document";
                attribute redefines style = "code";
            }
            part docsLink : Link {
                attribute redefines text = "(OpenSysML)";
                attribute redefines target = "https://opensysml.org/";
            }
            part see : Span {
                attribute redefines text = "— masses are tabulated in";
            }
            part massesRef : Ref {
                ref redefines target = breakdown;
            }
        }

        part breakdown : Section {
            attribute redefines title = "Subsystem Masses";

            part masses : Table {
                attribute redefines caption = "All subsystems by mass";
                calc rows : SubsystemTable {
                    in root = telescope;
                }
            }

            part zones : Table {
                attribute redefines caption = "Subsystems grouped by zone";
                attribute redefines groupBy = "zone";
                calc rows : ZonedSubsystems {
                    in root = telescope;
                }
            }

            part heavy : Section {
                attribute redefines title = "Heavy Subsystems";

                part note : Paragraph {
                    attribute redefines text = "Subsystems at or above 10 kg:";
                }

                part heavyItems : List {
                    attribute redefines style = "number";
                    calc items : HeavySubsystemNames {
                        in root = telescope;
                        in threshold = "10";
                    }
                }
            }
        }

        part requirements : Section {
            attribute redefines title = "Mass Requirement";

            part satisfiers : Table {
                attribute redefines caption = "Parts satisfying the mass requirement";
                calc rows : SatisfyingParts {
                    in req = massRequirement;
                }
            }

            part verifiers : Table {
                attribute redefines caption = "Verifications of the mass requirement";
                calc rows : VerifyingElements {
                    in req = massRequirement;
                }
            }
        }

        part diagrams : Section {
            attribute redefines title = "Diagrams";

            part imaging : Diagram {
                attribute redefines caption = "Imaging chain interconnection";
                ref redefines source = interconnectView;
            }

            part structure : Diagram {
                attribute redefines caption = "Telescope part tree, left to right";
                attribute redefines kind = "tree";
                attribute redefines direction = "LR";
                ref redefines source = telescope;
            }
        }
    }
}

The full rendered output

# Telescope Mass Report

Mass rollup and requirement status for the telescope assembly.

This report is *generated* from the model by `sysml -render-document` [(OpenSysML)](<https://opensysml.org/>) — masses are tabulated in [Subsystem Masses](#breakdown)

<a id="breakdown"></a>

## Subsystem Masses

<!-- caption -->
*All subsystems by mass*

| name | mass |
| --- | --- |
| mount | 15 |
| optics | 8.5 |
| segmentControl | 20 |

<!-- caption -->
*Subsystems grouped by zone*

**zone: support**

| zone | name | mass |
| --- | --- | --- |
| support | mount | 15 |

**zone: payload**

| zone | name | mass |
| --- | --- | --- |
| payload | optics | 8.5 |
| payload | segmentControl | 20 |

### Heavy Subsystems

Subsystems at or above 10 kg:

1. mount
2. segmentControl

## Mass Requirement

<!-- caption -->
*Parts satisfying the mass requirement*

| name | qualifiedName |
| --- | --- |
| telescope | Observatory::telescope |

<!-- caption -->
*Verifications of the mass requirement*

| qualifiedName |
| --- |
| Observatory::massVerification |

## Diagrams

<!-- caption -->
*Imaging chain interconnection*

```mermaid
%% Observatory::interconnectView — interconnection rendering (render asInterconnectionDiagram)
flowchart LR
  subgraph n0 ["part Observatory::imagingChain"]
    n1["part camera (Camera)"]
    n2["part recorder (Recorder)"]
  end
  n1 ---|"link"| n2
```

<!-- caption -->
*Telescope part tree, left to right*

```mermaid
%% tree rendering (the diagram states kind "tree")
flowchart LR
  n0["part Observatory::telescope"]
  n1["part optics (Subsystem)"]
  n2["attribute mass"]
  n1 --- n2
  n3["attribute zone"]
  n1 --- n3
  n0 --- n1
  n4["part segmentControl (Subsystem)"]
  n5["attribute mass"]
  n4 --- n5
  n6["attribute zone"]
  n4 --- n6
  n0 --- n4
  n7["part mount (Subsystem)"]
  n8["attribute mass"]
  n7 --- n8
  n9["attribute zone"]
  n7 --- n9
  n0 --- n7
```

Reading it back

A few details worth pausing on:

  • The group order differs from the row order. Rows sort by name (mount, optics, segmentControl), so support is the first zone encountered and its subtable comes first — group order is order of first appearance.
  • WhereType + Descendants is the collection idiom: Subsystems collects part usages under a root once, and four other queries reuse it by invocation with different filters and projections.
  • The cross-reference resolves to the section's title. massesRef names no text, so it renders as the target's title, linked to the anchor emitted before the section.
  • mount renders as 15, not 15.0 — reals render in shortest notation.
  • The traversal queries feed tables. SatisfyingParts and VerifyingElements are the cookbook's satisfy/verify recipes with a Project on the end, which is all a traceability table is.