A car fleet -- FunctionalComplex, Collection, Quantity, and their part-whole properties
gUFO's gufo:Object is not a single undifferentiated bucket. It has three subclasses that
differ in how their parts relate to the whole, and each comes with its own part-whole property.
This example shows all three at once, in a domain where they naturally coexist: a car (a complex made of
parts playing different roles), a fleet (a collection of uniform members), and fuel (a quantity of stuff).
A car-sharing operator, CityWheels, has a fleet of cars. Each car is built from components (an engine, wheels). The fleet itself gains and loses cars as they are bought and retired. Cars run on gasoline -- a blend of a petroleum distillate and ethanol -- stored in bulk in a tank at the depot before being pumped into individual cars.
A gufo:FunctionalComplex is what most ordinary objects are: a whole whose parts (components)
each contribute something different. A car's engine and its wheels do not play the same role in the car, so
:Car, :Engine and :Wheel are all functional complexes, and the
part-whole link between them is (a sub-property of) gufo:isComponentOf:
:Car a owl:Class , gufo:Kind ;
rdfs:subClassOf gufo:FunctionalComplex .
:Engine a owl:Class , gufo:Kind ;
rdfs:subClassOf gufo:FunctionalComplex .
:isEngineOf a owl:ObjectProperty ;
rdfs:subPropertyOf gufo:isComponentOf ;
rdfs:domain :Engine ;
rdfs:range :Car .
:Car001Engine a :Engine ;
:isEngineOf :Car001 .
Declaring a domain-specific sub-property (:isEngineOf) rather than using
gufo:isComponentOf directly is optional but recommended: it lets you constrain domain and range
per part type (an engine is a component of a car; a wheel, of a car too, but through its own property) while
every asserted fact remains, by subsumption, also an instance of the general gufo:isComponentOf
relation.
The fleet is different: every member -- every car in it -- plays the very same role ("being a member of
the fleet"). That uniform structure is what gufo:Collection is for. Because CityWheels buys and
retires cars over time, the fleet's membership is variable, so it is specifically a
gufo:VariableCollection (the alternative, gufo:FixedCollection, is for collections
like a hand of cards dealt in a game, where swapping a member yields a strictly different collection):
:Fleet a owl:Class , gufo:Kind ;
rdfs:subClassOf gufo:VariableCollection .
:isFleetMemberOf a owl:ObjectProperty ;
rdfs:subPropertyOf gufo:isCollectionMemberOf ;
rdfs:domain :Car ;
rdfs:range :Fleet .
:CityWheelsFleet a :Fleet .
:Car001 :isFleetMemberOf :CityWheelsFleet .
FunctionalComplex or Collection? gUFO's own documentation flags this
exact case: if the fleet's cars are genuinely undifferentiated -- each just "a member of the fleet" -- then
Fleet is a collection. If the cars actually play distinct functional roles in it -- "cargo
car", "passenger car" -- then the fleet is really a functional complex, whose parts play those distinct
roles. This is not a free choice for the ontology to make: it is a fact about how the fleet is structured,
which the ontology is responsible for getting right. What genuinely is a modeling choice is scope --
whether the ontology needs to represent that role differentiation at all. Choosing to model the fleet as a
plain collection because the roles do not matter for the task at hand is a legitimate (if coarser-grained)
conceptualization.
Collections can also have sub-collections, when a subset of the members forms a collection in its own
right -- gufo:isSubCollectionOf (transitive) is for exactly this, distinct from membership:
:ElectricSubfleet a :Fleet ;
gufo:isSubCollectionOf :CityWheelsFleet .
:Car001 :isFleetMemberOf :ElectricSubfleet .
Gasoline has neither distinct components nor uniform "members" -- it is not made of parts in the way a
car or a fleet is. gufo:Quantity models exactly this: a maximally self-connected
portion of stuff. Note the class name, :QuantityOfGasoline, not just :Gasoline:
its instances are individual portions of the stuff (this tankful, that delivery), not the material itself --
the same naming convention the gUFO usage guide uses for AmountOfMarble.
"Maximally self-connected" is not decoration; it is what constrains everything below. Any two portions
of gasoline in contact are, together, one quantity rather than two, because neither of them alone
is maximal. So a quantity of gasoline can never have a smaller quantity of gasoline as a part. What it does
have as parts are portions of the different stuffs it is a blend of: pump gasoline is a blend of a
petroleum distillate and ethanol, and each of those is a maximally self-connected portion of its own stuff
even while mixed into the whole. This is the standard UFO illustration -- a portion of wine has a portion of
water and a portion of alcohol as sub-quantities -- and gUFO's own definition of
gufo:isSubQuantityOf uses it verbatim: "the quantity of water in a wine glass is a sub-quantity
of the wine in that glass".
:QuantityOfGasoline a owl:Class , gufo:Kind ;
rdfs:subClassOf gufo:Quantity .
:QuantityOfEthanol a owl:Class , gufo:Kind ;
rdfs:subClassOf gufo:Quantity ;
owl:disjointWith :QuantityOfGasoline .
:isEthanolPortionOf a owl:ObjectProperty , owl:FunctionalProperty ;
rdfs:subPropertyOf gufo:isSubQuantityOf ;
rdfs:domain :QuantityOfEthanol ;
rdfs:range :QuantityOfGasoline . # part and whole are of *different* kinds
:Car002FuelTankContents a :QuantityOfGasoline .
:Car002EthanolPortion a :QuantityOfEthanol ;
:isEthanolPortionOf :Car002FuelTankContents .
It is tempting to declare :isPortionOf with :QuantityOfGasoline as
both domain and range, and then assert that a delivery lot poured into the depot tank is a
sub-quantity of the tank's supply. That is exactly what quantities rule out. Once the lot is in the tank it
is in contact with the rest of the gasoline, so it is not a maximal portion any more -- it is not a quantity
at all, and there is nothing there for gufo:isSubQuantityOf to relate. Quantities are
maximally self-connected and therefore do not have parts of the same kind. In this
ontology the quantity kinds are declared pairwise disjoint, which is enough for a DL reasoner to
reject the mistake outright: asserting :SomeGasoline :isEthanolPortionOf :OtherGasoline makes
the ontology inconsistent, because the property's domain would force that portion of gasoline to also be a
portion of ethanol.
Four further constraints come with subQuantityOf, each stated in the ontology as far as OWL
allows. The first three all fall out of the same two facts -- quantities are topologically connected, and
they are maximal:
:isEthanolPortionOf a owl:FunctionalProperty.:QuantityOfGasoline rdfs:subClassOf
[ a owl:Restriction ;
owl:onProperty [ owl:inverseOf :isEthanolPortionOf ] ;
owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger ;
owl:onClass :QuantityOfEthanol ] .
A reasoner puts this to work: assert two differently-named ethanol portions of the same gasoline and it
infers that they are the same individual -- which is exactly what maximality claims.owl:IrreflexiveProperty and owl:AsymmetricProperty safely. This example
therefore recovers, at the domain level, a constraint gUFO cannot state globally.gufo:standsInQualifiedParthood -- its pattern for
parthood holding only for a time -- "is not applicable for sub-quantities". Correspondingly, quantities are
extensional: change any part and you have a different whole (strong supplementation holds, unlike for
gufo:isSubCollectionOf, which satisfies only the weaker axiom). Essentiality is a modal claim
and beyond OWL; the cardinalities above are the part of it OWL can carry.Transitivity is the one characteristic not declared on the typed properties. It belongs to
gufo:isSubQuantityOf, which gUFO declares transitive (Guizzardi, 2005, p. 184) -- unlike
gufo:isComponentOf and gufo:isCollectionMemberOf, which are not. Each typed
property here relates one specific stuff to another, so it is not transitive on its own; its assertions
feed the transitive super-property instead:
:isBenzenePortionOf a owl:ObjectProperty ;
rdfs:subPropertyOf gufo:isSubQuantityOf ;
rdfs:domain :QuantityOfBenzene ;
rdfs:range :QuantityOfPetroleumDistillate .
:Car002BenzenePortion a :QuantityOfBenzene ;
:isBenzenePortionOf :Car002DistillatePortion .
# inferred: :Car002BenzenePortion gufo:isSubQuantityOf :Car002FuelTankContents
# -- which is what one means by saying the fuel in the tank contains benzene.
All of this is where quantities part company with functional complexes, and it is easy to get backwards.
Unbolt a wheel and put it back on and the car has regained the very same component -- components can come
and go while the whole keeps its identity. A quantity cannot: gUFO's gufo:Quantity comment
states that it "has a fixed constitution, and thus, removing or adding a sub-quantity would result in a
different quantity".
So what does one say about Tuesday's delivery being pumped into the depot tank, and about fuel
later being pumped from the tank into a car? Because constitution is fixed, none of these events gives an
existing quantity a new part or takes one away. Each yields a different quantity, and what survives
is a historical link, not a parthood link. gufo:historicallyDependsOn is the property for it
(the same one used for causal and historical dependence between events in
example 5 -- its domain and range are the fully general
gufo:ConcreteIndividual, not just gufo:Event):
:DepotSupplyBeforeTuesday a :QuantityOfGasoline .
:TuesdayDeliveryLot a :QuantityOfGasoline .
:DepotSupplyAfterTuesday a :QuantityOfGasoline ;
gufo:historicallyDependsOn :DepotSupplyBeforeTuesday , :TuesdayDeliveryLot .
:Car002FuelTankContents gufo:historicallyDependsOn :DepotSupplyAfterTuesday .
Note what is not asserted: neither input is a sub-quantity of the mixture. They are of the same
kind as it, and after mixing neither is a maximally self-connected portion any more. Nor did
:DepotSupplyBeforeTuesday simply grow into :DepotSupplyAfterTuesday -- a quantity
does not survive a change of constitution.
If the ontology needs to track "the tank's supply" as one thing that persists across deliveries, the
answer is not to stretch gufo:isSubQuantityOf to cover it. The persisting thing is the tank (a
gufo:FunctionalComplex), successively constituted by different quantities of gasoline --
see gufo:isConstitutedBy and the gufo:TemporaryConstitutionSituation pattern in the
gUFO usage guide, which uses exactly this arrangement for the Venus de Milo and its quantities of
marble.
| gUFO class | What its parts have in common | Part-whole property | In this example |
|---|---|---|---|
gufo:FunctionalComplex | Each part plays a different role | gufo:isComponentOf | Engine, Wheel → Car |
gufo:Collection | Every part plays the same role | gufo:isCollectionMemberOf, gufo:isSubCollectionOf | Car → Fleet |
gufo:Quantity | Parts are portions of the other stuffs the whole is a blend of -- never of the same stuff, which maximality rules out | gufo:isSubQuantityOf (essential, non-shareable) | Ethanol portion, distillate portion → Tank contents |
All of these are sub-properties of the top-level gufo:isObjectProperPartOf, which is itself a
sub-property of gufo:isProperPartOf (the part-whole relation shared with events -- see
example 5).
See ontology.ttl for the complete file.
Unlike example 1, this example is not primarily about the taxonomy of types that gufoshapes targets, so its main value here is a straightforward pass: both checks confirm the ontology is well-formed.
gufo.ttl + ontology.ttl is
consistent, and every class remains satisfiable: the disjointness of the three quantity kinds, the
functional sub-quantity properties and the exactly-one cardinality restrictions all hold together, and
:ElectricSubfleet being both a sub-collection of :CityWheelsFleet and itself a
:Fleet does not collapse anything. Add the same-kind sub-quantity assertion described above and
the reasoner reports the ontology inconsistent -- which is the point of declaring the kinds disjoint.cd examples/tests
.venv/bin/pytest -v -k objects-and-parts