Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 08 Nov 21:55
· 301 commits to main since this release
6cf798d

Breaking changes

  • Default output, all IFC profiles and the JSON output (Closes #98): RvtReaderOptions.ResolveCurrentCopies is now true by default, so an element that several partition streams hold is read from its current copy only, and the output changes for every file saved in several increments. --keep-all-copies (or ResolveCurrentCopies = false) restores the earlier reading of every copy; --resolve-current-copies is still accepted and does nothing. For files with shared work see Known issues.

  • API and command line: RvtReaderOptions.RevitLocalizationDirectory is [Obsolete] and ignored, and the --revit-localization option is deprecated and ignored (the CLI prints "ignored: texts are built in"): the texts are built in and the reader opens no file of an installed host application. Nothing replaces the option; remove it.

  • Full JSON and API: MaterialLayer.Function and Layers[].Function carry the names of the Revit API, None, Structure, Substrate, Insulation, Finish1, Finish2, Membrane and StructuralDeck (before: Unknown, Structure, Substrate, ThermalAir, Finish, Membrane), and types whose compound structure holds a Membrane or a StructuralDeck layer now have Layers; a wall whose type holds such a layer is exported as an IfcWallStandardCase instead of an IfcCurtainWall. Code that compares the earlier names must change (Refs #31, Refs #16).

  • API: Element.BoundingBox is now the axis-aligned outline stored in the element header, as the Revit API bounding box (null for elements with no stored outline, such as grids and levels). The tight box of the decoded geometry, which Element.BoundingBox held before, is Element.Geometry.BoundingBox, so FilteredElementCollector.WhereBoundingBoxIsNotNull now selects by the stored outline.

  • Default output, current IFC profile: quantities and property reals are written with 17 significant digits (2699.9999999999627), with a three-digit signed exponent, a point after an integer value and -0. for negative zero. The values are the same numbers, the text changes; the legacy profile keeps its 15-digit reals. There is no option to restore the earlier text.

  • Default output, IFC openings: the Depth of the opening of a door or window is the total layer width of its host wall type where the family cuts with a profile, and the Depth of a wall opening is the extrusion length of its solid, both without the 0.2 mm that was added before; a filled opening whose depth equals the width of its straight host wall is measured in the model frame where the host-axes measure does not apply. --ifc-opening-legacy-axes keeps the earlier selection of the frame, but the Depth loses the 0.2 mm in both cases.

  • Default output, current IFC profile: the GlobalId of an IfcRelAssociatesClassification is derived from the first character of the classification name and of the code and from the GlobalId of the first related object, so classification associations get new GlobalIds.

  • Default output, current IFC profile: the IfcStairFlightType of a spiral run has the predefined type SPIRAL, and its GlobalId changes with it.

  • Default output: some elements are no longer written as products: wall sweeps of the reveal kind, a wall record that is the type of another element and names no project phase, in a file whose geometry is not decoded a wall without a solid or face body, elements whose "Export to IFC" parameter is No (or "Export Type to IFC" of the type, while the instance is left at the default), and, in a file whose bounds are decoded, an element of an IfcElement class (other than a wall) without bounds. There is no option to restore them; the entries below give the rules.

  • Default output: a wall sweep, slab edge, fascia, gutter, roof soffit or 3D model text proxy is named after the fixed system family (Wall Sweep, Slab Edge, Fascia, Gutter, Compound Ceiling, Model Text) instead of the OST_ category name.

  • Synthesizes the ROOM_LEVEL_ID parameter (-1006916) and fallback ROOM_UPPER_LEVEL (-1006922) for room elements (OST_Rooms) in schema-read models (Revit 2023 and later) from LEVEL_PARAM (-1001952) or SCHEDULE_LEVEL_PARAM (-1002062) when no element references are present, restoring parameter parity with legacy-read models for API and JSON export consumers (Closes #141).

  • Current profile: stair runs and landings without ELEM_TYPE_PARAM resolve type-level Manufacturer in Pset_ManufacturerTypeInformation from their stair component type ahead of reference element type (Closes #75).

  • Decode statistics in the export report now record the element table (Global/ElemTable), streams in the Global and Formats storages, and Global/History, and streams with members rebuilt by structural recovery mark the report status as partial; a stream that holds bytes but not one gzip member (its header is gone) counts as one failed member, so the read is partial and not complete, also for the partition streams (Closes #60).

  • Documentation: clarifies in docs/export-report.md that export.exported in the export report counts Revit elements rather than exported product entities (a roof element writing both IfcRoof and IfcSlab counts once) (Closes #62).

  • Reads the axis-aligned outline stored in the element header (schema class 1340) as Element.BoundingBox across all Revit releases (2020 to 2027), matching the Revit API bounding box. The tight box of the decoded geometry stays on Element.Geometry.BoundingBox. Elements with no stored outline (such as grids and levels) have null BoundingBox, and elements whose records disagree remain unresolved. The files that carry their own class schema (Revit 2020, 2021 and 2023 and later) give the box to a read without the R22 layout as well, read by the structure of the header; the IFC export measures bodies by the box of the decoded geometry and not by this outline, except that a wall with decoded geometry is placed by it as the reference does, so a roof that only holds curtain panels and every element of a read without the R22 layout (outline, no geometry) are exported as before. The Length quantity of a wall placed by the outline can differ from the earlier value by micrometres. On 73 synthetic models, 1009 of 1009 elements with an API box match exactly and 216 of 216 without an API box remain null (Closes #99, Closes #68, Closes #67, Closes #66).

  • Export report: grid elements (OST_Grids) written by the grid pass as IfcGrid axes are now credited in exported and removed from the skip log, preserving the considered == exported + skipped invariant. Unwritten grid elements (such as types or disconnected lines) remain logged as skipped (Closes #59).

  • Current profile: a foundation whose profile gives no width (a round pile has neither an extrusion body nor a vertical prism) takes the Width quantity from the Width parameter of the instance, else of its type, so the round piles of the Revit 2025 structural sample carry the diameter their types store there (2 feet for the 24 inch families, 2.5 feet for the 30 inch one) as the reference output does. Foundations with an extrusion body or a vertical prism keep the width of their profile.

  • The export refuses a --report path that names not only the input file or the export output, but also --dev-external-dump and the compact measurement dumps (--dev-candidates, --dev-trace, --dev-facts, --dev-solids, --dev-user-params, --dev-collisions), so the run can no longer overwrite one of its own outputs with the report.

  • Reads a room phase of element id 0 from the room metadata too where a project phase can have that id (files whose element layout is read from their own schema, Revit 2023 and later), and the ROOM_PHASE and ROOM_PHASE_ID parameters of a room now also come from the room's own record when no phase reference resolves: the placed rooms of the Revit 2023 Japanese architectural sample (サンプル意匠.rvt, 113 rooms whose phase is the id-0 phase) and of a Revit 2025 architectural sample (54 rooms in the New Construction phase) carry them where none were written before, as the reference exports name their phase. Rooms whose phase reference resolves keep their values. In the IFC export more elements are contained in the IfcSpace of their room where the room phase is now read (910 elements before and 1155 after in the Revit 2025 Japanese architectural sample, 1149 in the reference export); 12 elements per file go to an IfcSpace where the reference keeps the storey (see Known issues).

  • Fix, current profile: an assembled stair run takes no Length quantity again. The count of risers of a sketched run without an end riser was changed to one less than its index span for the NumberOfRiser and NumberOfTreads attributes of the flight, and the Length of a run read that attribute, so such a run (a "Non-Monolithic Run" in the sample projects, 4000 mm in rac_basic_sample_project) got a Length that the reference export does not have. The Length now counts the treads of the index span, as before, whatever the attribute says; the attributes themselves are unchanged.

  • Current profile: a wall record that is the type of another element and names no project phase is no longer written as an IfcWall (the type is written from the instances that use it), and, in a file whose geometry is not decoded, a wall that holds neither a solid nor a face body is no longer written (a wall without a body has nothing to show). Curtain walls and walls with any body are unchanged, and the legacy profile is unchanged.

  • IFC export: an element whose "Export to IFC" parameter (instance) is No, or whose "Export Type to IFC" parameter (type) is No while the instance is left at the default, is no longer written; an instance set to Yes is written whatever its type says, and the wall openings of a wall left out go with it. The same rule holds for every writer that makes an entity from an element outside the product loop: the spaces, the grids, the assembly of a beam system, the site surface of a topography, the groups (model groups, area schemes, reinforcement systems) and the MEP systems; the members of a group or system left out are written as before. No sample of the reference corpus sets either flag, so the change is covered by synthetic tests only.

  • Does not write a product that has no body: in a file whose bounds are decoded, an element of an IfcElement class (a door, a window, a column, a proxy and so on, but not a wall) without bounds is skipped, except the classes that only group other products (curtain wall, roof, stair, ramp, element assembly), which are written without a representation. A file with no decoded bounds at all keeps its products as before. Bounds count as decoded only when they are valid (six finite values in order), so one element with damaged bounds neither turns the rule on for the others nor is taken for a body: it is rejected by its own bounds as before. A wall foundation (IfcFooting) is an element of the rule like the others. A freestanding door or window that has a body (no host wall) is now written; one without a body is still skipped.

  • Names a wall sweep, slab edge, fascia, gutter, roof soffit or 3D model text proxy after the fixed system family (Wall Sweep, Slab Edge, Fascia, Gutter, Compound Ceiling, Model Text) instead of the OST_ category name, and names such a proxy that has neither a type nor a name by its element id alone, with an empty object type.

  • A wall of several solids is classified IfcWallStandardCase (and gets its BaseQuantities) from its first solid alone: the other solids and the inactive faces of the node do not count, a curved face of another solid no longer makes the wall an IfcWall, and the depth of its extrusion, Height, areas and volumes are measured on the first solid, not on the union of the solids. A single-solid wall is unchanged and still counts its inactive faces. On the reference samples 10 walls (9 in one residential sample, 1 in a facade sample) move from IfcWall to IfcWallStandardCase; no other wall changes class.

  • Current profile: the GlobalId of an IfcRelAssociatesClassification is derived from the first character of the classification name, the first character of the code and the GlobalId of the first related object, so a classification association keeps the same GlobalId when the code changes after its first character or when other objects are added behind the first one. An empty name or code contributes nothing, and an association without a related object uses an empty GlobalId. Where two associations would get the same GlobalId (a panel with its own code and the code of its curtain wall, written before it), the later one takes the whole code instead of its first character.

  • Current profile: the trimmed axis of a straight wall that has no face-history trim is its location line bounded by the extreme parameters (along the curve driver) of the vertices of the vertical end faces of its first solid, provided both ends of the solid are among those faces (at least two, reaching the lowest and the highest parameter of any vertex of the solid), else the whole location line stays; when no vertex lies above the origin parameter the upper end parameter of the line stands in. The Length quantity and the body of such a wall follow it, a wall with a face-history trim keeps its value, and arcs are unchanged.

  • Current profile: a ramp (category Ramps) is written as an IfcRamp with its own IfcTypeProduct and Pset_RampCommon (IsExternal, Reference). A system ramp whose geometry is one body (a solid of more than 8 faces, a curved or open solid) or that hosts a railing is an IfcRamp without a representation that aggregates a second IfcRamp holding the body; the property sets, the type and the storey containment stay with the first, the material association goes to the second. A system ramp made of several closed solids of at most 8 faces, each with an upward-facing face, is an IfcRamp without a representation that aggregates one IfcRampFlight per inclined solid and one IfcSlab (LANDING) per horizontal one, each with its own material association; a single such solid, and a ramp placed from a loaded family, is one IfcRamp with the body. The ramps of the sample projects were written as proxies or left out before (Refs #34).

  • The built-in name table (revit_ui_names.tsv) is now keyed by the library's own SystemFamilyKind / RevitUiText member names (columns: culture, key, text; every text is unchanged), and the reader no longer opens any file of an installed host application. RvtReaderOptions.RevitLocalizationDirectory is marked [Obsolete] and the --revit-localization option is deprecated: both are ignored (the CLI prints "ignored: texts are built in") because the texts are built in. The version gate (2022 and later) and the locale gate (RUS and ENU only) are unchanged. The profile names inside the embedded unit catalogs are neutral (Revit2022-unit-catalog, Revit2022-unit-details), their data are unchanged, and no export output changes. Tests need no installed host application and no external resource file.

  • Current profile: reads spiral stair runs (Refs #13). A stair with a spiral run now has the shape SPIRAL_STAIR, the flight type of a spiral run is SPIRAL, and the flight gets the NumberOfRiser, NumberOfTreads, RiserHeight and TreadLength attributes and the Pset_StairFlightCommon properties of any other run, as the reference exports of the spiral stairs of two sample projects have them (before: NOTDEFINED, empty attributes, no flight properties). Winder runs are still not read, as no reference file holds one.

  • Current profile: the opening of a door or window in a straight wall whose family cuts with a profile (the family symbol stores cut loops) reaches through the whole wall also where it is not rebuilt from a cut loop and keeps the box of the family cutter: the side of that box across the wall is set to the total layer width of the host wall type and centred on the axis of the wall, and its Depth quantity follows. An opening of a family without cut loops keeps the box of its own void body and the Depth of that box, which can be deeper than the wall, as before this change. The width of a wall is now taken from its layers before the extent of its body, so a banded wall no longer gives the narrower body width. Unfilled wall openings are not changed (Refs #6).

  • Reads the layers of a type whose compound structure holds a Membrane or a StructuralDeck layer, and names the layer functions as the Revit API does (Refs #31, Refs #16). A layer array that holds the stored function 100 (Membrane) or 200 (StructuralDeck) was rejected as a whole, so the type had no Layers in the full JSON; 140 of 199 layer-bearing sample files hold such arrays. The stored values 0 to 5, 100 and 200 are accepted, any other value still rejects the array. Layers[].Function in the full JSON is now None, Structure, Substrate, Insulation, Finish1, Finish2, Membrane or StructuralDeck (before: Unknown, Structure, Substrate, ThermalAir, Finish, Membrane for 0 to 5, so the stored 5 is now Finish2). The IFC layer sets built from these layers skip a layer of zero thickness, as the native path already did, so a Membrane layer of zero thickness adds no IfcMaterialLayer. A wall, floor, ceiling or roof whose own record names its type takes the layers of that type only, no longer those of another type among its references, so a curtain wall that refers to the basic wall type of a panel stays an IfcCurtainWall. In the IFC export a wall whose type holds such a layer is no longer an IfcCurtainWall: it is an IfcWallStandardCase with its IfcWallType, layer width, Pset_WallCommon, BaseQuantities and openings, as the reference exports mostly have it (some of those walls are an IfcWall without quantities there).

  • Current profile: OverallHeight and OverallWidth of a door or window in a straight wall are now the vertical and the along-the-wall extent of the largest cut loop of the instance, in millimetres and unrounded, so the attributes follow the opening that is cut (a family with clearance around the frame gets the larger size) (Refs #84); the nominal Height and Width stay where no opening is rewritten (no wall host, curtain wall, curved wall, no decoded cut loop).

  • Writes ports, port relations and MEP systems for projects saved by Revit 2020, 2021, 2023 and later, as for 2022 (Refs #33). A connector gets a port only in mode 1 and 2; a connector without a stored direction (a fitting) takes the opposite of the direction of the connector it references when that one belongs to a system and is an inlet or an outlet, unless it belongs to the same system as the referenced connector, in which case it stays a bidirectional port (.SOURCEANDSINK.); two hosts joined at both ends get one connection. Electrical systems, circuits and devices connected by wires are still not written (#33 stays open). Projects saved by Revit 2022, which already had ports, can change too: the direction of a connector that belongs to another system than the connector it references is inferred as above, so part of the Port_ ports become InPort_ or OutPort_ ports, their GlobalIds change (the seed holds the direction) and the number of IfcRelConnectsPorts changes. Side effect: the field walk now also passes an extensible storage entity that holds no values, so more reinforcing bars are read (the 2024 DACH sample gains 33 IfcReinforcingBar, all equal to the reference by GlobalId). Bars of the RebarInSystem kind (area reinforcement) behind such an entity are not written yet.

  • Writes the BaseQuantities set of a door or window that holds no height or width parameter and no solid frame (typically a storefront door; the rule fires for any other door or window without those parameters too): Height is the extent of the stored box of the instance, Width the extent of the box of its family symbol along the family width axis, and Area their product (Closes #87).

  • Writes the NumberOfRisers and RiserHeight attributes of a stair flight from the flight itself (Closes #85): the flight's own number of risers, also where the stair's stored count differs, and the flight's elevation range divided by its riser index span, no longer the stair's nominal riser height.

  • Current profile: writes the IfcGroup of every model group in a file saved before Revit 2022 (the localized group label is not needed for the name "Model Group"); the 2021 rac advanced sample gains its model groups, files from 2022 on are unchanged.

  • Adds rvtio downgrade <source> --target-profile <file> --check [--guid-rule exact|minimal] [--report report.json] (experimental), which plans, and does not perform, the edit of the class table (Formats/Latest) of a newer project so that an older release could load it. The profile is the class table of a project or family saved by the older release, read from the file given on the command line. The plan lowers every class version above the profile's to it and makes the upgrade-identifier list of each class both tables define the profile's list (exact, the default) or removes only the identifiers the profile does not list (minimal); classes are matched by name. The summary and the JSON report list the classes only in the source with the elements the file holds in them, the classes only in the profile, the classes with a higher version, per class the stored fields that exist in one table only, and the changes of the identifier lists. The edit is applied to the stream in memory to check that it applies; nothing is written but the report. A source newer than release 2026, a profile not older than the source, a source whose saving release differs from its class table and a duplicated class name are refused with exit code 5; an unreadable file or a damaged partition stream exits with 2, an empty or unusable path with 1. On the Revit 2026 and Revit 2025 sample projects it finds 4584 common classes, 106 classes only in 2026 (64 elements of the 2026 sample in 3 of them), 16 only in 2025, 67 higher versions, 69 classes with different fields and 10 changed identifier lists (94 identifiers removed and 14 added by exact, none added by minimal). No application opened a converted file as part of this change. The export commands are unchanged.

  • Writes the Depth of the opening of a door or window whose family cuts with a profile as the total layer width of its host wall type (a family without cut loops keeps the depth of its void box), and the Depth of a wall opening as the extrusion length of its solid, without the 0.2 mm that was added to both before (a wall of 0.202 gave 0.2022; the Depth is now 0.202). A filled opening whose depth equals the width of its straight host wall is no longer measured in the frame of the wall when the host-axes measure of the door or window does not apply to it: its contour is measured in the model frame, as an opening of a wall and a door without cut loops already were. --ifc-opening-legacy-axes keeps the earlier selection and the earlier Width and Height of every opening: an opening that was written without the 0.2 mm is still measured in the frame of the wall, and only its Depth loses the 0.2 mm. The Depth change applies to the current and the legacy profile.

  • Measures the height of a wall by the Z range of its stored solids, without needing the profile helper or the cross-section attribute of the wall. The Height quantity, the length of the extrusion of a wall written by the body exporter and the depth of the extrusion of a simple rectangular wall now come from the Z extent of the union of the closed stored solids (several solids, a wall without a profile helper and a wall whose cross-section attribute is missing or not vertical are measured too); a wall that is not simple rectangular is measured when it has a single solid. A wall with an open or empty solid, a face outside the solids, a face in two solids, or several solids with a first solid that is not a simple rectangular wall keeps the written extrusion depth, as before. The IFC output changes only for the walls whose height was taken from the height parameter or from the depth of the written body before.

  • Default output, all IFC profiles and the JSON output (Closes #98): keeps only the current copy of an element that several partition streams hold. RvtReaderOptions.ResolveCurrentCopies is now true by default; the new --keep-all-copies command line option (or ResolveCurrentCopies = false) restores the earlier reading of every copy, and --resolve-current-copies is still accepted and does nothing. The output changes for every file saved in several increments. Of the 23 samples measured with it, it changed the 2021 rac advanced and rac basic sample projects and one 2023 rac advanced build, and left the other 20 as they were (IFC apart from the FILE_NAME timestamp, compact and full JSON byte for byte); a file with one partition stream is unchanged. Against the reference export, 2267 IFC quantity values, 30 layer sets, 365 type bindings and about 6300 spatial assignments of these three files move to the reference value and none of their values moves away; one slab perimeter (2021 rac advanced) ends 15 mm farther from a reference value that it misses by 18.6 m either way. In files with shared work saved in several increments part of the values moves away from the reference, and 3 wall openings of one such file are left without an IfcRelVoidsElement (see Known issues). The measurement covered the current profile; the option is not tied to a profile, so the legacy profile gets the same default. The full JSON gains placeholder records without a category and the compact JSON gains records that hold a name only. One repeated element of the 2021 rac basic sample stays unresolved and keeps all its copies. A file whose increment tables cannot be decoded (for example a damaged one) keeps every copy, as before, and the export report does not flag it. A file with several partition streams is read once more for the census (0.2 to 1.2 s on files of 15 to 20 MB).

  • Fixes the stale layer data behind #74 for files saved in several increments: the wall types of the 2021 rac advanced sample get the layer totals 303, 290 and 138.5 mm of the reference, and a wall no longer takes the width of its superseded copy (Refs #98).

  • Fixes the raw deflate path of the reader, which dropped the end of a stream longer than 64 KiB (Closes #92). The read loops stopped as soon as the final block was decoded, while up to a window of decoded bytes was still waiting in the output window; for zlib levels 1 to 9, .NET Fastest, Optimal and SmallestSize, stored and fixed Huffman blocks the output was cut at a multiple of 65,536 bytes and reported as complete, so a partition member with a page segment that another tool had recompressed gave a partial export with exit code 0 and a much smaller IFC. A stream that is really cut is still rejected. Files as Revit saved them are read as before.

  • Measures the opening of a door or window in a straight wall by its decoded cut loop (Width along the wall, Height vertical) instead of the nominal size of the door or window when the cut is larger than the nominal size, as Revit cuts it; the nominal size stays for an opening whose cut loop is not decoded (for the sample arch25 this changes the Width and Height of 124 openings, which now agree with the reference export). Closes #72.

  • Gives a stair with straight and sketched runs the shape of its straight runs in the current profile (Closes #70). A sketched run (flight type FREEFORM) is no flight of the shape rule there, so a straight run, a landing and a sketched run turned back is STRAIGHT_RUN_STAIR, as in the reference export, instead of undefined; a stair of sketched runs alone stays undefined. The older profile and files without such stairs are unchanged.

  • Reads a family instance as unambiguous when a file saved in several increments holds a second copy of its root block that reads the same: same class, type, original symbol, placement and host (any other difference in those fields still makes it ambiguous, as before). Before, the second copy left a door or window of such a file without its original symbol and cut loops, so the opening of a Revit 2021 door or window kept the thickness of the wall plus 0.2 mm as Depth (0.2022 for a wall of 0.202) instead of the thickness (Refs #74). All 7 such openings of the 2021 rac basic sample project and 53 of the 77 of the 2021 rac advanced sample project now get the Depth of the wall, as the 2023 builds and the reference do; 24 belong to instances whose two copies really differ, which only the resolution of the current copies fixes (with --keep-all-copies they stay as they were). The door and window styles of these instances are grouped by their original symbol as in the other releases (one more window style in the rac basic 2021 sample, 5 more door styles in the rac advanced 2021 sample). Of the 21 samples compared, the IFC of the other 19 and the compact and full JSON of all 21 are unchanged. The opening of a door or window in a straight wall is rebuilt from the width parameter of its host wall, not from the width of its layers or body, when the type thickness of the wall is not confirmed (its type root and compound structure are not read once) and the two disagree by more than 0.1 mm; such a wall holds a stale copy (counted as wall-width-conflict in the diagnostics). Before this check the one such opening of the 2021 rac advanced sample took the stale 138.5 mm as Depth instead of the 303.0 mm of the reference.

  • Default output: a wall sweep of the reveal kind (category Reveals) is no longer written as a product, whatever the category map holds; the other wall sweeps (category Wall Sweeps) stay IfcBuildingElementProxy with their geometry, named Family:Type:ElementId (Refs #58).

  • Current profile: quantities and property reals (spans, typed area and volume parameters, real parameters) are written with all 17 significant digits of the double, as in the reference (2699.9999999999627), with a three-digit signed exponent (1.0000000000000001E-005), a point after an integer value (100.) and -0. for negative zero. New StepWriter.RealExact; the legacy profile keeps its 15-digit reals. In the default millimetre output a length quantity is scaled as a double and printed again (4700., not 4700.0000000000002).

Known issues

  • Files with shared work saved in several increments (ResolveCurrentCopies, now the default): for part of the elements the single copy that remains is read differently from the reference export, in the type reference, the containment in a room, the topology of the wall openings and the wall quantities. In the corpus checked about 2150 values move away from the reference, while the same setting moves many more to it (see the entry of the setting above); in one file 18 IfcSpace (and their types) are not written, and in another 3 wall openings are left without an IfcRelVoidsElement, which the inverse of IFC2X3 forbids. --keep-all-copies restores the earlier reading.
  • Containment in an IfcSpace when the room phase is read (the room phases entry above): in two samples, the Revit 2023 and Revit 2025 Japanese architectural samples, 12 elements per file (4 building element proxies and 8 furnishing elements) are contained in an IfcSpace where the reference export keeps them in the storey. No property of the data that separates them from the cases that are right was found.