> For the complete documentation index, see [llms.txt](https://docs.dataclearinghouse.org/dch-2.0-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.dataclearinghouse.org/dch-2.0-documentation/si-docs/dch-onboarding-brick-model-construction/relate-operation.md).

# RELATE Operation

The RELATE operation is used to create relationships between existing things in the models such as hasLocation, or feeds.

## Manifest Configuration

The configuration in the manifest file for the Relate operation has one parameter - the name of the skeleton input csv. The tool will look in the input\_csvs folder for the named csv file.  It is specified as:

```json
        {
            "operation_type": "RELATE",
            "config": {
                "skeleton_file": "skeleton.csv"
            }
        }
```

## Skeleton

The CSV file used with the RELATE operation is referred to as the `skeleton` csv file. &#x20;

This file works on the principle that entities within a row get related to other entities within the same row based on relationship details in the column headers. Relationships constructed in this way can represent many-to-many relationships across entities. Entities are identified in the same format as used within the CREATE operation (Model\_ID|Entity\_Name).

{% hint style="info" %}
Due to the complexity of modelling a full set of relationships between entities within a site or building, consideration of breaking the skeleton CSV into multiple files is supported and recommended where possible.
{% endhint %}

## Examples

Unless otherwise noted, all the examples below use the following manifest file.

{% code title="manifest.json" %}

```json
{
  "manifest_version": "1.0.0",
  "id_mapping": {
    "bld": "dch:org/documentation/site/Relate_Eg1/building/building_eg1#"
  },
  "operations": [
    {
      "operation_type": "CREATE",
      "config": {
        "object_file": "objects.csv"
      }
    },
    {
      "operation_type": "RELATE",
      "config": {
        "skeleton_file": "skeleton.csv"
      }
    }
  ]
}
```

{% endcode %}

As the relate operation relates existing entities, the first operation in the manifest is a create operation (using the `input_csvs/objects.csv` also below).  The various examples show the tool outputs as you provide different `skeleton.csv` files to the relate operation. &#x20;

`input_csvs/objects.csv`

| Model\_ID\|Entity\_Name | Class             |
| ----------------------- | ----------------- |
| bld\|test\_building     | Building          |
| bld\|maintenance        | Room              |
| bld\|library            | Library           |
| bld\|meter              | Electrical\_Meter |
| bld\|fan                | Fan               |
| bld\|power              | Power\_Sensor     |
| bld\|speed              | Speed\_Sensor     |

{% tabs %}
{% tab title="Example 1" %}

### Example 1: Basic skeleton file

***

Each column header contains two required pieces of information and one optional description to aid readability.  The mandatory information is the desired Brick relationship and a reference to the target column. By extending the csv with new columns and new rows there is no limit to the set of desired relationships that can be described between objects in a model.&#x20;

Target column references are 'zero indexed' i.e the first column is referred to as column 0.  To assist users the tool also recognises Excel-style column references, i.e. the first column can alternatively be referred to as column "A"&#x20;

#### Inputs

`skeleton.csv`\
(table below is shown with Excel style column and row numbering, but contents of the csv header row use numeric column references):

<table><thead><tr><th width="70"></th><th width="162">A</th><th width="126">B</th><th width="178">C</th><th width="155">D</th><th width="147">E</th></tr></thead><tbody><tr><td>1</td><td><strong>hasPart, 1</strong></td><td><strong>isPartOf, 0</strong></td><td><strong>hasLocation, 1</strong></td><td><strong>hasPoint, 4</strong></td><td><strong>isPointOf, 3</strong></td></tr><tr><td>2</td><td>bld|test_building</td><td>bld|maintenance</td><td>bld|meter, bld|fan</td><td>bld|meter</td><td>bld|power</td></tr><tr><td>3</td><td>bld|test_building</td><td>bld|library</td><td></td><td>bld|fan</td><td>bld|speed</td></tr></tbody></table>

Understanding this skeleton file cell by cell:

* **Cell A2:**   According to the header in the Skeleton, the column A `hasPart, 1`, (cell A2) is defines in the cell `bld|test_building` (which is a Brick Building if you look at the `objects.csv` file) `hasPart` whatever is on the same row but in the column indexed by 1.  In this case, because of the zero indexing of columns, column 1 refers to column B in the above table.   Cell B2 is the room "`bld|maintenance`.   So cell A2 defines the relationship of the entity `bld|test_building` with a relation of `brick:hasPart` to the other entity `bld|maintenance`. In the resulting building model this would be represented as: \
  `bld:test_building brick:hasPart bld:maintenance .`
* **Cell A3:**  using the same logic as Cell A2, this defines the relationship: `bld|test_building brick:hasPart bld|library .`
* **Cell B2:** The header in column B is `isPartOf, 0`, so Cell B2 defines the relationship `bld|maintenance` relates to `bld|test_building` as an isPartOf relation. In the generated model, this will be represented as:\
  `bld|maintenance brick:isPartOf bld:test_building .`
* **Cell B3:** similarly, create relationship:\
  `bld|library brick:isPartOf bld|test_building .`
* **Cell C2:** This shows how comma-separated lists can be used in a cell to relate multiple relationships to things at once. Column Header for column C is `hasLocation, 1`, so cell C2 says say both the meter and the fan are in the maintenance room i.e. create both of these relationships:
  * `bld|meter brick:hasLocation bld|maintenance .`
  * `bld|fan brick:hasLocation bld|maintenance .`

{% hint style="info" %}
The "target" cell of a relationship can also include a list of things.
{% endhint %}

* **Cell D2:** Following the same pattern, the header of `hasPoint, 4` asks for the triple `bld|meter brick:hasPoint bld|power .`
* **Cell D3:** similarly `bld|fan brick:hasPoint bld|speed .`
* **Cell E2:** this generates the reverse link `bld|power brick:isPointOf bld|meter .`
* **Cell E3:** this generates the link `bld|speed brick:isPointOf bld|fan .`

{% hint style="info" %}
The reverse links are not created for you in this tool.  e.g. looking at cells C2 and B2, the relationships  `bld|maintenance brick:isLocationOf bld|meter` and `bld|maintenance brick:isLocationOf bld|fan` are not automatically created.  If these relationships are desired, the relationships need to be explicitly described in the skeleton CSV by using another column.

However, note when loading models into DCH, such reverse links are automatically created (the BRICK ontology handles it).  So it is not necessary to create them, however it is wise to check if other external tools you may want to use, do correctly inference those reverse links.
{% endhint %}

{% file src="/files/ynvJi01K40UqrB1DZIGx" %}
RELATE Example 1: Full input archive
{% endfile %}

***

#### **Outputs**

Zipping up the manifest, with a input\_csvs folder containing both the objects.csv file and the skeleton.csv file and and running that through the tool will produce the following turtle output file, which contains all those triples:

<pre class="language-turtle" data-title="bld.ttl"><code class="lang-turtle">@prefix bld: &#x3C;dch:org/documentation/site/Relate_Eg1/building/building_eg1#> .
@prefix brick: &#x3C;https://brickschema.org/schema/Brick#> .

bld:fan a brick:Fan ;
<strong>    brick:hasLocation bld:maintenance ;
</strong>    brick:hasPoint bld:speed .

bld:library a brick:Library ;
    brick:isPartOf bld:test_building .

bld:meter a brick:Electrical_Meter ;
    brick:hasLocation bld:maintenance ;
    brick:hasPoint bld:power .

bld:power a brick:Power_Sensor ;
    brick:isPointOf bld:meter .

bld:speed a brick:Speed_Sensor ;
    brick:isPointOf bld:fan .

bld:test_building a brick:Building ;
    brick:hasPart bld:library,
        bld:maintenance ;
    brick:keyValue [ brick:key "mgtool" ;
            brick:value "build date: 2024-04-08" ],
        [ brick:key "mgtool" ;
            brick:value "version: 2.0" ] .

bld:maintenance a brick:Room ;
    brick:isPartOf bld:test_building .
</code></pre>

{% file src="/files/ohtLAxaNtGQKjTcr5CRT" %}
RELATE Example 1: Full output archive
{% endfile %}
{% endtab %}

{% tab title="Example 2" %}

### **Example 2: Alphabetic column references**

***

To make it easier for modellers that create or manipulate CSVs using the popular Excel software it is possible to refer to columns using their alphabetic column name rather than the zero-indexed column number references.&#x20;

The following `skeleton.csv` returns the same output model file as Example 1:

#### Inputs

`skeleton.csv`  \
(table below is shown with Excel style column and row numbering, and Excel style column references):

<table><thead><tr><th width="70"></th><th width="179">A</th><th width="186">B</th><th width="178">C</th><th width="155">D</th><th width="147">E</th></tr></thead><tbody><tr><td>1</td><td><strong>hasPart, B</strong></td><td><strong>isPartOf, A</strong></td><td><strong>hasLocation, A</strong></td><td><strong>hasPoint, E</strong></td><td><strong>isPointOf, D</strong></td></tr><tr><td>2</td><td>bld|test_building</td><td>bld|maintenance</td><td>bld|meter, bld|fan</td><td>bld|meter</td><td>bld|power</td></tr><tr><td>3</td><td>bld|test_building</td><td>bld|library</td><td></td><td>bld|fan</td><td>bld|speed</td></tr></tbody></table>

{% file src="/files/0jFlZPVcA94SwhaFuvZ6" %}
RELATE Example 2: Full input archive
{% endfile %}

***

**Outputs**

{% file src="/files/DRjzue0dZngakMeoQC01" %}
RELATE Example 2: Full output archive
{% endfile %}
{% endtab %}

{% tab title="Example 3" %}

### **Example 3: Optional column descriptors**&#x20;

***

An optional human-readable description can be added to column headers.  This information does not affect constructed models.

The following `skeleton.csv` returns the same output model file as Examples 1 & 2:

#### **Inputs**

`skeleton.csv` \
(with optional header descriptors)

<table><thead><tr><th width="87"></th><th width="162">A</th><th width="203">B</th><th width="266">C</th><th width="118">D</th><th width="231">E</th></tr></thead><tbody><tr><td>1</td><td>hasPart, B</td><td><strong>the_rooms,</strong> isPartOf, A</td><td><strong>the_equipment,</strong> hasLocation, B</td><td>hasPoint, E</td><td><strong>the_points,</strong> isPointOf, D</td></tr><tr><td>2</td><td>bld|test_building</td><td>bld|maintenance</td><td>bld|meter, bld|fan</td><td>bld|meter</td><td>bld|power</td></tr><tr><td>3</td><td>bld|test_building</td><td>bld|library</td><td></td><td>bld|fan</td><td>bld|speed</td></tr></tbody></table>

{% file src="/files/nvNxBYTlmgUPSJMfTT1o" %}
RELATE Example 3: Full input archive
{% endfile %}

***

#### **Outputs**

{% file src="/files/qf28GqxxlomKIDWyxiII" %}
RELATE Example 3: Full output archive
{% endfile %}

{% hint style="info" %}
Note sometimes it is desirable for readability to have multiple column headers the same i.e two columns with `hasPart, A`.  When you do this the tool will issue a warning to flag it, in case it was unintentional.  Adding unique descriptions to those duplicate column headers is a way of supressing those warnings when such duplication was intentional for readability.&#x20;
{% endhint %}
{% endtab %}

{% tab title="Example 4" %}

### **Example 4: Commenting out columns**

***

As the skeleton CSVs can become complex the model generation tooling offers a way of commenting out/omitting columns.  Starting a description in a column header with a `#` character means that the relationships for that column won't be made. Important to note though, is that relationships in other columns that reference things in the column will still be made.&#x20;

#### Inputs

`skeleton.csv`

<table><thead><tr><th></th><th width="198">A</th><th width="244">B</th><th width="285">C</th><th width="148">D</th><th width="258">E</th></tr></thead><tbody><tr><td>1</td><td>#<strong>,</strong> hasPart, B</td><td>the_rooms, isPartOf, A</td><td>the_equipment, hasLocation, B</td><td>hasPoint, E</td><td><strong>#the_points,</strong> isPointOf, D</td></tr><tr><td>2</td><td>bld|test_building</td><td>bld|maintenance</td><td>bld|meter,bld|fan</td><td>bld|meter</td><td>bld|power</td></tr><tr><td>3</td><td>bld|test_building</td><td>bld|library</td><td></td><td>bld|fan</td><td>bld|speed</td></tr></tbody></table>

{% file src="/files/iSkxmFZQU9np2lyBMbdw" %}
RELATE Example 4: Full input archive
{% endfile %}

***

#### Outputs

The tool produces the following model output file, which contains fewer relationships than in the previous examples:

{% code title="bld.ttl" %}

```turtle
@prefix bld: <dch:org/documentation/site/Relate_Eg4/building/building_eg4#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .

bld:fan a brick:Fan ;
    brick:hasLocation bld:maintenance ;
    brick:hasPoint bld:speed .

bld:library a brick:Library ;
    brick:isPartOf bld:test_building .

bld:meter a brick:Electrical_Meter ;
    brick:hasLocation bld:maintenance ;
    brick:hasPoint bld:power .

bld:power a brick:Power_Sensor .

bld:speed a brick:Speed_Sensor .

bld:maintenance a brick:Room ;
    brick:isPartOf bld:test_building .

bld:test_building a brick:Building ;
    brick:keyValue [ brick:key "mgtool" ;
            brick:value "build date: 2024-04-08" ],
        [ brick:key "mgtool" ;
            brick:value "version: 2.0" ] .
```

{% endcode %}

{% hint style="info" %}
Specifically note the building no longer has hasPart relationships to the rooms as the hasPart column is commented, but the rooms are still part of the building - i.e references to things in commented columns are still made, but the relationships from things in the commented columns are not made.  Similarly the points no longer have the isPointOf  relationships to the equipment but the equipment still has the hasPoint relationships to the points.
{% endhint %}

{% file src="/files/qYa4BfOt1Jaw0p0OXH5w" %}
RELATE Example 4: Full output archive
{% endfile %}
{% endtab %}

{% tab title="Example 5" %}

### **Example 5: Splitting the skeleton csv up for readability**

***

As the skeleton files can become both very wide and very long they can be difficult to comprehend. It is highly recommended you split up the skeleton files into multiple files to help with comprehension. e.g., you may have one skeleton file that describes the hasPart relationships between all the locations, and another that covers the same relationship for equipment and another for the feeds relationship, etc.

This example splits the skeleton csv up into three separate files.  How to split files up is an arbitrary choice but in this instance one csv declares the relationships between locations and other locations, one csv declares the relationships between equipment and locations and one that declares the relationships concerning points. To produce the full model the Relate operation needs to be run three times, once with each skeleton csv file i.e. declared three times in the manifest:

#### Inputs

{% code title="manifest.json" %}

```json
{
  "manifest_version": "1.0.0",
  "id_mapping": {
    "bld": "dch:org/documentation/site/Relate_Eg5/building/building_eg5#"
  },
  "operations": [
    {
      "operation_type": "CREATE",
      "config": {
        "object_file": "objects.csv"
      }
    },
    {
      "operation_type": "RELATE",
      "config": {
        "skeleton_file": "skeleton_locations.csv"
      }
    },
    {
      "operation_type": "RELATE",
      "config": {
        "skeleton_file": "skeleton_equipment.csv"
      }
    },
    {
      "operation_type": "RELATE",
      "config": {
        "skeleton_file": "skeleton_points.csv"
      }
    }
  ]
}
```

{% endcode %}

`input_csvs/skeleton_locations.csv`

<table><thead><tr><th width="62"></th><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>hasPart, B</td><td>isPartOf, A</td></tr><tr><td>2</td><td>bld|test_building</td><td>bld|maintenance</td></tr><tr><td>3</td><td>bld|test_building</td><td>bld|library</td></tr></tbody></table>

`input_csvs/skeleton_equipment.csv`

<table><thead><tr><th width="58.333333333333314"></th><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>#, isLocationOf, B</td><td>hasLocation, A</td></tr><tr><td>2</td><td>bld|maintenance</td><td>bld|meter, bld|fan</td></tr></tbody></table>

`input_csvs/skeleton_points.csv`

<table><thead><tr><th width="64.33333333333331"></th><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>hasPoint, B</td><td> isPointOf, A</td></tr><tr><td>2</td><td>bld|meter</td><td>bld|power</td></tr><tr><td>3</td><td>bld|fan</td><td>bld|speed</td></tr></tbody></table>

{% file src="/files/9Qs7NTL2G2oOVo1gqni6" %}
RELATE Example 5: Full input archive
{% endfile %}

***

**Outputs**

The tool results in the same outputs as examples 1-3, demonstrating that splitting the skeleton CSV into multiple files is an effective way to divide your model without impacting on the quality of the model.

{% hint style="info" %}
Note the commented column A in skeleton\_equipment.csv is to make the output identical to the previous examples.  It is not required, and if omitted the output Turtle file will include the extra reverse isLocationOf triple.&#x20;
{% endhint %}

{% file src="/files/AbiFw8WKReA4jh4EQIeb" %}
RELATE Example 5: Full output archive
{% endfile %}
{% endtab %}
{% endtabs %}
