Fix/enum parameter data type - #320
corroleaus wants to merge 8 commits into
Conversation
CXF-Core.jsonld and the G36 SetPoints/CoolingCoil reference predate the explicit
prefix bindings in getCxfGraph, so they still spell every IRI out in full while
fresh output abbreviates with the S231 and ex prefixes. Regenerating rewrites
the files wholesale without changing their meaning, which would bury any real
change in serialization noise, so do it on its own.
node app.js -f Buildings/Controls/OBC/CDL -o cxf --elementary --cxfCore \
--prettyPrint
cp cxf/CXF-Core.jsonld .
Compared as RDF rather than as text, both files are unchanged: CXF-Core.jsonld
5197 triples before and after with none added or removed, CoolingCoil.jsonld
153. The tests already compare CXF as graphs, so they pass either way.
CXF recorded a data type only for parameters and constants declared as one of
the four Modelica built-ins. An enumeration-typed parameter therefore carried
neither S231:isOfDataType nor rdf:type S231:Parameter, so its type survived only
when a fully-qualified default literal happened to sit in S231:value, and not at
all when the declaration had no default:
parameter Buildings.Controls.OBC.ASHRAE.G36.Types.VentilationStandard venStd
"Ventilation standard, ASHRAE 62.1 or Title 24";
emitted a node with only accessSpecifier, description and label. A consumer
cannot recover the enumeration, which is needed to check members and to resolve
conditional expressions over them.
Resolve the declared type instead of matching it against a fixed list.
resolveEnumerationType locates the class through searchPath, confirms it
declares `type <Name> = enumeration(`, and takes the qualified name from that
file's own within clause. This is required because a declaration carries the
type exactly as written, which may be fully qualified, partially qualified
(CDL.Types.SimpleController) or relative to an enclosing package
(Types.VentilationStandard); resolveTypeSpecifier returns early on any dotted
name and so never resolves the latter two.
The emitted node uses the S231 namespace for CDL types and the example namespace
otherwise, matching the prefix rule the enumeration_class branch already applies
when it emits the enumeration itself. A type that is neither a built-in nor an
enumeration still yields no isOfDataType triple.
Adds ParameterWithEnumeration, covering an enumeration parameter with and
without a default, for a CDL and a non-CDL enumeration.
The updated references add only these triples: two per enumeration parameter in
TestEvaluation_4 and G36 SetPoints/CoolingCoil, and ten in CXF-Core for
Reals.PID, Reals.PIDWithReset, Sources.CalendarTime and Sources.TimeTable.
resolveEnumerationType assumed the file located by searchPath declares a class of the same name, so it only recognised one-enumeration-per-file layouts. A package that groups its enumerations in a single file — Buildings/Templates/ Components/Types.mo, Modelica/Fluid/Types.mo — was located correctly and then rejected, losing the data type for parameters that reference it. Look the enumeration up by the last segment of the type specifier instead, and build the qualified name from the class the file itself declares. Guard the reconstruction with the invariant that the result must end with the type specifier as written. It does not hold for a monolithic package.mo holding nested packages: Modelica.Media.Interfaces.Choices.ReferenceEnthalpy resolves to Modelica/Media/package.mo, where the enumeration sits two packages below the class the file declares, and would otherwise have been rebuilt as the wrong name Modelica.Media.ReferenceEnthalpy. Checked over every fully-qualified type reference in the Buildings library and the MSL: 112 resolve as enumerations, 0 with a name that disagrees with the reference as written. Blank string literals and comments before matching. Classes carry their documentation in info strings and reference material quotes declaration syntax verbatim, so ModelicaReference/package.mo contains `type E = enumeration(...)` inside a string; it is the one such case in 8187 files, and widening the lookup to grouped declarations reads deeper into exactly the files that carry large documentation blocks.
…rectory searchPath walks down from each MODELICAPATH entry, so it locates a class only when MODELICAPATH holds the directory containing the library. Pointed at the library directory itself — `…/modelica-buildings/Buildings`, an equally common convention — it looks for `…/Buildings/Buildings/Controls/…` and returns nothing, so every enumeration silently lost its data type again and the emitter fell back to writing no triple at all. Confirmed against a consumer that parses staged models this way: 221 CXF files, 1395 isOfDataType triples for built-ins, zero for enumerations. Fall back to getMoFiles, which matches by path suffix and so resolves the class under either convention. It is only reached when searchPath finds nothing, and the result is still checked for an enumeration declaration and validated against the type specifier as written, so the fallback cannot widen what qualifies. getMoFiles returns its input unchanged when it finds nothing, hence the filter on a real `.mo` path. Key the cache on MODELICAPATH too. A caller may parse successive models against different library sets in one process — modelica-json is used in-process and the variable swapped per invocation — and a lookup that failed under one set must not be reused under another.
…numeration searchPath resolves relative to the enclosing package, which under a MODELICAPATH that names the library directory itself can land on a plausible but unrelated file: `Types.VentilationStandard` declared inside `…G36.AHUs.MultiZone.VAV` resolves to `Buildings/Types/package.mo`, the top-level Buildings.Types package that happens to sit one level below the MODELICAPATH entry. Because that returned a file, the getMoFiles fallback added in the previous commit never ran and the data type was lost again. Try the fallback whenever no candidate checks out rather than only when searchPath returns nothing. Candidate acceptance already required an enumeration declaration of the right name whose rebuilt qualified name ends with the specifier as written, so a wrong file is rejected rather than trusted, and extending the search cannot widen what qualifies. Extracted as firstEnumerationType, which walks candidates in order and returns the first that satisfies both checks.
|
@corroleaus thank you for your proposal and fix to handle enumerations. What do you think of proposing a new predicate called This would require us to parse |
The ontology models every data type as a class plus one singleton individual of it: S231:RealDatatype is the class and S231:Real the individual, and likewise S231:EnumerationDatatype and S231:EnumerationType. The enumeration_class branch typed every concrete enumeration with the individual, which declares SimpleController a sibling of S231:Real rather than an instance of the enumeration class. Nothing then connects it to S231:DataType, so the Parameter shape's `sh:class S231:DataType` on isOfDataType cannot reach it, which is the failure a separate hasEnumerationType predicate was proposed to route around. Use the class. The enumeration node is then an instance of S231:EnumerationDatatype and, as before, the class of its own members, so a parameter reaches S231:DataType in two hops: isOfDataType, rdf:type, rdfs:subClassOf. S231:EnumerationType stays in the ontology as the generic marker, parallel to S231:Real, and is no longer emitted. Updates the enumeration references and CXF-Core.jsonld: one @type per enumeration class, nothing else.
S231:Datatype is referenced six times in s231ClassesProperties.ttl and never declared: the rdf:type of S231:Real, S231:String and S231:EnumerationType, and the rdfs:subClassOf of their companion classes S231:RealDatatype, S231:StringDatatype and S231:EnumerationDatatype. Everything else spells it S231:DataType, which is the declared class and the one the Parameter shape names in `sh:class S231:DataType`. Three consecutive blocks written with the wrong spelling, companion class name included, so they were self-consistent enough to go unnoticed. Because of it every Real- and String-typed parameter trips that shape today, at sh:Info, and enumeration classes instantiating S231:EnumerationDatatype would too. The generator emits neither spelling, so the change is confined to the ttl and the CXF-Core.jsonld it is merged into.
The enumeration_class branch reused the label/value/description emission written for members, so every enumeration class carried S231:value set to its own short name: "SimpleController" on S231:Buildings.Controls.OBC.CDL.Types.SimpleController. A class has no value; the triple described nothing and shipped in CXF-Core.jsonld for all four CDL enumerations. Members keep their S231:value, which is the member name a parameter's S231:value selects.
Thanks for the feedback! See updated PR body and Diff. |
An enumeration-typed parameter carries no data type in CXF.
isOfDataTypeis only emitted forReal,Integer,BooleanandString—lib/cxfExtractor.js, next to a standing// TODO: check if enumeration should be here.Defaulted enums survive by accident: the fully-qualified literal in
S231:valuelets a reader strip the last segment and infer the type. Without a default, nothing is left.{ "@id": "ex:….SeriesFanVVF.Controller.venStd", "S231:accessSpecifier": "public", "S231:description": "Ventilation standard, ASHRAE 62.1 or Title 24", "S231:label": "venStd" }A consumer cannot tell this is an enumeration, let alone which members are valid. The node is also not typed
S231:Parameter, so the Parameter shape never applies to it.Fix. Resolve the declared type instead of matching a fixed list.
resolveEnumerationTypelocates the class viasearchPath, confirms it declarestype X = enumeration(...), and takes the qualified name from that file'swithin. Needed because the type is stored as written —CDL.Types.SimpleController,Types.VentilationStandard— andresolveTypeSpecifierreturns early on any dotted name. Object is the enumeration class node:S231:for CDL types,ex:otherwise, same rule theenumeration_classbranch already uses.{ "@id": "ex:….SeriesFanVVF.Controller.venStd", "@type": "S231:Parameter", "S231:accessSpecifier": "public", "S231:description": "Ventilation standard, ASHRAE 62.1 or Title 24", "S231:isOfDataType": { "@id": "ex:Buildings.Controls.OBC.ASHRAE.G36.Types.VentilationStandard" }, "S231:label": "venStd" }Why
isOfDataType→ the concrete class, and nohasEnumerationType. The ontology already has a class plus a singleton individual for every data type:S231:EnumerationTypeis theS231:Realslot — the generic marker, already there.S231:EnumerationDatatypeis theS231:RealDatatypeslot — what a concrete enumeration should instantiate. The generator was using the individual:{ "@id": "S231:Buildings.Controls.OBC.CDL.Types.SimpleController", "@type": "S231:EnumerationType" }That makes
SimpleControllera sibling ofS231:Real, not an instance of the enumeration class, sosh:class S231:DataTypeon the Parameter shape can't reach it. With the right term it's two hops:Same shape as s223:
hasEnumerationKind→ the concrete kind,hasValue→ the member. HereisOfDataTypeandS231:value. If a marker is wanted,sh:class S231:EnumerationDatatypeselects exactly the enumeration-typed parameters. No new term either way. On parsing: the generator already opens the class file and checks forenumeration(before emitting, so it knows at generation time.Two one-line fixes, both here.
enumeration_classbranch:s231Ns('EnumerationType')→s231Ns('EnumerationDatatype').lib/s231ClassesProperties.ttl:S231:Datatypeis referenced six times and never declared —Real,String,EnumerationTypeand their companion classes. Everything else spells itS231:DataType. Independent of enums: everyRealandStringparameter trips the shape today (atsh:Info), and it ships inCXF-Core.jsonld.Also dropped:
"S231:value": "SimpleController"on the class node. Leftover from reusing the member emission — a class has no value. Members keep theirs.After. From
test/reference/cxf/Buildings/Controls/OBC/CDL/Types/SimpleController.jsonldandtest/reference/cxf/test/FromModelica/ParameterWithEnumeration.jsonld:{ "@id": "S231:Buildings.Controls.OBC.CDL.Types.SimpleController", "@type": "S231:EnumerationDatatype", "S231:description": "Enumeration defining P, PI, PD, or PID simple controller type", "S231:label": "SimpleController" } { "@id": "S231:SimpleController.PI", "@type": "S231:Buildings.Controls.OBC.CDL.Types.SimpleController", "S231:description": "PI controller", "S231:label": "PI", "S231:value": "PI" } { "@id": "ex:FromModelica.ParameterWithEnumeration.conTyp", "@type": "S231:Parameter", "S231:accessSpecifier": "public", "S231:description": "Enumeration parameter of a CDL type", "S231:isOfDataType": { "@id": "S231:Buildings.Controls.OBC.CDL.Types.SimpleController" }, "S231:label": "conTyp", "S231:value": "Buildings.Controls.OBC.CDL.Types.SimpleController.PI" }The enumeration is an instance of
S231:EnumerationDatatypeand the class of its own members — same punning as before, and as s223. From a parameter with no default:Validation. Shapes
lib/s231ClassesProperties.ttl, data = ontology + one Parameter per row, asgetCxfCoremerges them. pyshacl 0.40.1 and shifty-cli 0.3.0 agree.isOfDataType→S231:EnumerationTypeS231:EnumerationDatatypeS231:Real,S231:StringS231:Integer,S231:BooleanNot in this PR.
S231:valueon the parameter is a string, so a shape can't check the selection against the enumeration. Making it{ "@id": … }needs fully qualified member IRIs first —S231:SimpleController.PIvs…CDL.Types.SimpleController.PItoday — which rewrites every member inCXF-Core.jsonld. Follow-up. Same forRealDatatypevsIntegerDataType.Fixtures.
Enumeration1,SimpleController,Smoothness,Extrapolation,ZeroTime×2,ParameterWithEnumeration, bothCXF-Core.jsonld.