Krande / ifcdb

Experiments with IFC as a database using EdgeDB
https://krande.github.io/ifcdb
10 stars 1 forks source link

Passing changes to/from EdgeDB and Unordered lists in IFC/Diffing technique #9

Open Krande opened 1 year ago

Krande commented 1 year ago

Hey @aothms,

I am quite close to having a working PoC of EdgeDB as a backend IFC DB for Blender.

Right now I am focusing on how specific changes are passed to/from the EdgeDB database.

Therefore, I have 2 questions about the IFC schema that I find challenging when roundtripping changes to/from the EdgeDB database that I wanted to get your thoughts on.

Q1 -> IfcRepresentation.Items is a SET (unordered list).

Any suggestions on how I can ensure that I am updating the correct item in the scenario I would have multiple IfcRepresentationItem objects and the IfcRepresentationItem does not have any unique markers? Currently I am thinking I would have to replace the entire set :(

FYI see this for a more detailed description of the issue I am facing and a specific example.

Q2 -> My Diffing technique

Inspired by the IfcDiff module, I made my own class that also uses deepdiff but compares all rooted elements (each rooted element is compared recursively) and it returns a dictionary with precise property changes and their path per rooted element.

My question is then whether you think this strategy would miss any changes to non-rooted elements (inverse attribute-objects etc.) and if so any tips on how I could catch those?

aothms commented 1 year ago

Like all problems in life, I think this is mostly caused by IfcOpenShell.

We know IfcRepresentation.Items is a SET but still we return a Python list. What does EdgeDB give you?

I think when reading an IFC model we should be able to tweak the data types. If we map an Express SET to a python (frozen)set then equality comparison would work. Making this change now in ifopsh is a bit drastic, but we should be able to work out some sort of mapping function to help.

import pprint
import ifcopenshell
from functools import partial
# pip install frozendict
from frozendict import frozendict

f = ifcopenshell.open("d.ifc")
inst = [i for i in f.by_type('IfcShapeRepresentation') if len(i.Items) > 1][0]

def transform_aggregates(f, val, attr=None, ent=None):
    if isinstance(val, dict):
        ent = ifcopenshell.ifcopenshell_wrapper.schema_by_name(f.schema).declaration_by_name(val['type'])
        def inner(kv):
            return kv[0], transform_aggregates(f, kv[1], attr=kv[0], ent=ent)
        return frozendict(map(inner, val.items()))
    elif isinstance(val, (list, tuple)):
        adef = [a for a in ent.all_attributes() if a.name() == attr][0]
        if adef.type_of_attribute().type_of_aggregation_string():
            return frozenset(map(partial(transform_aggregates, f), val))
    else:
        return val

tree1 = inst.get_info(include_identifier=False, recursive=True)
pprint.pprint(tree1)
tree2 = transform_aggregates(f, tree1)
pprint.pprint(tree2)

See above for a rough sketch (doesn't work for list of list yet). The difficulty is in the nesting. If we want attributes to be sets, they need to be hashable. But neither entity_instance nor dict is hashable because they both can be changed.

So we need to use both frozendict and frozenset so that they can be contained in higher level sets. It's a bit obscure, and I don't know how deepdiff would treat these kind of things. But the good news is that you can readily compare them for equality in python.


There is one non-rooted entity instance that in many times conceptually is a root, it's IfcStyledItem, reachable from the IfcRepresentationItem.StyledByItem.

Krande commented 1 year ago

Like all problems in life, I think this is mostly caused by IfcOpenShell.

Haha :)

Thanks for getting back to me so quickly!

Before delving into the matter in your response, I can at least say that EdgeDB gives me an ordered list for Items (defined as a multi link in edgedb).

Krande commented 1 year ago

Oops, I misspoke. EdgeDB's multi link apparently does NOT return an ordered list as a default. But there are options to order items in multi link sets in EdgeDB. I'll try to find a way to order the items in EdgeDB using an identifier that makes sense!