# SHACL Validation Rules Documentation - ABP-Dev Environment

**Version:** 1.0
**Last Updated:** 2026-02-17
**Environment:** abp-dev
**Data Model:** sp_rmdm_dm (RMDM v8.19)

---

## Table of Contents

1. [Overview](#overview)
2. [Architecture](#architecture)
3. [Entity Relationship Diagram](#entity-relationship-diagram)
4. [Validation Hierarchy](#validation-hierarchy)
5. [Severity Levels](#severity-levels)
6. [Detailed Rules by Entity](#detailed-rules-by-entity)
   - [Tag](#1-tag-base-interface)
   - [Pump](#2-pump)
   - [Compressor](#3-compressor)
   - [HeatExchanger](#4-heatexchanger)
   - [TimeSeriesExt](#5-timeseriesext)
   - [EquipmentClass](#6-equipmentclass)
   - [EquipmentType](#7-equipmenttype)
   - [FailureNotification](#8-failurenotification)
7. [SPARQL Constraints](#sparql-constraints)
8. [CDF SDK Integration](#cdf-sdk-integration)
9. [Validation Flow](#validation-flow)
10. [Standards Compliance](#standards-compliance)
11. [Property Validation Matrix](#property-validation-matrix)
12. [Future Enhancements](#future-enhancements)

---

## Overview

The ABP-Dev SHACL validation rules provide comprehensive data quality validation for industrial equipment and time series data. The rules enforce:

- **ENS (Equipment Numbering System)** standards
- **Norsok Z-DP-002** tag naming conventions
- **ISO 14224** equipment classification
- **CDF data quality** best practices

### Key Statistics

| Metric | Count |
|--------|-------|
| Total SHACL Rule Files | 10 |
| Total Lines of Validation Code | ~2,140 |
| Entity Types Validated | 10 |
| Required Properties (Violation) | 32 |
| Recommended Properties (Warning) | 35+ |
| SPARQL Constraints | 10 |
| CDF SDK Functions Used | 3 |
| INDSL Functions Used | 2 |

---

## Architecture

### Design Principles

1. **Single Source of Truth**: Tag validation is defined once and inherited by all equipment types
2. **Inheritance via sh:node**: Equipment types inherit Tag validations to avoid duplication
3. **Auto-Loading**: Reference shapes trigger automatic loading of related entities
4. **Layered Severity**: Critical violations, recommended warnings, and informational checks
5. **Standards-Based**: Aligned with ENS, Norsok, and ISO 14224 standards

### File Structure

```
config/environments/abp-dev/shacl_rules/
├── tag_shacl_rules.ttl                      (~300 lines) - Foundation for all equipment
├── pump_shacl_rules.ttl                     (~180 lines) - Inherits Tag
├── compressor_shacl_rules.ttl               (~200 lines) - Inherits Tag
├── heatexchanger_shacl_rules.ttl            (~350 lines) - Inherits Tag + extensive design validation
├── timeseriesext_shacl_rules.ttl            (~280 lines) - Time series data quality
├── equipmentclass_shacl_rules.ttl           (~100 lines) - ISO 14224 classification
├── equipmenttype_shacl_rules.ttl            (~130 lines) - ISO 14224 classification
├── failurenotification_shacl_rules.ttl      (~400 lines) - ISO 14224 failure tracking + RCA
├── failuremode_shacl_rules.ttl              (~100 lines) - ISO 14224 failure modes
└── failurecause_shacl_rules.ttl             (~100 lines) - ISO 14224 root cause analysis
```

---

## Entity Relationship Diagram

```mermaid
graph TB
    subgraph "Equipment Hierarchy"
        Tag[Tag<br/>Base Interface]
        Pump[Pump<br/>implements Tag]
        Compressor[Compressor<br/>implements Tag]
        HeatExchanger[HeatExchanger<br/>implements Tag]
    end

    subgraph "Classification"
        EqClass[EquipmentClass<br/>ISO 14224]
        EqType[EquipmentType<br/>ISO 14224]
    end

    subgraph "Asset Management"
        AssetClass[CogniteAssetClass]
        AssetType[CogniteAssetType]
    end

    subgraph "Time Series"
        TS[TimeSeriesExt<br/>Measurement Data]
    end

    subgraph "Failure Tracking & RCA"
        FN[FailureNotification<br/>ISO 14224 Failures]
        FM[FailureMode<br/>ISO 14224]
        WO[WorkOrder<br/>Maintenance]
    end

    Tag -->|ISOEquipmentClass| EqClass
    Tag -->|ISOEquipmentType| EqType
    Tag -->|assetClass| AssetClass
    Tag -->|type| AssetType

    Pump -->|inherits| Tag
    Compressor -->|inherits| Tag
    HeatExchanger -->|inherits| Tag

    EqType -->|equipmentClass| EqClass

    TS -->|assets| Tag
    TS -->|assets| Pump
    TS -->|assets| Compressor
    TS -->|assets| HeatExchanger

    FN -->|failureMode| FM
    FN -->|workOrders| WO

    style Tag fill:#e1f5ff,stroke:#0066cc,stroke-width:3px
    style Pump fill:#fff4e1,stroke:#ff9900,stroke-width:2px
    style Compressor fill:#fff4e1,stroke:#ff9900,stroke-width:2px
    style HeatExchanger fill:#fff4e1,stroke:#ff9900,stroke-width:2px
    style EqClass fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style EqType fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style TS fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
    style FN fill:#ffebee,stroke:#d32f2f,stroke-width:2px
    style FM fill:#fce4ec,stroke:#c2185b,stroke-width:2px
    style WO fill:#fff3e0,stroke:#f57c00,stroke-width:2px
```

---

## Validation Hierarchy

```mermaid
graph TD
    subgraph "Validation Layers"
        TagShape[TagShape<br/>Foundation Validation]

        PumpShape[PumpShape<br/>Pump-Specific]
        CompressorShape[CompressorShape<br/>Compressor-Specific]
        HeatExchangerShape[HeatExchangerShape<br/>HeatExchanger-Specific]

        TagSystemShape[TagSystemNameConsistencyShape<br/>SPARQL Constraint]
    end

    subgraph "Auto-Loading References"
        EqClassShape[EquipmentClassShape]
        EqTypeShape[EquipmentTypeShape]
        AssetClassShape[CogniteAssetClassShape]
        AssetTypeShape[CogniteAssetTypeShape]
    end

    subgraph "Time Series Layer"
        TSBasicShape[TimeSeriesExtBasicPropertiesShape]
        TSExternalIdShape[TimeSeriesExtExternalIdMatchShape]
        TSRecentDataShape[TimeSeriesExtRecentDataShape]
        TSExistsShape[TimeSeriesExtExistsShape]
        TSNoGapsShape[TimeSeriesExtNoGapsShape]
    end

    TagShape -->|sh:node| PumpShape
    TagShape -->|sh:node| CompressorShape
    TagShape -->|sh:node| HeatExchangerShape

    TagShape -->|references| EqClassShape
    TagShape -->|references| EqTypeShape
    TagShape -->|references| AssetClassShape
    TagShape -->|references| AssetTypeShape

    TagShape -.->|validates| TagSystemShape

    TSBasicShape -.->|SPARQL| TSExternalIdShape
    TSBasicShape -.->|CDF SDK| TSRecentDataShape
    TSBasicShape -.->|CDF SDK| TSExistsShape
    TSBasicShape -.->|INDSL| TSNoGapsShape

    style TagShape fill:#0066cc,color:#fff,stroke:#003d7a,stroke-width:4px
    style PumpShape fill:#ff9900,color:#fff,stroke:#cc7a00,stroke-width:2px
    style CompressorShape fill:#ff9900,color:#fff,stroke:#cc7a00,stroke-width:2px
    style HeatExchangerShape fill:#ff9900,color:#fff,stroke:#cc7a00,stroke-width:2px
    style TSBasicShape fill:#9c27b0,color:#fff,stroke:#6a1b9a,stroke-width:2px
```

---

## Severity Levels

### Overview

```mermaid
pie title "Validation Rules by Severity"
    "Violation (Critical)" : 32
    "Warning (Recommended)" : 35
    "Info (Optional)" : 6
```

### Severity Definitions

| Severity | Symbol | Usage | Example |
|----------|--------|-------|---------|
| **sh:Violation** | 🔴 | Critical requirements that MUST be met | Tag name required, must follow ENS format |
| **sh:Warning** | 🟡 | Recommended best practices that SHOULD be present | HeatExchanger should have design data |
| **sh:Info** | 🔵 | Informational checks, optional enhancements | Time series outlier detection |

---

## Detailed Rules by Entity

### 1. Tag (Base Interface)

**File:** `tag_shacl_rules.ttl`
**Lines:** ~300
**Purpose:** Foundation validation for all equipment instances

#### Required Properties (🔴 sh:Violation)

| Property | Validation | Constraint | Message |
|----------|------------|------------|---------|
| **name** | Non-empty string | `minCount: 1`, `minLength: 1` | Tag name is REQUIRED |
| **name** | Pattern match | Regex: `^[0-9]{2}-[A-Z0-9]{1,10}-[0-9]{4}$` | Must follow ENS/Norsok format |
| **description** | Non-empty string | `minCount: 1`, `minLength: 1` | Tag description is REQUIRED |
| **system** | 2-digit code | `minCount: 1`, pattern: `^[0-9]{2}$` | System property is REQUIRED |
| **functionCode** | Non-empty string | `minCount: 1`, `minLength: 1` | Function code is REQUIRED |
| **functionCodeDesc** | Non-empty string | `minCount: 1`, `minLength: 1` | Function code description is REQUIRED |
| **dateInstalled** | String (can be empty) | `minCount: 1` | Date installed property is REQUIRED |
| **tagDiscipline** | Non-empty string | `minCount: 1`, `minLength: 1` | Tag discipline is REQUIRED |
| **ISOEquipmentClass** | Reference | `minCount: 1`, `maxCount: 1` | Must have exactly one EquipmentClass |
| **ISOEquipmentType** | Reference | `minCount: 1`, `maxCount: 1` | Must have exactly one EquipmentType |

#### Recommended Properties (🟡 sh:Warning)

| Property | Validation | Message |
|----------|------------|---------|
| **assetClass** | Reference to CogniteAssetClass | Tag SHOULD have asset class for better classification |
| **type** | Reference to CogniteAssetType | Tag SHOULD have type for better classification |

#### SPARQL Constraints

**System-Name Consistency:**
```sparql
SELECT $this ?systemValue ?nameValue ?expectedSystem
WHERE {
    $this dm:name ?nameValue .
    $this dm:system ?systemValue .
    BIND(SUBSTR(STR(?nameValue), 1, 2) AS ?expectedSystem)
    FILTER(?systemValue != ?expectedSystem)
}
```
🔴 **Violation:** System property must match first 2 digits of Tag name

---

### 2. Pump

**File:** `pump_shacl_rules.ttl`
**Lines:** ~180
**Inherits:** All Tag validations via `sh:node tag:TagShape`

#### Validation Summary

| Category | Count | Status |
|----------|-------|--------|
| Inherited from Tag | 12 | ✅ Active |
| Pump-Specific | 0 | 📋 Future (documented) |

#### Future Properties (Documented)

**Design Data (TBC):**
- designCapacity, designPressure, pumpType, impellerDiameter, motorPower, efficiency

**Operational Data (TBC):**
- flowRate, dischargePressure, suctionPressure, speed, vibration, temperature

---

### 3. Compressor

**File:** `compressor_shacl_rules.ttl`
**Lines:** ~200
**Inherits:** All Tag validations via `sh:node tag:TagShape`

#### Validation Summary

| Category | Count | Status |
|----------|-------|--------|
| Inherited from Tag | 12 | ✅ Active |
| Compressor-Specific | 0 | 📋 Future (documented) |

#### Future Properties (Documented)

**Design Data (TBC):**
- designCapacity, compressionRatio, stages, compressorType, motorPower

**Operational Data (TBC):**
- flowRate, inletPressure, dischargePressure, speed, surge detection

---

### 4. HeatExchanger

**File:** `heatexchanger_shacl_rules.ttl`
**Lines:** ~350
**Inherits:** All Tag validations via `sh:node tag:TagShape`

#### Validation Summary

| Category | Count | Status |
|----------|-------|--------|
| Inherited from Tag | 12 | ✅ Active |
| Design Properties | 4 | 🟡 Warning |
| Operating Conditions | 4 | 🟡 Warning |
| Shell-Side Properties | 7 | 🟡 Warning |
| Tube-Side Properties | 7 | 🟡 Warning |
| SPARQL Constraints | 1 | 🟡 Warning |

#### Design Properties (🟡 sh:Warning)

| Property | Validation | Message |
|----------|------------|---------|
| **designCapacity** | Float ≥ 0 | Should be present and positive |
| **designPressureMax** | Float ≥ 0 | Should be present and positive |
| **designTemperatureMax** | Float | Should be present |
| **designTemperatureMin** | Float | Should be present |

#### Shell-Side Properties (🟡 sh:Warning)

| Property | Validation |
|----------|------------|
| shellMinimumDesignMetalTemperature | Float |
| shellSideNormalOperatingInletPressure | Float ≥ 0 |
| shellSideNormalOperatingInletTemperature | Float |
| shellSideNormalOperatingMassFlowRate | Float ≥ 0 |
| shellSideNormalOperatingOutletPressure | Float ≥ 0 |
| shellSideNormalOperatingOutletTemperature | Float |
| shellSideUpperLimitDesignTemperature | Float |

#### Tube-Side Properties (🟡 sh:Warning)

| Property | Validation |
|----------|------------|
| tubeSideNormalOperatingInletPressure | Float ≥ 0 |
| tubeSideNormalOperatingInletTemperature | Float |
| tubeSideNormalOperatingMassFlowRate | Float ≥ 0 |
| tubeSideNormalOperatingOutletTemperature | Float |
| tubeSideNormalOperatingOutletPressure | Float ≥ 0 |
| tubeSideUpperLimitDesignTemperature | Float |
| tubeUpperLimitDesignPressure | Float ≥ 0 |

#### SPARQL Constraint

**Temperature Range Consistency:**
```sparql
SELECT $this ?max ?min
WHERE {
    $this dm:designTemperatureMax ?max ;
          dm:designTemperatureMin ?min .
    FILTER (?max <= ?min)
}
```
🟡 **Warning:** designTemperatureMax must be greater than designTemperatureMin

---

### 5. TimeSeriesExt

**File:** `timeseriesext_shacl_rules.ttl`
**Lines:** ~280
**Purpose:** Time series data quality validation

#### Required Properties (🔴 sh:Violation)

| Property | Validation | Message |
|----------|------------|---------|
| **name** | Non-empty string | TimeSeriesExt MUST have a name |
| **description** | Non-empty string | TimeSeriesExt MUST have a description |
| **unit** | Non-empty string | TimeSeriesExt MUST have a source unit |
| **assets** | Min 1 Tag | TimeSeriesExt MUST be linked to at least one Tag |

#### SPARQL Constraint (🔴 sh:Violation)

**ExternalId Contains Tag Code:**
```sparql
SELECT $this ?tsExternalId
WHERE {
    $this dm:externalId ?tsExternalId .
    FILTER NOT EXISTS {
        $this dm:assets ?tag .
        ?tag dm_tag:name ?tagCode .
        FILTER(CONTAINS(STR(?tsExternalId), STR(?tagCode)))
    }
}
```
🔴 **Violation:** TS externalId must contain tag code from linked asset

#### Data Quality Checks (🟡 sh:Warning)

| Check | CDF Function | Threshold | Message |
|-------|--------------|-----------|---------|
| **Recent Data** | `cdf_sdk:datapoints_count($this, "7d-ago", "now")` | count = 0 | No data in last 7 days |
| **Exists in CDF** | `cdf_sdk:timeseries_exists($this)` | exists = false | Time series does not exist |
| **Data Gaps** | `cdf_indsl:gaps_identification($this, 5.0)` | gapCount > 0 | Has gaps > 5x normal interval |

#### Optional Checks (🔵 sh:Info)

| Check | Function | Message |
|-------|----------|---------|
| **Extreme Outliers** | `cdf_indsl:extreme_outliers($this, 0.05)` | Contains outliers (95% confidence) |

---

### 6. EquipmentClass

**File:** `equipmentclass_shacl_rules.ttl`
**Lines:** ~100
**Purpose:** ISO 14224 equipment classification

#### Required Properties (🔴 sh:Violation)

| Property | Validation | Message |
|----------|------------|---------|
| **name** | Non-empty string | EquipmentClass name is REQUIRED |
| **code** | Non-empty string | Unique identifier per ISO 14224 is REQUIRED |
| **standard** | Non-empty string | Standard specification (e.g., "ISO14224") is REQUIRED |

#### Recommended Properties (🟡 sh:Warning)

| Property | Message |
|----------|---------|
| **description** | EquipmentClass SHOULD have a description |

---

### 7. EquipmentType

**File:** `equipmenttype_shacl_rules.ttl`
**Lines:** ~130
**Purpose:** ISO 14224 equipment type classification

#### Required Properties (🔴 sh:Violation)

| Property | Validation | Message |
|----------|------------|---------|
| **name** | Non-empty string | EquipmentType name is REQUIRED |
| **code** | Non-empty string | Unique identifier per ISO 14224 is REQUIRED |
| **equipmentClass** | Reference | Must have exactly one EquipmentClass |
| **standard** | Non-empty string | Standard specification is REQUIRED |

#### Recommended Properties (🟡 sh:Warning)

| Property | Message |
|----------|---------|
| **description** | EquipmentType SHOULD have a description |
| **standardReference** | EquipmentType SHOULD have standard reference |

---

### 8. FailureNotification

**File:** `failurenotification_shacl_rules.ttl`
**Lines:** ~400
**Purpose:** ISO 14224:2016 failure tracking and Root Cause Analysis (RCA) methodology

#### Overview

Failure Notifications document equipment failures following ISO 14224:2016 standards for reliability and maintenance data collection. This entity supports comprehensive Root Cause Analysis (RCA) and links failure events to corrective actions.

**Standards:**
- ISO 14224:2016 Section 6 - Failure Data Collection
- ISO 14224:2016 Section 6.4 - Time Data
- ISO 14224:2016 Section 6.5 - Failure Mode
- ISO 14224:2016 Annex E - Detection Method Taxonomy
- ISO 14224:2016 Annex F - Maintenance Data

#### Required Properties (🔴 sh:Violation)

| Property | Validation | Constraint | Message |
|----------|------------|------------|---------|
| **name** | Non-empty string | `minCount: 1`, `maxCount: 1`, `minLength: 1` | Unique notification ID (e.g., W00000211) |
| **description** | Non-empty string | `minCount: 1`, `maxCount: 1`, `minLength: 1` | Unique description of failure event |
| **type** | Non-empty string | `minCount: 1`, `minLength: 1` | Type code per customer convention (e.g., M2) |
| **typeDesc** | Non-empty string | `minCount: 1`, `minLength: 1` | Human-readable type description |
| **actualStartTime** | DateTime | `minCount: 1`, `maxCount: 1`, `datatype: xsd:dateTime` | Failure start time reported by end-user |
| **actualEndTime** | DateTime | `minCount: 1`, `maxCount: 1`, `datatype: xsd:dateTime` | Failure end time reported by end-user |
| **longText** | Non-empty string | `minCount: 1`, `minLength: 1` | Event details in long text (essential for RCA) |
| **failureModeCode** | Non-empty string | `minCount: 1`, `minLength: 1` | Direct relationship to failure mode code (e.g., ELP) |
| **failureModeDesc** | Non-empty string | `minCount: 1`, `minLength: 1` | Failure mode description (e.g., Plugged/Choked) |

#### Recommended Properties (🟡 sh:Warning)

| Property | Message |
|----------|---------|
| **priorityDescription** | Should describe priority level for work prioritization |
| **detectionMethodCode** | Should have detection method code (e.g., 0007) - Future RMDM |
| **detectionMethodCodeDesc** | Should have detection method description - Future RMDM |

#### Informational Properties (🔵 sh:Info)

| Property | Message |
|----------|---------|
| **userStatus** | User who issued notification (useful for RCA audit trail) |

#### SPARQL Constraints

**1. ISO 14224 Failure Mode Relationship (ISO-RCA-001)** 🔴 **Violation**

```sparql
sh:property [
    sh:path dm:failureMode ;
    sh:node fm:FailureModeShape ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
]
```
**Purpose:** FailureNotification must reference a valid FailureMode instance. Enables Root Cause Analysis and failure pattern identification.

**2. ISO 14224 Work Order Relationship (ISO-RCA-002)** 🔴 **Violation**

```sparql
sh:property [
    sh:path dm:workOrders ;
    sh:minCount 1 ;
]
```
**Purpose:** Must have direct relationship to one or more Work Orders. Links failure events to corrective maintenance activities.

**3. ISO 14224 Time Data Consistency (ISO-RCA-003)** 🔴 **Violation**

```sparql
SELECT $this ?startTime ?endTime
WHERE {
    $this dm:actualStartTime ?startTime ;
          dm:actualEndTime ?endTime .
    FILTER(?endTime <= ?startTime)
}
```
**Purpose:** actualEndTime must be after actualStartTime. Accurate time data is essential for failure duration analysis.

**4. ISO 14224 Failure Mode Code-Description Consistency (ISO-RCA-004)** 🟡 **Warning**

```sparql
SELECT $this ?code ?desc
WHERE {
    $this dm:failureModeCode ?code ;
          dm:failureModeDesc ?desc .
    FILTER(STRLEN(?desc) < 3)
}
```
**Purpose:** Verify that failureModeDesc is appropriate for failureModeCode. Improves pattern recognition.

**5. ISO 14224 Detection Method Consistency (ISO-RCA-005)** 🟡 **Warning**

```sparql
SELECT $this ?code
WHERE {
    $this dm:detectionMethodCode ?code .
    FILTER NOT EXISTS { $this dm:detectionMethodCodeDesc ?desc }
}
```
**Purpose:** When detectionMethodCode exists, detectionMethodCodeDesc should be present. Future RMDM data.

#### Validation Summary

| Category | Count | Status |
|----------|-------|--------|
| Required Properties | 9 | 🔴 Violation |
| Recommended Properties | 3 | 🟡 Warning |
| Informational Properties | 1 | 🔵 Info |
| SPARQL Constraints (Violation) | 3 | 🔴 Critical |
| SPARQL Constraints (Warning) | 2 | 🟡 Recommended |

---

## SPARQL Constraints

### Overview

```mermaid
graph LR
    subgraph "Tag Constraints"
        SC1[System-Name<br/>Consistency]
    end

    subgraph "HeatExchanger Constraints"
        SC2[Temperature<br/>Range]
    end

    subgraph "TimeSeriesExt Constraints"
        SC3[ExternalId<br/>Match]
        SC4[Recent<br/>Data]
        SC5[TS<br/>Exists]
    end

    subgraph "FailureNotification Constraints"
        SC6[Failure Mode<br/>Relationship]
        SC7[Work Order<br/>Relationship]
        SC8[Time Data<br/>Consistency]
        SC9[Failure Mode<br/>Code-Desc]
        SC10[Detection<br/>Method]
    end

    SC1 -->|🔴 Violation| TagData[Tag Instances]
    SC2 -->|🟡 Warning| HXData[HeatExchanger Instances]
    SC3 -->|🔴 Violation| TSData[TimeSeriesExt Instances]
    SC4 -->|🟡 Warning| TSData
    SC5 -->|🟡 Warning| TSData
    SC6 -->|🔴 Violation| FNData[FailureNotification Instances]
    SC7 -->|🔴 Violation| FNData
    SC8 -->|🔴 Violation| FNData
    SC9 -->|🟡 Warning| FNData
    SC10 -->|🟡 Warning| FNData

    style SC1 fill:#ff4444,color:#fff
    style SC2 fill:#ffaa00,color:#fff
    style SC3 fill:#ff4444,color:#fff
    style SC4 fill:#ffaa00,color:#fff
    style SC5 fill:#ffaa00,color:#fff
    style SC6 fill:#ff4444,color:#fff
    style SC7 fill:#ff4444,color:#fff
    style SC8 fill:#ff4444,color:#fff
    style SC9 fill:#ffaa00,color:#fff
    style SC10 fill:#ffaa00,color:#fff
```

### Detailed Constraints

#### 1. Tag System-Name Consistency (🔴 Violation)

**Purpose:** Ensure data consistency between system property and Tag name
**Implementation:** SPARQL constraint with substring extraction
**Example:** Tag "11-PA-9102" must have system="11"

```turtle
dm:TagSystemNameConsistencyShape
    sh:sparql [
        sh:severity sh:Violation ;
        sh:select """
            PREFIX dm: <http://purl.org/cognite/sp_rmdm_dm/Tag/>
            SELECT $this ?systemValue ?nameValue ?expectedSystem
            WHERE {
                $this dm:name ?nameValue .
                $this dm:system ?systemValue .
                BIND(SUBSTR(STR(?nameValue), 1, 2) AS ?expectedSystem)
                FILTER(?systemValue != ?expectedSystem)
            }
        """ ;
    ] .
```

#### 2. HeatExchanger Temperature Range (🟡 Warning)

**Purpose:** Validate design temperature range consistency
**Implementation:** SPARQL constraint with comparison
**Example:** designTemperatureMax (150°C) must be > designTemperatureMin (20°C)

```turtle
dm:HeatExchangerTemperatureRangeShape
    sh:sparql [
        sh:severity sh:Warning ;
        sh:select """
            PREFIX dm: <http://purl.org/cognite/sp_rmdm_dm/HeatExchanger/>
            SELECT $this ?max ?min
            WHERE {
                $this dm:designTemperatureMax ?max ;
                      dm:designTemperatureMin ?min .
                FILTER (?max <= ?min)
            }
        """ ;
    ] .
```

#### 3. TimeSeriesExt ExternalId Match (🔴 Violation)

**Purpose:** Ensure TS externalId contains linked Tag code
**Implementation:** SPARQL with relationship traversal and string matching
**Example:** TS "19-XY-72895.VALUE" must link to Tag "19-XY-72895"

```turtle
dm:TimeSeriesExtExternalIdMatchShape
    sh:sparql [
        sh:severity sh:Violation ;
        sh:select """
            PREFIX dm: <http://purl.org/cognite/sp_rmdm_dm/TimeSeriesExt/>
            PREFIX dm_tag: <http://purl.org/cognite/sp_rmdm_dm/Tag/>
            SELECT $this ?tsExternalId
            WHERE {
                $this dm:externalId ?tsExternalId .
                FILTER NOT EXISTS {
                    $this dm:assets ?tag .
                    ?tag dm_tag:name ?tagCode .
                    FILTER(CONTAINS(STR(?tsExternalId), STR(?tagCode)))
                }
            }
        """ ;
    ] .
```

#### 4. FailureNotification Failure Mode Relationship (🔴 Violation)

**Purpose:** Ensure FailureNotification references a valid FailureMode
**Implementation:** sh:node constraint with cardinality
**Example:** FailureNotification "W00000211" must link to FailureMode "ELP"

```turtle
dm:FailureModeRelationshipShape
    sh:property [
        sh:path dm:failureMode ;
        sh:node fm:FailureModeShape ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] .
```

#### 5. FailureNotification Work Order Relationship (🔴 Violation)

**Purpose:** Ensure FailureNotification links to corrective Work Orders
**Implementation:** sh:minCount constraint for relationship
**Example:** FailureNotification must link to at least one WorkOrder

```turtle
dm:WorkOrderRelationshipShape
    sh:property [
        sh:path dm:workOrders ;
        sh:minCount 1 ;
    ] .
```

#### 6. FailureNotification Time Data Consistency (🔴 Violation)

**Purpose:** Validate that actualEndTime occurs after actualStartTime
**Implementation:** SPARQL constraint with date comparison
**Example:** If start=2024-01-15T08:00:00Z, end must be after that time

```turtle
dm:TimeDataConsistencyShape
    sh:sparql [
        sh:severity sh:Violation ;
        sh:select """
            PREFIX dm: <http://purl.org/cognite/sp_rmdm_dm/FailureNotification/>
            SELECT $this ?startTime ?endTime
            WHERE {
                $this dm:actualStartTime ?startTime ;
                      dm:actualEndTime ?endTime .
                FILTER(?endTime <= ?startTime)
            }
        """ ;
    ] .
```

#### 7. FailureNotification Failure Mode Code-Description Consistency (🟡 Warning)

**Purpose:** Validate that failureModeDesc is not empty or too short
**Implementation:** SPARQL with string length check
**Example:** If failureModeCode="ELP", ensure failureModeDesc has meaningful text

```turtle
dm:FailureModeCodeDescConsistencyShape
    sh:sparql [
        sh:severity sh:Warning ;
        sh:select """
            PREFIX dm: <http://purl.org/cognite/sp_rmdm_dm/FailureNotification/>
            SELECT $this ?code ?desc
            WHERE {
                $this dm:failureModeCode ?code ;
                      dm:failureModeDesc ?desc .
                FILTER(STRLEN(?desc) < 3)
            }
        """ ;
    ] .
```

#### 8. FailureNotification Detection Method Consistency (🟡 Warning)

**Purpose:** When detectionMethodCode exists, ensure detectionMethodCodeDesc is present
**Implementation:** SPARQL with FILTER NOT EXISTS
**Example:** If detectionMethodCode="0007", detectionMethodCodeDesc should exist

```turtle
dm:DetectionMethodConsistencyShape
    sh:sparql [
        sh:severity sh:Warning ;
        sh:select """
            PREFIX dm: <http://purl.org/cognite/sp_rmdm_dm/FailureNotification/>
            SELECT $this ?code
            WHERE {
                $this dm:detectionMethodCode ?code .
                FILTER NOT EXISTS { $this dm:detectionMethodCodeDesc ?desc }
            }
        """ ;
    ] .
```

---

## CDF SDK Integration

### Overview

```mermaid
graph TD
    subgraph "CDF Functions"
        F1[cdf_sdk:datapoints_count]
        F2[cdf_sdk:timeseries_exists]
        F3[cdf_indsl:gaps_identification]
        F4[cdf_indsl:extreme_outliers]
    end

    subgraph "Validation Shapes"
        S1[TimeSeriesExtRecentDataShape]
        S2[TimeSeriesExtExistsShape]
        S3[TimeSeriesExtNoGapsShape]
        S4[TimeSeriesExtNoOutliersShape]
    end

    F1 -->|count = 0| S1
    F2 -->|exists = false| S2
    F3 -->|gapCount > 0| S3
    F4 -->|outlierCount > 0| S4

    S1 -->|🟡 Warning| Report[Validation Report]
    S2 -->|🟡 Warning| Report
    S3 -->|🟡 Warning| Report
    S4 -->|🔵 Info| Report

    style F1 fill:#4285f4,color:#fff
    style F2 fill:#4285f4,color:#fff
    style F3 fill:#34a853,color:#fff
    style F4 fill:#34a853,color:#fff
```

### CDF SDK Functions

| Function | Parameters | Return Type | Usage |
|----------|------------|-------------|-------|
| `cdf_sdk:datapoints_count` | `(ts_uri, start, end)` | integer | Count datapoints in time range |
| `cdf_sdk:timeseries_exists` | `(ts_uri)` | boolean | Check if TS exists in CDF |
| `cdf_sdk:datapoints_min` | `(ts_uri, start, end)` | float | Find minimum value |
| `cdf_sdk:datapoints_max` | `(ts_uri, start, end)` | float | Find maximum value |
| `cdf_sdk:datapoints_average` | `(ts_uri, start, end)` | float | Calculate average |

### INDSL Functions

| Function | Parameters | Return Type | Usage |
|----------|------------|-------------|-------|
| `cdf_indsl:gaps_identification` | `(ts_uri, threshold)` | integer | Count gaps > threshold × median |
| `cdf_indsl:extreme_outliers` | `(ts_uri, alpha)` | integer | Count outliers at confidence level |
| `cdf_indsl:value_decrease_check` | `(ts_uri, threshold)` | boolean | Detect value decreases |
| `cdf_indsl:low_density` | `(ts_uri)` | integer | Identify sparse periods |

### Time Window Placeholders

| Placeholder | Description | Example |
|-------------|-------------|---------|
| `"now"` | Current timestamp | 2026-02-17T15:30:00Z |
| `"7d-ago"` | 7 days before now | 2026-02-10T15:30:00Z |
| `"1h-ago"` | 1 hour before now | 2026-02-17T14:30:00Z |
| `"30d-ago"` | 30 days before now | 2026-01-18T15:30:00Z |

---

## Validation Flow

```mermaid
sequenceDiagram
    participant User
    participant NEAT as NEAT Framework
    participant SHACL as SHACL Engine
    participant CDF as CDF API
    participant Report as Validation Report

    User->>NEAT: validate_instances.with_shacl()
    NEAT->>NEAT: Load SHACL rules (ttl files)
    NEAT->>NEAT: Load instances from DMS

    alt Auto-Loading Enabled
        NEAT->>CDF: Load referenced entities<br/>(depth=2)
        CDF-->>NEAT: EquipmentClass, EquipmentType, etc.
    end

    NEAT->>SHACL: Execute validation

    loop For each instance
        SHACL->>SHACL: Check property constraints
        SHACL->>SHACL: Execute SPARQL constraints

        alt TimeSeriesExt validation
            SHACL->>CDF: cdf_sdk:datapoints_count()
            CDF-->>SHACL: datapoint count
            SHACL->>CDF: cdf_sdk:timeseries_exists()
            CDF-->>SHACL: exists boolean

            opt INDSL enabled
                SHACL->>CDF: cdf_indsl:gaps_identification()
                CDF-->>SHACL: gap count
            end
        end

        SHACL->>SHACL: Collect violations/warnings
    end

    SHACL-->>NEAT: Validation results
    NEAT->>Report: Generate report (RDF graph)
    NEAT->>Report: Generate text report
    Report-->>User: Validation summary

    alt Store results
        User->>CDF: Store to Records API
        CDF-->>User: Confirmation
    end
```

### Execution Steps

1. **Load Rules**: SHACL engine loads all .ttl files from `shacl_rules/` directory
2. **Load Instances**: Fetch instances from CDF DMS based on view configuration
3. **Auto-Load References**: Load related entities (EquipmentClass, EquipmentType) up to specified depth
4. **Execute Validation**:
   - Property-level constraints (minCount, datatype, pattern, etc.)
   - SPARQL constraints (complex business logic)
   - CDF SDK functions (time series data quality)
5. **Generate Report**: Create RDF graph with violations and text summary
6. **Store Results**: Optionally store validation results in CDF Records API

---

## Standards Compliance

### ENS (Equipment Numbering System)

```mermaid
graph LR
    subgraph "Tag Naming: XX-YYY-ZZZZ"
        A[System Number<br/>2 digits] -->|"-"| B[Equipment Class<br/>1-10 chars]
        B -->|"-"| C[Sequence Number<br/>4 digits]
    end

    subgraph "Validation"
        V1[Pattern Match<br/>Regex]
        V2[System Consistency<br/>SPARQL]
    end

    A -.->|validates| V1
    A -.->|validates| V2
    B -.->|validates| V1
    C -.->|validates| V1

    style A fill:#4285f4,color:#fff
    style B fill:#34a853,color:#fff
    style C fill:#fbbc04,color:#000
```

**Tag Name Format:**
- **System Number**: 2 digits (00-99) identifying process area/system
- **Equipment Class**: Alphanumeric code (1-10 characters) for equipment type
- **Sequence Number**: 4 digits (0000-9999) for unique identification

**Example:** `11-PA-9102`
- System: `11` (Process system 11)
- Equipment Class: `PA` (Pump, centrifugal)
- Sequence: `9102` (Unique within system/class)

**Validated By:**
- Pattern constraint: `^[0-9]{2}-[A-Z0-9]{1,10}-[0-9]{4}$`
- SPARQL constraint: System property matches first 2 digits

### Norsok Z-DP-002

**Compliance Areas:**
- Tag naming conventions
- Equipment classification codes
- Function code definitions
- System numbering

**Validation Rules:**
- Tag name format (sh:pattern)
- System property (required, 2 digits)
- Function code (required)
- Tag discipline (required)

### ISO 14224

```mermaid
graph TD
    subgraph "ISO 14224 Taxonomy"
        L1[Equipment Class<br/>High-level category]
        L2[Equipment Type<br/>Specific type]
        L3[Tag<br/>Individual instance]
    end

    L1 -->|classifies| L2
    L2 -->|instantiates| L3

    L1 -.->|code + standard| V1[EquipmentClassShape]
    L2 -.->|code + standard| V2[EquipmentTypeShape]
    L3 -.->|ISOEquipmentClass<br/>ISOEquipmentType| V3[TagShape]

    style L1 fill:#4caf50,color:#fff
    style L2 fill:#8bc34a,color:#fff
    style L3 fill:#cddc39,color:#000
```

**Hierarchy Levels:**
1. **Equipment Class** - Broad classification (e.g., "Rotating Equipment")
2. **Equipment Type** - Specific type (e.g., "Centrifugal Pump")
3. **Tag Instance** - Individual equipment (e.g., "11-PA-9102")

**Validated By:**
- EquipmentClass: code + standard required
- EquipmentType: code + standard + equipmentClass reference required
- Tag: ISOEquipmentClass + ISOEquipmentType relations required

---

## Property Validation Matrix

### Tag Properties

| Property | Required | Format | Severity | Constraint Type | Standards |
|----------|----------|--------|----------|-----------------|-----------|
| name | ✅ | XX-YYY-ZZZZ | 🔴 | pattern + minCount | ENS, Norsok |
| description | ✅ | string | 🔴 | minCount + minLength | All |
| system | ✅ | 2 digits | 🔴 | pattern + SPARQL | ENS, Norsok |
| functionCode | ✅ | string | 🔴 | minCount + minLength | Norsok |
| functionCodeDesc | ✅ | string | 🔴 | minCount + minLength | Norsok |
| dateInstalled | ✅ | string | 🔴 | minCount | All |
| tagDiscipline | ✅ | string | 🔴 | minCount + minLength | Norsok |
| ISOEquipmentClass | ✅ | reference | 🔴 | sh:node + cardinality | ISO 14224 |
| ISOEquipmentType | ✅ | reference | 🔴 | sh:node + cardinality | ISO 14224 |
| assetClass | ❌ | reference | 🟡 | sh:node | CDF CDM |
| type | ❌ | reference | 🟡 | sh:node | CDF CDM |

### HeatExchanger Properties

| Property | Required | Data Type | Severity | Constraint |
|----------|----------|-----------|----------|------------|
| *All Tag properties* | Inherited | - | 🔴/🟡 | From TagShape |
| designCapacity | ❌ | Float32 ≥ 0 | 🟡 | minInclusive |
| designPressureMax | ❌ | Float32 ≥ 0 | 🟡 | minInclusive |
| designTemperatureMax | ❌ | Float32 | 🟡 | datatype + SPARQL |
| designTemperatureMin | ❌ | Float32 | 🟡 | datatype + SPARQL |
| normalOperatingInletPressure | ❌ | Float32 ≥ 0 | 🟡 | minInclusive |
| shellSideNormalOperatingInletPressure | ❌ | Float32 ≥ 0 | 🟡 | minInclusive |
| tubeSideNormalOperatingInletPressure | ❌ | Float32 ≥ 0 | 🟡 | minInclusive |
| *...and 15+ more design properties* | ❌ | Float32 | 🟡 | Various |

### TimeSeriesExt Properties

| Property | Required | Format | Severity | Validation | CDF Function |
|----------|----------|--------|----------|------------|--------------|
| name | ✅ | string | 🔴 | minCount + minLength | - |
| description | ✅ | string | 🔴 | minCount + minLength | - |
| unit | ✅ | string | 🔴 | minCount + minLength | - |
| assets | ✅ | reference[] | 🔴 | minCount: 1 | - |
| externalId | ✅ | contains tag code | 🔴 | SPARQL + CONTAINS | - |
| *recent data* | ❌ | datapoints | 🟡 | count > 0 | cdf_sdk:datapoints_count |
| *exists in CDF* | ❌ | boolean | 🟡 | exists = true | cdf_sdk:timeseries_exists |
| *no gaps* | ❌ | gap count | 🟡 | gaps = 0 | cdf_indsl:gaps_identification |
| *no outliers* | ❌ | outlier count | 🔵 | informational | cdf_indsl:extreme_outliers |

### FailureNotification Properties

| Property | Required | Data Type | Severity | Constraint Type | Standards |
|----------|----------|-----------|----------|-----------------|-----------|
| name | ✅ | string | 🔴 | minCount + maxCount + minLength | ISO 14224 Section 6 |
| description | ✅ | string | 🔴 | minCount + maxCount + minLength | ISO 14224 Section 6.3 |
| type | ✅ | string | 🔴 | minCount + minLength | ISO 14224 Section 6.2 |
| typeDesc | ✅ | string | 🔴 | minCount + minLength | ISO 14224 Section 6.2 |
| actualStartTime | ✅ | xsd:dateTime | 🔴 | minCount + maxCount + datatype + SPARQL | ISO 14224 Section 6.4 |
| actualEndTime | ✅ | xsd:dateTime | 🔴 | minCount + maxCount + datatype + SPARQL | ISO 14224 Section 6.4 |
| longText | ✅ | string | 🔴 | minCount + minLength | ISO 14224 Section 6.3 (RCA) |
| failureModeCode | ✅ | string | 🔴 | minCount + minLength + SPARQL | ISO 14224 Section 6.5 |
| failureModeDesc | ✅ | string | 🔴 | minCount + minLength + SPARQL | ISO 14224 Section 6.5 |
| failureMode | ✅ | reference | 🔴 | sh:node + minCount + maxCount | ISO 14224 Section 6.5 |
| workOrders | ✅ | reference[] | 🔴 | minCount | ISO 14224 Annex F |
| priorityDescription | ❌ | string | 🟡 | minLength | ISO 14224 Annex F |
| detectionMethodCode | ❌ | string | 🟡 | minLength + SPARQL | ISO 14224 Annex E |
| detectionMethodCodeDesc | ❌ | string | 🟡 | minLength + SPARQL | ISO 14224 Annex E |
| userStatus | ❌ | string | 🔵 | minLength | RCA Audit Trail |

---

## Future Enhancements

### Phase 1: Equipment-Specific Properties

**Pump Design Data:**
- [ ] designCapacity, designPressure, pumpType
- [ ] Pressure consistency SPARQL (discharge > suction)
- [ ] Operating range validation

**Compressor Design Data:**
- [ ] compressionRatio (must be > 1.0), stages, compressorType
- [ ] Pressure/temperature consistency SPARQL
- [ ] Surge detection validation

**Status:** Properties not yet in data model - uncomment when added

### Phase 2: Unit of Measure (UOM) Validation

**Objective:** Validate that numeric properties have corresponding UOM fields

**Example SPARQL:**
```sparql
# If designCapacity exists, designCapacity_UOM should exist
SELECT $this WHERE {
    $this dm:designCapacity ?capacity .
    FILTER NOT EXISTS { $this dm:designCapacity_UOM ?uom }
}
```

**Controlled Vocabulary:**
- Pressure: "bar", "psi", "Pa", "MPa"
- Temperature: "°C", "°F", "K"
- Flow: "m³/h", "gpm", "kg/h", "L/s"

### Phase 3: Advanced Time Series Validation

**Value Range Validation:**
```sparql
# Latest value within expected operational range
BIND(cdf_sdk:datapoints_latest($this) AS ?latest)
FILTER(?latest < min_threshold || ?latest > max_threshold)
```

**Trend Analysis:**
- Rolling averages validation
- Rate of change detection
- Anomaly detection with ML models

### Phase 4: Cross-Entity Validation

**Tag Hierarchy Consistency:**
- Validate parent-child relationships
- Check path consistency
- Verify root assignments

**Time Series to Tag Consistency:**
- TS unit matches Tag sensor unit
- TS operating range within Tag design range
- TS alarms linked to Tag trip conditions

### Phase 5: Future RMDM Properties

**Tag Operation Data:**
- normalOperatingMode (ISO 14224 controlled vocabulary)
- commissioningDate, startDateCurrentService
- surveillanceTimeCalculated

**Tag Location Data:**
- installationCode/Name (e.g., "VAL", "Valhall")
- plantCode/Name (e.g., "VPH")
- facility, area (ENS coding convention)

**Equipment Metadata:**
- manufacturerName (required for all equipment)
- serialNumber (unique identification)
- subSystem (ISO 14224 naming)
- fromTag/toTag (process flow relationships)

---

## Appendix A: Namespace Reference

| Prefix | Namespace URI | Purpose |
|--------|---------------|---------|
| `sh` | `http://www.w3.org/ns/shacl#` | SHACL vocabulary |
| `xsd` | `http://www.w3.org/2001/XMLSchema#` | XML Schema datatypes |
| `rdf` | `http://www.w3.org/1999/02/22-rdf-syntax-ns#` | RDF vocabulary |
| `rdfs` | `http://www.w3.org/2000/01/rdf-schema#` | RDF Schema vocabulary |
| `dm` | `http://purl.org/cognite/sp_rmdm_dm/{Entity}/` | RMDM data model entities |
| `cdf_sdk` | `https://cognite.com/cdf/sdk/` | CDF SDK functions |
| `cdf_indsl` | `https://cognite.com/cdf/indsl/` | INDSL data quality functions |

## Appendix B: Quick Reference Commands

### Validate Instances
```python
from cognite.neat import NeatSession

neat = NeatSession(client)

# Validate with auto-loading
conforms, report_graph, report_text = neat.validate_instances.with_shacl(
    instances=tag_instances,
    shacl_rules=tag_shacl_rules,
    datamodel_space="sp_rmdm_dm",
    datamodel_external_id="RMDM",
    datamodel_version="v8.19",
    auto_load_depth=2,
    verbose=True
)

print(report_text)
```

### Deploy SHACL Rules
```bash
python scripts/deploy_infrastructure.py --env abp-dev --views tag
```

---

**Document Version:** 1.0
**Generated:** 2026-02-17
**Maintained By:** Data Quality Team
**Review Frequency:** Quarterly or with data model updates
