Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion libs/@hashintel/petrinaut/src/constants/ui-subviews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { simulationSettingsSubView } from "../views/Editor/panels/BottomPanel/su
import { simulationTimelineSubView } from "../views/Editor/panels/BottomPanel/subviews/simulation-timeline";
import { differentialEquationsListSubView } from "../views/Editor/panels/LeftSideBar/subviews/differential-equations-list";
import { entitiesTreeSubView } from "../views/Editor/panels/LeftSideBar/subviews/entities-tree";
import { netsListSubView } from "../views/Editor/panels/LeftSideBar/subviews/nets-list";
import { nodesListSubView } from "../views/Editor/panels/LeftSideBar/subviews/nodes-list";
import { parametersListSubView } from "../views/Editor/panels/LeftSideBar/subviews/parameters-list";
import { typesListSubView } from "../views/Editor/panels/LeftSideBar/subviews/types-list";
Expand All @@ -20,9 +21,13 @@ export const LEFT_SIDEBAR_SUBVIEWS: SubView[] = [
typesListSubView,
differentialEquationsListSubView,
parametersListSubView,
netsListSubView,
];

export const LEFT_SIDEBAR_TREE_SUBVIEWS: SubView[] = [entitiesTreeSubView];
export const LEFT_SIDEBAR_TREE_SUBVIEWS: SubView[] = [
entitiesTreeSubView,
netsListSubView,
];

// Base subviews always visible in the bottom panel
export const BOTTOM_PANEL_SUBVIEWS: SubView[] = [
Expand Down
47 changes: 47 additions & 0 deletions libs/@hashintel/petrinaut/src/core/types/sdcpn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export type Place = {
colorId: null | ID;
dynamicsEnabled: boolean;
differentialEquationId: null | ID;
/** When true, this place is exposed as a port on component instances of this subnet. */
isPort?: boolean;
visualizerCode?: string;
showAsInitialState?: boolean;
// UI positioning
Expand Down Expand Up @@ -112,13 +114,58 @@ export type Scenario = {
};
};

/**
Copy link
Copy Markdown

@augmentcode augmentcode Bot Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libs/@hashintel/petrinaut/src/core/types/sdcpn.ts:L117 — The JSDoc for “An instance of a subnet…” currently sits above Wire, so it looks like it’s documenting the wrong type (and the Wire docblock follows immediately after). Consider moving that first docblock onto ComponentInstance to avoid misleading generated docs/tooltips.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

* An instance of a subnet placed inside a net.
* It references a subnet definition and provides concrete parameter values.
*/
/**
* A wire connects a place in the parent net to a place inside the subnet.
*/
export type Wire = {
/** ID of a place in the parent net (external to the subnet). */
externalPlaceId: ID;
/** ID of a place inside the referenced subnet. */
internalPlaceId: ID;
};

export type ComponentInstance = {
id: ID;
/** Display name for this instance. */
name: string;
/** ID of the subnet this instance instantiates. */
subnetId: ID;
/**
* Concrete values for the subnet's parameters.
* Keys are parameter IDs from the referenced subnet; values are expressions.
*/
parameterValues: Record<ID, string>;
/** Connections between places in the parent net and places inside the subnet. */
wiring: Wire[];
// UI positioning
x: number;
y: number;
};

export type Subnet = {
id: ID;
name: string;
places: Place[];
transitions: Transition[];
types: Color[];
differentialEquations: DifferentialEquation[];
parameters: Parameter[];
componentInstances?: ComponentInstance[];
};

export type SDCPN = {
places: Place[];
transitions: Transition[];
types: Color[];
differentialEquations: DifferentialEquation[];
parameters: Parameter[];
scenarios?: Scenario[];
subnets?: Subnet[];
componentInstances?: ComponentInstance[];
};

export type MinimalNetMetadata = {
Expand Down
218 changes: 218 additions & 0 deletions libs/@hashintel/petrinaut/src/examples/hospital-network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import { SNAP_GRID_SIZE } from "../constants/ui";
import type { SDCPN } from "../core/types/sdcpn";

/**
* Hospital Network example — demonstrates subnets.
*
* The root net models patient flow between departments (ER → Ward → Discharge).
* A subnet models the internal triage process within the ER department.
*/
export const hospitalNetwork: { title: string; petriNetDefinition: SDCPN } = {
title: "Hospital Network",
petriNetDefinition: {
places: [
{
id: "place__er",
name: "Emergency Room",
colorId: null,
dynamicsEnabled: false,
differentialEquationId: null,
showAsInitialState: true,
x: -20 * SNAP_GRID_SIZE,
y: 10 * SNAP_GRID_SIZE,
},
{
id: "place__ward",
name: "Hospital Ward",
colorId: null,
dynamicsEnabled: false,
differentialEquationId: null,
x: 0 * SNAP_GRID_SIZE,
y: 10 * SNAP_GRID_SIZE,
},
{
id: "place__discharged",
name: "Discharged",
colorId: null,
dynamicsEnabled: false,
differentialEquationId: null,
x: 20 * SNAP_GRID_SIZE,
y: 10 * SNAP_GRID_SIZE,
},
],
transitions: [
{
id: "transition__admit",
name: "Admit",
inputArcs: [{ placeId: "place__er", weight: 1, type: "standard" }],
outputArcs: [{ placeId: "place__ward", weight: 1 }],
lambdaType: "stochastic",
lambdaCode:
"export default Lambda((tokens, parameters) => parameters.admission_rate)",
transitionKernelCode:
'export default TransitionKernel(() => {\n return {\n "Hospital Ward": [{}],\n };\n});',
x: -10 * SNAP_GRID_SIZE,
y: 5 * SNAP_GRID_SIZE,
},
{
id: "transition__discharge",
name: "Discharge",
inputArcs: [{ placeId: "place__ward", weight: 1, type: "standard" }],
outputArcs: [{ placeId: "place__discharged", weight: 1 }],
lambdaType: "stochastic",
lambdaCode:
"export default Lambda((tokens, parameters) => parameters.discharge_rate)",
transitionKernelCode:
"export default TransitionKernel(() => {\n return {\n Discharged: [{}],\n };\n});",
x: 10 * SNAP_GRID_SIZE,
y: 5 * SNAP_GRID_SIZE,
},
],
types: [],
differentialEquations: [],
parameters: [
{
id: "param__admission_rate",
name: "Admission Rate",
variableName: "admission_rate",
type: "real",
defaultValue: "2",
},
{
id: "param__discharge_rate",
name: "Discharge Rate",
variableName: "discharge_rate",
type: "real",
defaultValue: "1",
},
],
componentInstances: [
{
id: "instance__er_triage",
name: "ER Triage Unit",
subnetId: "subnet__er_triage",
parameterValues: {
param__triage_rate: "5",
param__treatment_rate: "3",
},
wiring: [
{
externalPlaceId: "place__er",
internalPlaceId: "place__waiting",
},
{
externalPlaceId: "place__ward",
internalPlaceId: "place__treated",
},
],
x: -10 * SNAP_GRID_SIZE,
y: 20 * SNAP_GRID_SIZE,
},
],
scenarios: [
{
id: "scenario__normal_day",
name: "Normal Day",
description: "Typical daily patient flow through the hospital.",
scenarioParameters: [],
parameterOverrides: {},
initialState: {
type: "per_place",
content: {
place__er: "10",
place__ward: "5",
place__discharged: "0",
},
},
},
],
subnets: [
{
id: "subnet__er_triage",
name: "ER Triage",
places: [
{
id: "place__waiting",
name: "Waiting",
colorId: null,
dynamicsEnabled: false,
differentialEquationId: null,
isPort: true,
showAsInitialState: true,
x: -15 * SNAP_GRID_SIZE,
y: 5 * SNAP_GRID_SIZE,
},
{
id: "place__assessment",
name: "Assessment",
colorId: null,
dynamicsEnabled: false,
differentialEquationId: null,
x: 0 * SNAP_GRID_SIZE,
y: 5 * SNAP_GRID_SIZE,
},
{
id: "place__treated",
name: "Treated",
colorId: null,
dynamicsEnabled: false,
differentialEquationId: null,
isPort: true,
x: 15 * SNAP_GRID_SIZE,
y: 5 * SNAP_GRID_SIZE,
},
],
transitions: [
{
id: "transition__triage",
name: "Triage",
inputArcs: [
{ placeId: "place__waiting", weight: 1, type: "standard" },
],
outputArcs: [{ placeId: "place__assessment", weight: 1 }],
lambdaType: "stochastic",
lambdaCode:
"export default Lambda((tokens, parameters) => parameters.triage_rate)",
transitionKernelCode:
"export default TransitionKernel(() => {\n return {\n Assessment: [{}],\n };\n});",
x: -7 * SNAP_GRID_SIZE,
y: 0 * SNAP_GRID_SIZE,
},
{
id: "transition__treat",
name: "Treat",
inputArcs: [
{ placeId: "place__assessment", weight: 1, type: "standard" },
],
outputArcs: [{ placeId: "place__treated", weight: 1 }],
lambdaType: "stochastic",
lambdaCode:
"export default Lambda((tokens, parameters) => parameters.treatment_rate)",
transitionKernelCode:
"export default TransitionKernel(() => {\n return {\n Treated: [{}],\n };\n});",
x: 7 * SNAP_GRID_SIZE,
y: 0 * SNAP_GRID_SIZE,
},
],
types: [],
differentialEquations: [],
parameters: [
{
id: "param__triage_rate",
name: "Triage Rate",
variableName: "triage_rate",
type: "real",
defaultValue: "5",
},
{
id: "param__treatment_rate",
name: "Treatment Rate",
variableName: "treatment_rate",
type: "real",
defaultValue: "3",
},
],
},
],
},
};
Loading
Loading