Aqui está a essência de como estou fazendo as coisas e quais tipos de inferência/modelagem estão habilitados
Quase tudo que usamos se enquadra no OWL Lite, com exceção da propriedade owl:hasValue
que usamos para definir um mapeamento 1-1 entre conjuntos de tags e nomes de classes. Por causa disso, a solução atual se enquadra no OWL DL.
Isso também significa que precisamos de um raciocinador, mas felizmente não um que suporte OWL Full :). OWL-RL é uma boa escolha que funciona com RDFlib.
# G is our RDFlib Graph
# apply reasoning to expand all of the inferred triples
import owlrl
owlrl . DeductiveClosure ( owlrl . OWLRL_Semantics ). expand ( G )
# get namespaces
print ( list ( G . namespaces ()))
G . query ( "SELECT ?x WHERE { ?x brick:hasTag tag:Equip }" )
Podemos serializar a forma expandida do gráfico para disco se precisarmos usar um processador de consulta SPARQL que não suporte raciocínio.
https://brickschema.org/schema/1.1.0/Brick#
owl:Class
e são organizadas em uma hierarquia com rdfs:subClassOf
owl:equivalentClass
skos:definition
brick:AHU_Average_Exhaust_Air_Static_Pressure_Sensor
é apenas um Average_Exhaust_Air_Static_Pressure_Sensor
que é um ponto de uma AHU.As classes raiz que definimos são:
Equipment
Location
Point
Tag
Substance
Quantity
(Relacionamentos são o termo Brick para ObjectProperties de coruja entre instâncias de classes)
No nível superficial, o relacionamento funciona da mesma forma que no Brick original. Todos os mesmos relacionamentos ainda existem (onde me lembrei de defini-los), e eles têm seus inversos definidos usando owl:inverseOf
.
Domínios e intervalos são definidos em termos de classes. Afirmar que o rdfs:range
de um relacionamento é da classe brick:Equipment
significa que o objeto do relacionamento deve ser uma instância da classe brick:Equipment
.
Este protótipo inclui sub-relacionamentos além de relacionamentos. Sub-relacionamentos podem ser usados no lugar do super-relacionamento para adicionar mais detalhes à natureza desse relacionamento. O único exemplo até agora é que feedsAir
é uma subpropriedade de feeds
.
Algo a descobrir é como poderíamos inferir o relacionamento feedsAir
; talvez se os dois equipamentos de endpoint tiverem o air
tag e um relacionamento feeds
? Isso pode ser algo que precisa ser especificado explicitamente, em vez de inferido.
https://brickschema.org/schema/1.1.0/BrickTag#
skos:definition
Isso é feito declarando uma classe Brick (por exemplo, Air_Temperature_Sensor
) como equivalente a uma classe anônima, que é uma owl:Restriction
que é a interseção de entidades que possuem determinadas tags.
# in turtle format
brick:Temperature_Sensor a owl:Class ;
rdfs:subClassOf brick:Sensor ;
owl:equivalentClass [ owl:intersectionOf (
[ a owl:Restriction ;
owl:hasValue tag:Sensor ;
owl:onProperty brick:hasTag
]
[ a owl:Restriction ;
owl:hasValue tag:Temperature ;
owl:onProperty brick:hasTag
]
) ] .
A primeira owl:Restriction
é o conjunto de todas as classes que possuem tag:Sensor
como valor para uma de suas propriedades brick:hasTag
.
Isto significa que um sensor de temperatura :ts1
poderia ser definido de duas maneiras diferentes e o raciocinador inferiria os outros triplos:
# using classes
:ts1 a brick:Temperature_Sensor
# using tags
:ts1 brick:hasTag tag:Temp
:ts1 brick:hasTag tag:Sensor
Brick agora define uma hierarquia de substâncias ( substances.py
) e uma hierarquia de quantidades ( quantities.py
). Substâncias e quantidades podem estar relacionadas a equipamentos e pontos.
Nem tudo isso é implementado. No protótipo atual, os sensores estão relacionados a substâncias e quantidades através da relação brick:measures
.
:ts1 a brick:Temperature_Sensor
:ts1 brick:measures :Air
# this implies the following
:ts1 a brick:Air_Temperature_Sensor
Podemos subclassificar substâncias para fornecer contexto em nível de sistema ou processo para suas definições:
:ts1 a brick:Sensor
:ts1 brick:measures brick:Return_Air
:ts1 brick:measures brick:Temperature
# implies...
:ts1 a brick:Return_Air_Temperature_Sensor
Brick usa restrições OWL para refinar classes com base em tais relacionamentos. Para este exemplo, porque :ts1
mede Air (especificamente a classe brick:Air
), OWL infere nosso sensor como um brick:Air_Temperature_Sensor
.
Aqui está a aparência da definição em tartaruga:
brick:Air_Temperature_Sensor a owl:Class ;
rdfs:subClassOf brick:Temperature_Sensor ;
owl:equivalentClass [ owl:intersectionOf ( [ a owl:Restriction ;
owl:hasValue brick:Temperature ;
owl:onProperty brick:measures ] [ a owl:Restriction ;
owl:hasValue brick:Air ;
owl:onProperty brick:measures ] ) ] .
Nota : estamos usando classes como valores aqui, o que é diferente do resto do Brick. Isso é chamado de "trocadilhos". Isso evita a necessidade de criar instâncias de substâncias para nossos sensores medirem e assim por diante, mas reserva a possibilidade de implementar isso no futuro. Instâncias de substâncias podem modelar regiões/pedaços de “coisas” em um estágio de um processo, por exemplo, a água que entra em um resfriador ou a região de mistura de ar de uma unidade de tratamento de ar.
Em vez de nos perdermos na confusão de Sísifo sobre como formatar tudo como YAML, estamos apenas usando dicionários Python para que não precisemos nos preocupar com nenhuma (bem, nem tanto) lógica de análise.
definitions = {
"Lighting_System" : {
"tagvalues" : [ # Lighting_System class is equivalent to the Lighting tag
( BRICK . hasTag , TAG . Lighting ),
# if you have more required tags add them as their own tuple in the list
],
# defining subclasses. This can be nested ad-infinitum
"subclasses" : {
"Lighting" : {
"subclasses" : {
"Luminaire" : {},
"Luminaire_Driver" : {},
},
},
"Interface" : {
"subclasses" : {
"Switch" : {
"subclasses" : {
"Dimmer" : {},
},
},
"Touchpanel" : {},
},
},
},
}
}
define_subclasses ( definitions , BRICK . Equipment )
Por enquanto, o código é a documentação. Veja equipment.py
, point.py
, etc para exemplos e como adicionar a cada uma das hierarquias de classe.
Autores: Gabe Fierro