Skip to content

CIM RDF Schema Domain Models

Purpose and Overview

The GridLab.Gmss.Cim.Schemas namespace provides a strongly-typed .NET domain model that represents CIM RDF Schema files as defined by IEC 61970-552:2016. These schemas define the structure of CIM (Common Information Model) profiles used for power system model exchange.

A CIMXML model exchange document uses a subset of the CIM to address the model exchange needs of a specific use case; see Part 400 series profile documents. A CIM profile defines that portion of the CIM that an importer and exporter of a CIMXML document should be expected to handle. The RDF Schema for a profile then contains only the classes and properties defined for that profile.

A RDF Schema file can be generated from the CIM UML model by an application having a user interface where the subset of the CIM UML model is interactively specified. The RDF Schema file can be used by an application to validate a CIMXML document.

Figure

Key Objectives

This namespace serves multiple critical purposes in the GridLab GMSS RDF framework:

  1. Type-Safe Schema Object Model: Provides C# classes that represent RDF Schema elements (classes, properties, enumerations), enabling compile-time type checking and IntelliSense support for developers working with CIM schemas.

  2. Schema Parsing and Validation: Facilitates parsing of CIM RDF Schema XML files into in-memory object graphs for schema introspection and CIMXML document validation.

  3. Multi-Version Support: Manages multiple schema profiles across different CGMES versions (2.4.15, 3.0.0), enabling cross-version compatibility and migration support.

  4. Code Generation Support: Provides metadata extraction capabilities for generating C# classes, properties, and enumerations from CIM schema definitions.

  5. Profile Management: Implements repository patterns for storing, querying, and organizing schema profiles by version and profile type.

Class Diagram

classDiagram
    class IRdfXmlSchema {
        <<interface>>
        +string Version
        +string Encoding
        +string BaseUri
        +RdfSchemaProfile Profile
        +IReadOnlyDictionary~string,string~ Namespaces
    }

    class RdfXmlSchemaRoot {
        <<abstract>>
        +string Version
        +string Encoding
        +string BaseUri
        +RdfSchemaProfile Profile
        +IReadOnlyDictionary~string,string~ Namespaces

        +int Count
        +bool HasContent
        +SetProfile(RdfSchemaProfile)
        +AddNamespace(string prefix, string uri)
        +FindNamespace(string prefix)
    }

    class RdfSchemaProfile {
        +IRdfSchemaVersion Version
        +IReadOnlyList~RdfSchemaPackage~ Packages
        +IReadOnlyList~RdfSchemaClass~ Classes
        +AddPackage(RdfSchemaPackage) RdfSchemaProfile
        +AddPackages(IEnumerable~RdfSchemaPackage~) RdfSchemaProfile
        +AddClass(RdfSchemaClass) RdfSchemaProfile
        +AddClasses(IEnumerable~RdfSchemaClass~) RdfSchemaProfile
        +GetClassByUri(string) RdfSchemaClass?
        +GetClassByName(string) RdfSchemaClass?
        +GetClassesByPackage(string) IEnumerable~RdfSchemaClass~
        +GetEnumerations() IEnumerable~RdfSchemaClass~
        +GetPrimitives() IEnumerable~RdfSchemaClass~
        +GetCimDatatypes() IEnumerable~RdfSchemaClass~
        +GetConcreteClasses() IEnumerable~RdfSchemaClass~
        +GetAbstractClasses() IEnumerable~RdfSchemaClass~
        +GetSubclasses(string) IEnumerable~RdfSchemaClass~
        +GetInheritanceChain(string) IEnumerable~RdfSchemaClass~
        +GetAllPropertiesForClass(string) IEnumerable~RdfSchemaProperty~
    }

    class RdfSchemaClass {
        +string Uri
        +string Name
        +string Label
        +string? Comment
        +string? SuperClassUri
        +string? PackageUri
        +RdfSchemaStereotype Stereotype
        +IReadOnlyList~string~ AdditionalStereotypes
        +IReadOnlyList~RdfSchemaProperty~ Properties
        +IReadOnlyList~RdfSchemaEnumValue~ EnumValues
        +bool IsTopLevel
        +bool IsEnumeration
        +bool IsPrimitive
        +bool IsCimDatatype
        +bool IsConcrete
        +bool IsAbstract
        +WithComment(string) RdfSchemaClass
        +WithSuperClass(string) RdfSchemaClass
        +WithPackage(string) RdfSchemaClass
        +WithStereotype(RdfSchemaStereotype) RdfSchemaClass
        +AddStereotype(string) RdfSchemaClass
        +AddProperty(RdfSchemaProperty) RdfSchemaClass
        +AddProperties(IEnumerable~RdfSchemaProperty~) RdfSchemaClass
        +AddEnumValue(RdfSchemaEnumValue) RdfSchemaClass
        +AddEnumValues(IEnumerable~RdfSchemaEnumValue~) RdfSchemaClass
        +GetAttributes()
        +GetAssociations()
    }

    class RdfSchemaProperty {
        +string Uri
        +string Name
        +string Label
        +string? Comment
        +string DomainUri
        +string? RangeUri
        +string? DataTypeUri
        +string? InverseRoleNameUri
        +string? FixedValue
        +RdfSchemaMultiplicity Multiplicity
        +RdfSchemaStereotype Stereotype
        +bool IsAssociationUsed
        +bool IsAttribute
        +bool IsAssociation
        +bool IsReadOnly
        +bool IsNullable
        +WithComment(string) RdfSchemaProperty
        +WithDataType(string) RdfSchemaProperty
        +WithRange(string) RdfSchemaProperty
        +WithMultiplicity(RdfSchemaMultiplicity) RdfSchemaProperty
        +WithStereotype(RdfSchemaStereotype) RdfSchemaProperty
        +WithFixedValue(string) RdfSchemaProperty
        +WithInverseRoleName(string) RdfSchemaProperty
        +WithAssociationUsed(bool) RdfSchemaProperty
    }

    class RdfSchemaEnumValue {
        +string Uri
        +string Name
        +string Label
        +string? Comment
        +string EnumerationUri
        +WithComment(string) RdfSchemaEnumValue
    }

    class RdfSchemaPackage {
        +string Uri
        +string Label
        +string? Comment
        +WithComment(string) RdfSchemaPackage
    }

    class RdfSchemaVersion {
        <<abstract>>
        +string? Version
        +string? BaseUml
        +string? ShortName
        +string? Description
        +string? SourceName
        +string ProfileIdentifier*
        +WithVersion(string?) RdfSchemaVersion
        +WithBaseUml(string?) RdfSchemaVersion
        +WithShortName(string?) RdfSchemaVersion
        +WithDescription(string?) RdfSchemaVersion
        +WithSourceName(string?) RdfSchemaVersion
    }

    class RdfSchemaStereotype {
        <<enumeration>>
        None
        Concrete
        Enumeration
        Primitive
        CimDatatype
        Attribute
        Compound
        EnumValue
        European
        Entsoe
        ClassCategory
    }

    class RdfSchemaMultiplicity {
        <<enumeration>>
        Unspecified
        ExactlyOne
        ZeroOrOne
        OneOrMore
        ZeroOrMore
        TwoOrMore
    }

    IRdfXmlSchema <|.. RdfXmlSchemaRoot
    IRdfXmlSchema --> RdfSchemaProfile : Profile
    RdfSchemaProfile --> RdfSchemaVersion
    RdfSchemaProfile --> RdfSchemaClass : Classes
    RdfSchemaProfile --> RdfSchemaPackage : Packages
    RdfSchemaClass --> RdfSchemaProperty : Properties
    RdfSchemaClass --> RdfSchemaEnumValue : EnumValues
    RdfSchemaClass --> RdfSchemaStereotype
    RdfSchemaProperty --> RdfSchemaMultiplicity
    RdfSchemaProperty --> RdfSchemaStereotype

Usage Examples

Working with Schema Classes

// Create a new schema class
var acLineSegment = new RdfSchemaClass(
    uri: "#ACLineSegment",
    name: "ACLineSegment",
    label: "AC Line Segment")
    .WithComment("A wire or combination of wires, with consistent electrical characteristics...")
    .WithSuperClass("#Conductor")
    .WithPackage("#Package_WiresProfile")
    .WithStereotype(RdfSchemaStereotype.Concrete);

// Add properties to the class
var resistanceProperty = new RdfSchemaProperty(
    uri: "#ACLineSegment.r",
    name: "r",
    label: "Resistance",
    domainUri: "#ACLineSegment")
    .WithComment("Positive sequence series resistance of the entire line section")
    .WithDataType("#Resistance")
    .WithMultiplicity(RdfSchemaMultiplicity.ExactlyOne)
    .WithStereotype(RdfSchemaStereotype.Attribute);

acLineSegment.AddProperty(resistanceProperty);

// Check class characteristics
Console.WriteLine($"Is Concrete: {acLineSegment.IsConcrete}");
Console.WriteLine($"Is Top Level: {acLineSegment.IsTopLevel}");
Console.WriteLine($"Properties Count: {acLineSegment.Properties.Count}");

Working with Schema Properties

// Create an attribute property (has dataType)
var voltageProperty = new RdfSchemaProperty(
    uri: "#Terminal.voltage",
    name: "voltage",
    label: "Voltage",
    domainUri: "#Terminal")
    .WithDataType("#Voltage")
    .WithMultiplicity(RdfSchemaMultiplicity.ZeroOrOne)
    .WithStereotype(RdfSchemaStereotype.Attribute);

Console.WriteLine($"Is Attribute: {voltageProperty.IsAttribute}");
Console.WriteLine($"Is Nullable: {voltageProperty.IsNullable}");

// Create an association property (has range to another class)
var terminalEquipmentProperty = new RdfSchemaProperty(
    uri: "#Terminal.ConductingEquipment",
    name: "ConductingEquipment",
    label: "Conducting Equipment",
    domainUri: "#Terminal")
    .WithRange("#ConductingEquipment")
    .WithMultiplicity(RdfSchemaMultiplicity.ExactlyOne)
    .WithInverseRoleName("#ConductingEquipment.Terminals")
    .WithAssociationUsed(true);

Console.WriteLine($"Is Association: {terminalEquipmentProperty.IsAssociation}");
Console.WriteLine($"Has Inverse: {!string.IsNullOrEmpty(terminalEquipmentProperty.InverseRoleNameUri)}");

// Create a fixed-value property (read-only)
var fixedMultiplierProperty = new RdfSchemaProperty(
    uri: "#ActivePower.multiplier",
    name: "multiplier",
    label: "Multiplier",
    domainUri: "#ActivePower")
    .WithDataType("#UnitMultiplier")
    .WithFixedValue("M")
    .WithMultiplicity(RdfSchemaMultiplicity.ExactlyOne);

Console.WriteLine($"Is Read-Only: {fixedMultiplierProperty.IsReadOnly}");
Console.WriteLine($"Fixed Value: {fixedMultiplierProperty.FixedValue}");

Working with Enumerations

// Create an enumeration class
var phaseCode = new RdfSchemaClass(
    uri: "#PhaseCode",
    name: "PhaseCode",
    label: "Phase Code")
    .WithComment("Enumeration of phase identifiers")
    .WithStereotype(RdfSchemaStereotype.Enumeration);

// Add enumeration values
var enumValues = new[]
{
    new RdfSchemaEnumValue("#PhaseCode.A", "A", "Phase A", "#PhaseCode")
        .WithComment("Phase A"),
    new RdfSchemaEnumValue("#PhaseCode.B", "B", "Phase B", "#PhaseCode")
        .WithComment("Phase B"),
    new RdfSchemaEnumValue("#PhaseCode.C", "C", "Phase C", "#PhaseCode")
        .WithComment("Phase C"),
    new RdfSchemaEnumValue("#PhaseCode.ABC", "ABC", "Phases ABC", "#PhaseCode")
        .WithComment("Three-phase")
};

foreach (var enumValue in enumValues)
{
    phaseCode.AddEnumValue(enumValue);
}

Console.WriteLine($"Is Enumeration: {phaseCode.IsEnumeration}");
Console.WriteLine($"Enum Values: {phaseCode.EnumValues.Count}");

// Access enum values
foreach (var value in phaseCode.EnumValues)
{
    Console.WriteLine($"  {value.Name}: {value.Label}");
}

Querying Schema Profiles

// Assume we have a parsed profile
RdfSchemaProfile profile = /* ... */;

// Get all concrete classes
var concreteClasses = profile.GetConcreteClasses();
Console.WriteLine($"Concrete classes: {concreteClasses.Count()}");

// Get all enumerations
var enums = profile.GetEnumerations();
foreach (var enumClass in enums)
{
    Console.WriteLine($"Enum: {enumClass.Name}");
    foreach (var value in enumClass.EnumValues)
    {
        Console.WriteLine($"  - {value.Name}");
    }
}

// Get classes by package
var equipmentClasses = profile.GetClassesByPackage("#Package_CoreEquipmentProfile");
Console.WriteLine($"Equipment classes: {equipmentClasses.Count()}");

// Find a specific class
var terminal = profile.GetClassByName("Terminal");
if (terminal != null)
{
    Console.WriteLine($"Found: {terminal.Label}");
    Console.WriteLine($"  Super Class: {terminal.SuperClassUri}");
    Console.WriteLine($"  Properties: {terminal.Properties.Count}");
}

// Get all datatypes
var datatypes = profile.GetCimDatatypes();
foreach (var datatype in datatypes)
{
    Console.WriteLine($"Datatype: {datatype.Name}");
}

// Get all abstract classes
var abstractClasses = profile.GetAbstractClasses();
Console.WriteLine($"Abstract classes: {abstractClasses.Count()}");

Inheritance and Property Resolution

// Get the complete inheritance chain for a class
var inheritanceChain = profile.GetInheritanceChain("#ACLineSegment");
Console.WriteLine("Inheritance chain:");
foreach (var ancestor in inheritanceChain)
{
    Console.WriteLine($"  {ancestor.Name}");
}

// Get all properties including inherited ones
var allProperties = profile.GetAllPropertiesForClass("#ACLineSegment");
Console.WriteLine($"\nAll properties (including inherited): {allProperties.Count()}");
foreach (var property in allProperties)
{
    var typeInfo = property.IsAttribute 
        ? $"Attribute: {property.DataTypeUri}" 
        : $"Association: {property.RangeUri}";
    Console.WriteLine($"  {property.Name} ({typeInfo})");
}

// Get direct subclasses
var subclasses = profile.GetSubclasses("#Conductor");
Console.WriteLine($"\nDirect subclasses of Conductor:");
foreach (var subclass in subclasses)
{
    Console.WriteLine($"  {subclass.Name}");
}

// Check if a class is at the top of its hierarchy
var identifiedObject = profile.GetClassByName("IdentifiedObject");
if (identifiedObject != null)
{
    Console.WriteLine($"\nIs top-level class: {identifiedObject.IsTopLevel}");
}

Working with Schema Packages

// Create packages
var corePackage = new RdfSchemaPackage(
    uri: "#Package_Core",
    label: "Core Package")
    .WithComment("Core package contains fundamental types");

var equipmentPackage = new RdfSchemaPackage(
    uri: "#Package_Equipment",
    label: "Equipment Package")
    .WithComment("Equipment package contains electrical equipment models");

// Build a profile
var version = new Cgmes3SchemaVersion()
    .WithVersion("3.0.0")
    .WithShortName("EQ")
    .WithDescription("Equipment Profile");

var profile = new RdfSchemaProfile(version)
    .AddPackage(corePackage)
    .AddPackage(equipmentPackage);

// Add classes to the profile
profile.AddClass(identifiedObject)
    .AddClass(acLineSegment)
    .AddClass(terminal);

Console.WriteLine($"Profile: {profile.Version.ShortName}");
Console.WriteLine($"Classes: {profile.Classes.Count}");
Console.WriteLine($"Packages: {profile.Packages.Count}");

Supported Schema Versions

The domain models support both CGMES 2.4.15 and CGMES 3.0.0 schema formats:

  • CGMES 2.4.15: Uses http://iec.ch/TC57/2013/CIM-schema-cim16# namespace
  • CGMES 3.0.0: Uses http://iec.ch/TC57/CIM100# namespace

Key RDF/RDFS Elements Parsed

RDF Element Domain Model Description
rdfs:Class RdfSchemaClass Represents a class definition
rdf:Property RdfSchemaProperty Represents a property (attribute or association)
rdfs:subClassOf RdfSchemaClass.SuperClassUri Defines class inheritance
rdfs:label Label property Human-readable label
rdfs:comment Comment property Optional description text
rdfs:domain RdfSchemaProperty.DomainUri Class that defines the property
rdfs:range RdfSchemaProperty.RangeUri Target class for associations
cims:dataType RdfSchemaProperty.DataTypeUri Data type for attributes
cims:multiplicity RdfSchemaProperty.Multiplicity Cardinality constraint (0..1, 1..1, 0.., 1..)
cims:stereotype Stereotype property Classifies classes and properties
cims:belongsToCategory RdfSchemaClass.PackageUri Package/category assignment
cims:isFixed RdfSchemaProperty.FixedValue Constant/fixed value
cims:inverseRoleName RdfSchemaProperty.InverseRoleNameUri Bidirectional association name
cims:AssociationUsed RdfSchemaProperty.IsAssociationUsed Indicates which end to serialize

Design Patterns

Value Objects

All schema domain classes inherit from ValueObject (ABP Framework), ensuring:

  • Immutability: Properties are protected and can only be set through fluent methods
  • Value Equality: Two instances are equal if their properties are equal
  • Type Safety: Strong typing prevents accidental misuse
var class1 = new RdfSchemaClass("#Terminal", "Terminal", "Terminal");
var class2 = new RdfSchemaClass("#Terminal", "Terminal", "Terminal");

// Value equality - these are considered equal
Assert.True(class1.Equals(class2));

Fluent API Pattern

All domain classes use the With* prefix for fluent, chainable configuration:

var property = new RdfSchemaProperty("#Terminal.voltage", "voltage", "Voltage", "#Terminal")
    .WithDataType("#Voltage")
    .WithMultiplicity(RdfSchemaMultiplicity.ZeroOrOne)
    .WithStereotype(RdfSchemaStereotype.Attribute)
    .WithComment("The terminal voltage");

var schemaClass = new RdfSchemaClass("#ACLineSegment", "ACLineSegment", "AC Line Segment")
    .WithSuperClass("#Conductor")
    .WithPackage("#Package_Wires")
    .WithStereotype(RdfSchemaStereotype.Concrete)
    .AddProperty(property);

Computed Properties

Classes expose computed boolean properties for type checking:

// RdfSchemaClass
bool isEnum = schemaClass.IsEnumeration;      // Stereotype == Enumeration
bool isPrimitive = schemaClass.IsPrimitive;   // Stereotype == Primitive
bool isConcrete = schemaClass.IsConcrete;     // Stereotype == Concrete || contains "concrete"
bool isAbstract = schemaClass.IsAbstract;     // Not concrete, enum, primitive, or datatype
bool isTopLevel = schemaClass.IsTopLevel;     // No super class defined

// RdfSchemaProperty
bool isAttribute = property.IsAttribute;      // Has DataTypeUri
bool isAssociation = property.IsAssociation;  // Has RangeUri
bool isReadOnly = property.IsReadOnly;        // Has FixedValue
bool isNullable = property.IsNullable;        // Multiplicity allows zero

Repository Pattern

The profile acts as a repository for schema elements with query methods:

// Query by type
var enums = profile.GetEnumerations();
var concreteClasses = profile.GetConcreteClasses();
var primitives = profile.GetPrimitives();

// Query by identifier
var terminal = profile.GetClassByUri("#Terminal");
var terminalByName = profile.GetClassByName("Terminal");

// Query by relationship
var subclasses = profile.GetSubclasses("#IdentifiedObject");
var classesInPackage = profile.GetClassesByPackage("#Package_Core");

// Inheritance queries
var chain = profile.GetInheritanceChain("#ACLineSegment");
var allProps = profile.GetAllPropertiesForClass("#ACLineSegment");

Type Safety with Enumerations

RdfSchemaStereotype and RdfSchemaMultiplicity enums provide type-safe classification:

// Stereotype classification
switch (schemaClass.Stereotype)
{
    case RdfSchemaStereotype.Concrete:
        // Can be instantiated
        break;
    case RdfSchemaStereotype.Enumeration:
        // Has enum values
        break;
    case RdfSchemaStereotype.Primitive:
        // Basic data type
        break;
    case RdfSchemaStereotype.CimDatatype:
        // CIM-specific type with unit
        break;
}

// Multiplicity-based logic
var isRequired = property.Multiplicity == RdfSchemaMultiplicity.ExactlyOne ||
                 property.Multiplicity == RdfSchemaMultiplicity.OneOrMore;

var isCollection = property.Multiplicity == RdfSchemaMultiplicity.ZeroOrMore ||
                   property.Multiplicity == RdfSchemaMultiplicity.OneOrMore;

See Also