Skip to main content

Profiles & Datatypes

CIM profile ontologies (RDFS schemas) describe which classes and properties exist in a model and what datatypes their values carry. CIMXML uses a profile registry to hold those ontologies so the parser can resolve a literal such as a Voltage to the right RDF datatype instead of leaving it as a plain string. This page covers the profile model, the registry API, and how datatype resolution is configured.

CimProfile

A CimProfile (in de.soptim.opencgmes.cimxml.graph) wraps a profile ontology graph and exposes its metadata in a version-independent way. Profiles are versioned and identified by their version IRIs, which differ between CIM generations:

CIM versionVersion IRIsKeywordVersion info
CIM 16 (CGMES 2.4.15)cims:isFixed base/entsoe URIs{Profile}Version.shortName(none)
CIM 17/18 (CGMES 3.0+)owl:versionIRIdcat:keywordowl:versionInfo

Wrap a profile graph and query its metadata:

import de.soptim.opencgmes.cimxml.graph.CimProfile;

// Wrap a loaded profile graph
CimProfile profile = CimProfile.wrap(profileGraph);

String cimNamespace = profile.getCimNamespace();
Set<Node> versionIris = profile.getOwlVersionIris();
String keyword = profile.getDcatKeyword();
String versionInfo = profile.getOwlVersionInfo();
boolean isHeaderProfile = profile.isHeaderProfile();

A model profile describes the structure and properties of model data (Equipment, Topology, state variables, and so on); a header profile describes the structure of a model header (FullModel/DifferenceModel metadata). Header profiles are not referenced by the model itself, so the registry selects them by CIM namespace rather than by version IRI.

CimProfileRegistry

CimProfileRegistry (in de.soptim.opencgmes.cimxml.rdfs) is the interface for managing profiles; CimProfileRegistryStd is the standard, caching implementation. Each CimXmlParser owns its own registry, accessible via getCimProfileRegistry().

import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistry;
import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistryStd;

// Create and configure a registry directly
CimProfileRegistry registry = new CimProfileRegistryStd();

// Register a profile
CimProfile profile = CimProfile.wrap(profileGraph);
registry.register(profile);

// Query registered profiles
Set<CimProfile> profiles = registry.getRegisteredProfiles();

When registering, the registry extracts the datatypes of every property in the profile and stores them in a map for fast lookup. Registration throws IllegalArgumentException if a profile's owlVersionIRI is already registered, or — for a header profile — if one is already registered for the same CIM version.

Registering profiles through the parser

The most common path is to let the parser register profiles for you, so subsequent parses are profile-aware:

CimXmlParser parser = new CimXmlParser();
parser.parseAndRegisterCimProfile(Path.of("Equipment.rdf"));
parser.parseAndRegisterCimProfile(Path.of("Topology.rdf"));

CimDatasetGraph dataset = parser.parseCimModel(Path.of("model.xml"));

Datatype resolution

Once profiles are registered, the parser resolves property datatypes from them. The registry returns a PropertyInfo record per property, capturing its domain class (rdfType), the property IRI, the CIM datatype, and either a primitiveType (an RDFDatatype, for datatype properties) or a referenceType (the range class, for object properties) — exactly one of the latter two is non-null.

import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistry.PropertyInfo;

// Look up properties + datatypes for a set of profile version IRIs
Map<Node, PropertyInfo> properties = registry.getPropertiesAndDatatypes(profileVersionIris);

// Header properties are looked up per CIM namespace
Map<Node, PropertyInfo> headerProps =
registry.getHeaderPropertiesAndDatatypes(cimNamespace);

The returned maps are thread-safe for reading.

Resolving properties with an rdfs:range instead of a CIM datatype

Some properties carry no cims:dataType at all, only an rdfs:range. In that case the registry asks Jena's TypeMapper whether the range IRI is a registered RDFDatatype. If it is, the property is resolved as a datatype property using that type; if not, the range is treated as a reference type (the domain class of an object property) as before. This is what allows user-defined, non-XSD datatypes to work without a cims:dataType annotation, as long as the datatype IRI is registered with TypeMapper beforehand.

Registering custom primitive types

If a profile uses a primitive type the library does not map out of the box, register a mapping from the CIM primitive type name to a Jena RDFDatatype. The datatype must also be registered with Jena's TypeMapper; registering the same name again overwrites the previous mapping.

import org.apache.jena.datatypes.xsd.XSDDatatype;

registry.registerPrimitiveType("Voltage", XSDDatatype.XSDdouble);
registry.registerPrimitiveType("Current", XSDDatatype.XSDdouble);

// Inspect the full primitive-type mapping
Map<String, RDFDatatype> mapping = registry.getPrimitiveToRdfDatatypeMapping();
Validate queries against these profiles

The same profile ontologies power CIMVocabCheck, which statically checks SPARQL and SHACL against the schema — unknown classes/properties, domain/range violations, and datatype mismatches — without running anything.