How to validate strings against EXPRESS IfcShapeRepresentationTypes function cases
A technical question here. With IfcOpenShell, how can I programmatically check if a certain pair of strings is within IFC schema admitted values for RepresentationIdentifier
and RepresentationType
, as provided on the tables from the IfcShapeRepresentation documentation? They are not an EXPRESS enumeration, so I'm having a hard time figuring this out.
I've found that if I build an IfcShapeRepresentation
entity instance using the two attributes to be validated, and then check for the EXPRESS rule for function IfcShapeRepresentationTypes
, it does the job:
ifcopenshell.express.rules.IFC4.IfcShapeRepresentationTypes(shape_representation.RepresentationType, shape_representation.Items)
However, that requires to declare an entity instance. Is there a simpler way? For instance, when checking for the values of ContextIdentifier
and ContextType
in an IfcGeometricRepresentationContext
(if wanting to validate them against the same referred tables), it seems overkill to declare, validate and delete an IfcShapeRepresentation
.
Comments
Good question, ping @aothms - I'm not sure how :)
Yeah well, keep in mind that from the outside we don't know how 'complex' an express function is. Also keep in mind that
IfcShapeRepresentationTypes
goes to quite big effort in some cases to see whether e.g Curve is 2D or 3D, which means invoking the Dim derived attribute which can call another function such as IfcCurveDim(). So using the same function on a RepContext is not even really possible to begin with.If you want the string labels, maybe you can use the python ast (a bit messy) or you can use ifcopenshell.express.express_parser to get the express AST. But I don't think it gets you much closer to what you want..
Thanks for the help @aothms !
I see... so there's not any straightforward solution. I wonder why these two lists aren't an Enum in IFC, although that doesn't seem like a quick and easy thing to change.
I got into this one because I was thinking of the high level API as a tool which could validate arguments at runtime up to some useful degree of correctness (as hinted in #3774). In addition to
ifcopenshell.validate
for EXPRESS rules, which although being ultra powerful, could make it for a slower workflow to fix dumb mistakes. For instance, I'm thinking something that catches "Axes" identifier, instead of "Axis", when creating a representation subcontext, that kind of thing. Maybe it looks like a very minor issue, but I believe a good API design should be explicit on what it covers, issuing warnings or raising errors when any edge case falls outside of what the code was conceived for. That, in turn, makes the little technical debt more visible and thus easier to be addressed with time.A variation of the abstract syntax tree approach (Python case), returning only the values of interest, could be as in the code below. But of course, 1) this is not handy at all to perform runtime checks and 2) the visit method is curated for exactly this specific EXPRESS function... which feels very volatile.
And then the representation types could be obtained as (for any required IFC version):
So in summary, I guess the best option for what I'm thinking is to just use this code offline and hardcode the representation types for each schema version.
I have the general impression that checking WRs are expensive, so although it's ideal for the API to check it "on-the-fly", it would be nice for this to be disabled for performance if it does become a performance concern. This is all speculative of course.
Yes, I agree that on top of what I'm proposing, there might need to be an extra arg
validate=False
around all the API calls. Then, it'd be possible to keep validation on to prototype, and turn it off for production.Just thinking out loud now. Are you planning to migrate the high level API to C++ at some point? that would yield real performance, specially if concurrency/multithreading is taken into account (although that could be difficult to implement without requiring some knowledge from the Python API user also). As it is now, it's not like the typical usage of numpy or PyTorch, in which one barely executes any pure Python code.
Or something like
model.set_strict_validation(True)
to toggle on / off. Similar to how you can enable transactions.Yes, and more than that. I think we should always be critically aware of the appropriateness of different languages. Python is great for development speed, but definitely not great for performance. So sooner or later I expect more of the API to become "upstreamed". We'd probably target performance bottlenecks first (and this process has already been happening in the drawing / model loading area). Prior to that, the API needs a lot of work to make it work for batch editing as opposed to single objects. Batch editing relationships would already provide a huge performance gain regardless of language. (instead of setting RelatedObjects 200 times, just set it once). Similarly, batching ownership history changes per transaction is a smart thing to do and has been on my mental todo list for a long time.
But even more than that, sooner or later I think we also need to accept that just being a Blender Add-on may not cut it for performance. We may need to consider forking Blender to add some specific functions around drawing generation, culling of large models, etc ...
There's years of work ahead of us!
I think this batching is also hack around the fact that the ifcopenshell data model is so closely related to the serialization format, but it's a more fundamental problem that also involves schema. Ultimately it needs to become much cheaper to do operations on edges in the graph. And there there is a little bit of tension also with the NativeIFC concept. Can we introduce just the minimal amount of abstractions around things like objectified relationships, while still being fully native?
Nice work! But, I think I politely disagree. Collecting the string cases from the function is a one-time operation. It is not really significant wrt to the start up time of the interpreter. It is also not that specific to that single function. It's specific to all functions that use a Express CASE statement which gets compiled into a python if-elif. So there could be others. One thing I can imagine is that we detect this situation in the python code generation step. That instead of converting this Express function to a mere python function, we convert it to a Python functor / class with call function (it technically already is) that is, in addition to validating, also able to provide a bit more introspection in what the function is doing (e.g in this case provide a list of case label strings). Not something I can work on in the near future though in terms of time, but if you want to have a go with the code generator I'd be happy to assist.
Cool! I never thought of culling in a desktop app, are you planning to build cities? ;)
I guess it's all this pyparsing sorcery under
express.express_parser
andexpress.nodes
, if not any C++ I'm not seeing. I don't know if I'm going to have time for this in the near future, either, but if I do I'll come back at it!Codegen is here for express function -> python def is here:
https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py#L565-L575