OpenEik.org

Draft OpenEik 0.1.0-draft: draft, not final. From commit c6463c72 of 30 October 2026. Errata: the errata of 0.1. Raw text: /spec/0.1/bridges.md

On this page

OpenEik 0.1 specification

Part VIII. Bridges

This part is informative in 0.1, except where it says MUST or SHOULD.

OpenEik is a source format. IFC, glTF, DXF and PDF are delivery formats that a compiler produces from it, as a binary is produced from source code. A bridge is an exporter, or an importer, between OpenEik and one of them: section 1 prints IFC, and section 2, proposed for 0.2, reads it once to start a project.

An export is a print. Like a drawing printed on paper, it is a copy of the version it was made from: it is never the model's record, and two exports are never compared to find what changed, since the versions they came from are (Part VI, 4). Part XVIII, 7, proposed for 0.2 from plan 688, says so as rules, and says how an IFC names the version it was exported from, says to people that it is a print and who printed it, carries a short form of its fidelity report, and why its bytes depend only on the version, the bridge and its options.

1. IFC export

  1. A bridge that exports IFC MUST export every element of the core categories that has a body, with its type, its level as a building storey, and its hosted openings as voids in their hosts.
  2. A bridge MUST publish, with every export, a fidelity report: the IFC schema written, the counts of products, openings and types, and what the export leaves out or approximates.
  3. A bridge SHOULD give every product a GlobalId derived from the project's id and the record's id, so that exporting the same model twice gives the same GlobalIds, and exporting a different project gives different ones.
  4. A bridge MUST export a generic element (Part II, 6.6) with its declared bodies, as the IFC entity its generic.ifcClass names, else as IfcBuildingElementProxy. Where its IFC schema has no such entity, it exports IfcBuildingElementProxy and says so in its fidelity report.
  5. A bridge SHOULD print the property sets an element or a type was imported with (ifcSource.properties, Part II, 6) with the object it prints for that record: a set IFC4 defines, and that applies to the object, under its name, each property its template defines typed as the template types it; every other property in a set whose name does not start with Pset_. Section 1.5 says how.
  6. A bridge SHOULD write, on each IfcRailing it writes for a railing (Part II, 12.2), IFC4's standard property Pset_RailingCommon.Height: the railing's height, its type's railing.height, to which its top rail carries it (Part IV, 11.2, rule 5), as an IfcPositiveLengthMeasure in the units of its print. IFC4's template applies the set to an IfcRailing and its type, and types Height so. Where the bridge prints the sets the railing was imported with (rule 5), an imported Pset_RailingCommon joins this one, and its Height keeps this value (section 1.5, rule 5).
  7. A bridge MAY take the property sets an application wants on the objects it prints, data the model does not hold, and write each with the object the export writes for its record (an element's on its product, a room's on its space, and a type's as the own sets of each IFC type it writes for it), each value untyped, by what it is: a text an IfcLabel (an IfcText where it is longer than 255 characters), a number an IfcReal and a truth value an IfcBoolean. It MUST NOT write such a set under a name that starts with Pset in any case of its letters or with Qto_, which IFC reserves to the property and quantity sets it defines, or with OpenEik_ or Imported_, which this part reserves to a bridge's own sets and to imported ones (section 1.5, rule 4); nor under the name of a set it writes itself on that object, nor merge it into one. It leaves such a set out, and its fidelity report says so (rule 2). An application SHOULD name the sets it gives with a prefix of its own, as the reference implementation's are Stomra_IdentityAndParameters, Stomra_Railing, Stomra_Framing and Stomra_Electrical, so that they keep clear of IFC's, of the bridge's and of every other application's.

Why the project's id: it is part of the model (Part II, 2) and stable for the life of the project, as every record's id is (Part I, 6). Every version of the project, every alternative and every fork or copy of its folder carries the same project id, so an element keeps its GlobalId across them, and a model checked in IFC today can be compared with one exported a year later or from another line of work. A namespace taken from where the model is stored (a repository, a host) would change the GlobalIds when the model moves.

The reference bridge (ek ifc, exportIfc in src/core/io/ifcExport.ts):

  • writes IFC4, with tessellated geometry, swept solids for profiled members, material layer sets for layered types, element types and property sets;
  • derives each GlobalId as the IFC base-64 encoding of the first 128 bits of the SHA-256 of <project id>:element:<element id> (<project id>:type:<IFC class>:<type id> for types, such as type:IFCWALL:type-wall-exterior-300; level:<level id> for a storey; opening:<door or window id> for an opening), or keeps a GlobalId the element was imported with when asked to;
  • returns a report with the schema, scope, counts (products, openings, types, polygons, swept solids, spaces), units and a list of warnings that say what is approximated.

For examples/one-storey-house the reference bridge reports IFC4, 10 products, 3 openings and 6 types.

A componentIFC4 class, and its type'sPredefined type
a plate: its type's component.family is plateIfcPlate; IfcPlateType, NOTDEFINEDNOTDEFINED
a bolt group: it has bolts, and its type's family is bolt and holds a bolt (component.bolt)IfcMechanicalFastener, its NominalDiameter the bolt's diameter and its NominalLength the group's length where it gives one; IfcMechanicalFastenerType, NOTDEFINEDby the bolt's kind and nuts: an anchor, and a rod with one nut, ANCHORBOLT; a rod with none, DOWEL; a bolt (or no kind), and a rod with two nuts (or none said), BOLT
a weld: it has weld, and its type's family is weldIfcFastener; IfcFastenerType, NOTDEFINEDWELD
any other: a bracket, a hanger, a timber section, a cast-in item, a void form, an edge detail, and a bolt or a weld type's element without bolts or weldIfcBuildingElementProxy; IfcBuildingElementProxyType, NOTDEFINEDNOTDEFINED

The class is part of a type's GlobalId (type:<IFC class>:<type id>, rule 3), so a type whose elements take two classes is two IFC types. Where a bolt group gives no length, the reference works out the shortest bolt that fits from a table of ISO bolts that no record holds (open questions, 73), and ifcOfModel of @openeik/tools leaves NominalLength unset. With the proposal components (Part IV, 7.6, proposed for 0.2), a component's body is the one its frame's rules derive, and ifcOfModel writes it so, and writes a body an application gives over it where the compiler says part of it is reference behaviour, as a bolt's head, washers and nut are.

1.1 Heating, ventilation and plumbing

How a bridge can export the systems, the insulation, the heat and the components of Part II, 6.9 to IFC4, as the reference bridge does.

Systems. One IfcDistributionSystem for each system and number the model's runs use, named by the system's code and number as the project's drawings write it (VS20), its long name the system's name, and its predefined type by the system:

SystemIfcDistributionSystemEnum
coldWaterDOMESTICCOLDWATER
hotWater, hotWaterCirculationDOMESTICHOTWATER
sewageDRAINAGE
stormSTORMWATER
greaseWaste, oilWasteWASTEWATER
heating, districtHeatingHEATING
brineCHILLEDWATER
compressedAirCOMPRESSEDAIR
gasGAS
supplyAir, outdoorAirVENTILATION
extractAir, exhaustAir, kitchenExhaustEXHAUST

Its runs and terminals are assigned to it by IfcRelAssignsToGroup: a run by its system and number; a component in a pipe or a duct by its run's; another terminal by the system and number of the runs joined to it, else, for a heating terminal of a project with one heating system, by that system's number, else by its own system under the number 1. A port's flow direction is SOURCEANDSINK on a heating or a cooling system.

Components. A component exports as the class and predefined type of its kind:

KindIFC4: class, predefined type
isolatingValveIfcValve, ISOLATING
checkValveIfcValve, CHECK
balancingValveIfcValve, DOUBLEREGULATING
controlValveIfcValve, REGULATING
safetyValveIfcValve, PRESSURERELIEF
backflowPreventerIfcValve, DOUBLECHECK
airVentIfcValve, AIRRELEASE
pumpIfcPump, CIRCULATOR
strainerIfcFilter, STRAINER
energyMeterIfcFlowMeter, ENERGYMETER
expansionVesselIfcTank, EXPANSION
damperIfcDamper, CONTROLDAMPER
fireDamperIfcDamper, FIREDAMPER
smokeDamperIfcDamper, SMOKEDAMPER
flowRegulatorIfcDamper, BALANCINGDAMPER
silencerIfcDuctSilencer: ROUND in a round duct, RECTANGULAR in a rectangular one
ductFanIfcFan, USERDEFINED, with the ObjectType INLINE: IFC4's IfcFanTypeEnum has no inline fan

A unit heater exports as IfcSpaceHeater, CONVECTOR. In IFC a component in a pipe or a duct sits between two segments of its run, connected port to port (IfcRelConnectsPorts); a bridge MAY split the run at its components to write it so.

Insulation and heat. IFC holds a run's insulation as an IfcCovering of the predefined type INSULATION, related by IfcRelCoversBldgElements, or as properties. A code has no standard property: a bridge writes the code, its meaning where it knows it, and the thickness in a property set of its own. A space heater's power is Pset_SpaceHeaterTypeCommon.OutputCapacity; a terminal's design difference, kv and presetting go in a property set of the bridge's.

The reference bridge writes an IfcDistributionSystem per system and number (writeMepSystems), each run's insulation in a property set of its own (Pset_StomraInsulation: the code, its meaning and its thickness) and each terminal's heat in another (Pset_StomraHeat: its power, its flow in l/h, its design difference, kv and presetting), and the classes above (terminalIfc; a silencer's predefined type by its duct, mepIfc). It does not split a run at its components: one IfcPipeSegment or IfcDuctSegment runs through them.

Reference: writeMepSystems, ifcSystemType and portDirection in src/core/io/ifcMepSystems.ts; terminalIfc and mepIfc in src/core/elements/mep.ts.

Sources. buildingSMART's documentation of IFC4 ADD2 TC1: IfcDistributionSystem, IfcValve, IfcPump, IfcFilter, IfcFlowMeter, IfcTank, IfcDamper, IfcDuctSilencer, IfcFan, IfcSpaceHeater and IfcCovering.

1.2 How a door opens and a window is divided

By the steward's decisions of 26 and 27 October 2026 (open questions, 71), from the operation Part II, 6.5 gives a door and a window. A bridge that writes IFC4 SHOULD write a door's OperationType (IfcDoorTypeOperationEnum) and a window's PartitioningType (IfcWindowTypePartitioningEnum) so: on its IfcDoorType or IfcWindowType where it has a type, leaving the IfcDoor's or IfcWindow's own unset, as buildingSMART's normative rules BLT001 and BLT002 require; and on the IfcDoor or IfcWindow itself where it has none. Where the doors or windows of one type record open or are divided in different ways, it SHOULD write an IFC type for each way, so that the occurrences of every IFC type share theirs. Left and right are as IFC4 sees them, looking the way the leaf opens ("as viewed in the direction of the positive y-axis"): towards the side Part II, 6.5, rule 2 names. Looking towards the wall's exterior, the opening's start is on the left; towards its interior, on the right.

A door whose type's openingFamily isOperationType
of the kind door, one leaf, swinging (operation swing or none)SINGLE_SWING_LEFT where its hinge side (Part II, 6.5, rule 1) is on the left, else SINGLE_SWING_RIGHT
of the kind door or garageSideHinged, two leaves (lightsX, or panels), swingingDOUBLE_DOOR_SINGLE_SWING
of the kind door, slide, or pocket with one leafSLIDING_TO_LEFT where its hinge side, the way it slides (rule 4), is on the left, else SLIDING_TO_RIGHT
of the kind door, pocket with two leaves or moreDOUBLE_DOOR_SLIDING
of the kind garageSideHinged, one panelas a door of one leaf that swings
of the kind garageRollerROLLINGUP
of the kind garageFoldingDOUBLE_DOOR_FOLDING
of the kind garageSectionalUSERDEFINED, its user-defined type SECTIONAL (IFC4 has none)
not operable; hinged at its top or bottom (Part II, 6.5, rule 6); swinging with three leaves or more; or no openingFamilyNOTDEFINED

A window's partitioning is its grid of lights: across, an openingFamily's lightsX, or one more than its mullions.x, or a windowFamily's panes; up, lightsY, or one more than its mullions.z, or one more than its transoms. One light is SINGLE_PANEL; two or three side by side DOUBLE_PANEL_VERTICAL or TRIPLE_PANEL_VERTICAL; two or three one above the other DOUBLE_PANEL_HORIZONTAL or TRIPLE_PANEL_HORIZONTAL; any other grid USERDEFINED, its user-defined type <across> x <up>. A window whose type has neither family is NOTDEFINED. A type that a bridge does not split so writes the operation or partitioning all its occurrences share, else NOTDEFINED.

A door 900 wide with one leaf hinged left, swinging inward, on a wall that runs east: its hinges are at its west end, and it opens into the building, to the south; looking south, west is on the right, so it is SINGLE_SWING_RIGHT. With handFlipped, or swinging outward, it is SINGLE_SWING_LEFT. Sliding instead (operation: "slide"), it is SLIDING_TO_RIGHT.

Reference: doorOperation and windowPartitioning in packages/openeik/src/ifc-ports.js of @openeik/tools; the reference bridge's doorOperation in src/core/io/ifcExport.ts.

1.3 Shafts' openings

By the steward's decision of 26 October 2026 (open questions, 77). A bridge that writes IFC MUST write, in each host a shaft takes something out of (Part IV, 4.2, rule 8) and that the export writes, an IfcOpeningElement voiding it (IfcRelVoidsElement): of the predefined type OPENING where the shaft takes all of the host within its outline, and RECESS where it ends inside it, a foundation included. Its body is the shaft's outline extruded over the heights it takes out of the host. The host's own body has the shaft's prism cut already, as a wall's has its doors' and windows' openings (rule 1).

The reference bridge keys an opening shaft:<shaft id>:<host id> and its relation void:shaft:<shaft id>:<host id> for the GlobalIds of rule 3, names it by the shaft, and extrudes the outline from the higher of the shaft's base and a millimetre under the host's lowest point to the lower of its top and a millimetre over the host's highest point. ifcOfModel writes it so.

1.4 Where a member's profile sits (informative)

By the steward's decision of 26 October 2026 (open questions, 78), an informative mapping in 0.1, as section 1.1's is: it asks no new body. A bridge that writes a member's profile, a beam's or a column's section as an IfcMaterialProfileSet on its type, writes on the member:

  • an 'Axis' representation along its reference line (Part IV, 6.5), the line through the middle of its section, from its start to its end; and
  • an IfcMaterialProfileSetUsage of its type's set with the cardinal point 5, the middle of the profile's bounding box, which IFC4 places on the member's axis line. A beam's roll and a column's turn turn the profile about that line (Part IV, 6.2, rule 5, and 6.1, rule 3), and a tapered beam's end section is an IfcMaterialProfileSetUsageTapering.

IFC4 places a parameterised profile by the middle of its bounding box, as the reference line runs through it. A roll turns the section about the reference line, so only the cardinal point 5 keeps a rolled beam in place: with 2, the bottom middle, the profile would turn about the beam's underside. An analysis export writes the member's axis instead, through its centroid (Part IV, 6.5), with the cardinal point 10. A beam built from a framing family has no single profile (Part IV, 6.5), and gets no usage.

A rounded rectangle and an outline. By the steward's decision of 27 October 2026 (open questions, 87), a bridge writes a rectangle with a radius (Part II, 5.2, rule 7) as an IfcRoundedRectangleProfileDef, its XDim the section's width, its YDim its depth and its RoundingRadius its radius, which is at most half the smaller of the two, as IFC4's rule ValidRadius asks too (a larger radius held from before is written as that half, as it is drawn); and one without a radius, or with 0, as an IfcRectangleProfileDef. It writes a custom section (rules 8 to 10) as an IfcArbitraryClosedProfileDef, or as an IfcArbitraryProfileDefWithVoids where it has voids, named by its designation, each contour an IfcIndexedPolyCurve over an IfcCartesianPointList2D of its own:

  • each run of straight sides is an IfcLineIndex of its points, and each arc an IfcArcIndex of three: its start, a point on it and its end. A through entry's arc passes through that entry's point; a rounded corner's arc starts and ends where it meets its two sides, and passes through its own middle;
  • the last segment ends on the first point, and no two points of a contour are one: where two rounded corners take the whole of the side between them, their arcs meet at one point;
  • the points are the outline's less the middle of its bounding box, so that the profile's origin is its middle, as a parameterised profile's is, and the cardinal point 5 sets it on the reference line;
  • the outline runs counter-clockwise and each void clockwise, a contour written the other way round reversed, and each starts at its first corner, or, where that corner is rounded, where its arc meets the side towards the entry before it.

Nothing of the section is lost: its corners and its arcs are IFC4's own, and only the origin of its points and the way round they were written, which place nothing (Part II, 5.2, rules 8 and 10), are not kept.

1.5 Imported property sets

By the steward's decision of 28 October 2026 (open questions, 88). An element or a type imported from IFC carries the property sets it was imported with in ifcSource.properties (Part II, 6): by set and by property, each value a text, a number or a truth value. A bridge that prints them (section 1, rule 5) writes them so:

  1. Where. An element's with the object it was imported as, where the export writes one: the opening of a door or a window imported as an IfcOpeningElement; otherwise with the object the export writes for the element, a room's space included. A type's with each IFC type the export writes for it, as that type's own sets (HasPropertySets), since IFC4 relates no property set to a type by an IfcRelDefinesByProperties.
  2. A set IFC4 defines. A set whose name starts with Pset_, that IFC4's property set templates define, and whose template applies to the object, is written under its name. A template applies as buildingSMART's validation service holds it (its rule PSE001): to an occurrence of a class the template names or of a subclass of one, and to a type of such a class's type; where the template names a predefined type with the class, only to an object of that predefined type, its own, else its type's (for USERDEFINED, its object type or its element type); a set driven by types only to no occurrence, and a set driven by occurrences or by performance to no type.
  3. Typed as the template types it. In such a set, each property the template defines is written as the template has it: a single value of the measure or the defined type the template names; an enumerated value of the enumeration it names, the value one of its values; a bounded value whose set point is the value; a list value of the one value. A text is typed only where the type is a text and the text fits it (an IfcLabel or an IfcIdentifier of at most 255 characters; an IfcDate, an IfcDateTime, an IfcTime or an IfcDuration in its ISO 8601 form), a truth value only as an IfcBoolean or an IfcLogical, and a number only as a number within its type's bounds (an IfcPositiveLengthMeasure more than 0, an IfcNormalisedRatioMeasure from 0 to 1, an IfcInteger whole). Where the record's ifcSource.units is "specification" (Part II, 6), it holds a number in this specification's units, a length in millimetres and an angle in degrees (Part I, 3), any other measure in its SI unit; the bridge writes it in the units of its print, and declares the unit of each measure it writes (IfcUnitAssignment). Where units is absent, the record does not say which units its numbers are in: a number the template types as a measure with a unit, such as a length, an angle, an area, a volume or a thermal transmittance, cannot be typed, and rule 4 writes it, as it was imported; a number of a measure without a unit, such as a count, a ratio, an IfcInteger or an IfcReal, is typed as this rule says. No bridge converts it.
  4. Everything else, under a name without Pset_. A set IFC4 does not define, or whose template does not apply to the object; a property the set's template does not define; and a value that cannot be typed as rule 3 says (a text where the template names a measure, a number of a measure with a unit where the record does not say its units (rule 3), a value its enumeration does not hold, a property the template makes a reference, a table or a complex property, which one value is not, or one whose type the templates do not give) are written in a set whose name does not start with Pset_. Its name is the set's name without Pset_, and a set not named Pset_ keeps its name, unless that name is empty, starts with Pset in any case of its letters, starts with OpenEik_ (the bridge's own sets) or Imported_, is the name of a set the bridge writes itself on the object, or is the name another of the record's sets takes by this rule; then its name is Imported_ followed by the set's whole name. In it, a text is an IfcLabel (an IfcText where it is longer than 255 characters), a number an IfcReal and a truth value an IfcBoolean.
  5. The bridge's own sets. Where the bridge writes a set of the same name on the object itself (the sets of section 1.1 and of Part XIII, 10, a space's Pset_SpaceCommon, a railing's Pset_RailingCommon of section 1, rule 6), the imported properties join it, and a property the bridge writes itself keeps its value: the imported one is left out.
  6. The report. The fidelity report (section 1, rule 2) says which imported properties are written outside their set, and why, and which are left out.

Why: an IFC file from an authoring tool holds sets and properties IFC4 does not define, such as a set Pset_QuantityTakeOff, or a property TotalArea in Pset_RoofCommon, and values of other types than IFC4's templates give. buildingSMART's rule PSE001 refuses a set named Pset_ that IFC4 does not define, a property its template lacks and a value of another type; IFC reserves the prefix to the sets it defines, and its rule PSE002 warns of any other name that starts with Pset in any case. Printed as it was imported, such a record fails that rule; left out, its properties are lost. The name without Pset_ keeps them readable, and Imported_ keeps two sets apart and the bridge's own names its own.

1.6 A framing family's parts (proposed for 0.2)

Proposed for 0.2, with the proposal framing of Part IV, 6.6, and informative in 0.1. A bridge writes these only when it is asked for the proposal.

A beam or a slab edge element whose type is a framing family has the bodies of its family's parts (Part IV, 6.6). A bridge asked for the proposal SHOULD write them so:

  1. A family of profiles. Where every part that has a piece is a profile part, its parts are the element's own body: each part an IfcShapeAspect of its shape, named by the part's id, and a constituent of its IfcMaterialConstituentSet, as a door's parts are (Part IV, 4.3.1).
  2. A family with members. Where a member part or a plate has a piece, each part that has one is an element of its own (Part IV, 6.6, rule 13), aggregated under the element's object by an IfcRelAggregates, placed in the element's placement, and with its body and its material: its type's material, and a nail plate's steel. The element's object has no body of its own: its parts carry it. Each part's GlobalId is derived from framing:<element id>:<part id>, and its Tag is its key; where two elements' ids and their parts' ids make one key, as an element a with a part b:c and an element a:b with a part c, a bridge refuses to print the model, since their GlobalIds would be one; its IFC type is one for each class, predefined type, user type and section, keyed type:<class>:framing:<predefined type>:<user type>:<section> and named by its user type, else its predefined type, and its section (CHORD 45 × 220), as the reference bridge keys and names its framing members' types. The element keeps its own class, its type and its GlobalId.
  3. Its class. A part's IFC4 class and predefined type follow its role, as the reference implementation types a truss's members, and its name says its role in words, as the reference implementation names them (Part IV, 6.6, rule 13):
A partIFC4 classPredefined type, and its user typeIn words
a member of the role topChordIfcMemberCHORDtop chord
bottomChordIfcMemberCHORDbottom chord
webIfcMemberSTRUTweb
heelPostIfcMemberPOSTheel post
strutIfcMemberPOSTstrut
collarIfcMemberCOLLARcollar tie
rafterIfcMemberRAFTERrafter
purlinIfcMemberPURLINpurlin
headerIfcMemberUSERDEFINED, HEADERheader
ridgeBeamIfcBeamBEAM, RIDGEBEAMridge beam
postIfcColumnCOLUMNpost
plate, and a plate partIfcPlateUSERDEFINED, NAILPLATEnail plate
a profile part, in a family with membersIfcMemberMEMBER(its id alone)

Why parts and not shape aspects: an IfcShapeAspect names a part of one object's shape that has no identity of its own, as a door's lining and its panel, and carries no class, predefined type, type, material profile or quantities of its own. A truss's chords, posts and webs are each a piece of timber that is cut, listed, joined and analysed apart. The steward decided on 21 October 2026 that an analysis takes each member part of a family as a member with its own axis (open questions, 10), and on 26 October 2026 that each is written as an IfcMember or an IfcBeam with its own body (open questions, 83). IFC4 writes such a frame as a whole decomposed into its parts by IfcRelAggregates, as its IfcElementAssembly of the predefined type TRUSS is: each part is placed relative to its whole, as buildingSMART's rule OJP001 asks, and contained in no storey, its whole being; and a whole whose parts carry its body has none of its own, as an IfcStair whose flights carry it, so that nothing is counted twice. A family of profiles, a rafter cut to its seat, is one piece, the element itself, and stays one object. The element keeps the class its category gives it, an IfcBeam for a beam, so that its type and its GlobalId stay those it has without the proposal.

1.7 Where the plan stands on a map (proposed for 0.2)

Proposed for 0.2 with Part II, 2.2 (the proposal site), and informative in 0.1, which names no coordinate reference system: a bridge writes a map conversion only where its caller names a system.

By the steward's decision of 26 October 2026 (open questions, 79). A bridge that writes IFC, and reads a site whose crs names a horizontal system it projects (Part II, 2.2, rules 3 and 5), writes where the plan stands as an IfcMapConversion from its model's representation context to an IfcProjectedCRS:

  1. The system. The IfcProjectedCRS's Name is crs.horizontal (EPSG:3006), its VerticalDatum crs.vertical where the site gives it, and its MapUnit the system's unit of length.
  2. The place. The IfcMapConversion's Eastings and Northings are the site's latitude and longitude projected into the system, and its OrthogonalHeight the project's elevationOffset, each in the system's unit; its XAxisAbscissa and XAxisOrdinate are the direction of the plan's x axis in the grid, the plan turned by true north and by the grid's convergence at the site; and its Scale turns the model's millimetres into the system's unit, 0.001 for metres.
  3. What it cannot project. A bridge that does not project the system writes no map conversion, and says so in its fidelity report (rule 2). A site without crs places the plan on no map, unless the bridge's caller names a system.

1.8 Classification references (proposed for 0.2)

Proposed for 0.2 with Part II, 11 (the proposal classifications), and informative in 0.1, which defines no classification reference.

By the steward's decisions of 11 and 27 October 2026 (open questions, 4 and 86). A bridge that writes IFC writes the classification references of the records it prints objects for (Part II, 11, rule 6):

  1. The system. One IfcClassification for each system and edition the print's references name: its Name the system, its Edition the edition where the references give one.
  2. The reference. One IfcClassificationReference for each reference, the same system, edition, code and uri being one reference: its Identification the code, its Location the uri where it gives one, and its ReferencedSource its system's IfcClassification.
  3. What it classifies. An IfcRelAssociatesClassification relates each reference to every object of the print whose record carries it: an element's product, or the IfcSpace of a room; each IFC type a type is written as; the project's IfcProject; a level's IfcBuildingStorey.
  4. What it leaves out. The references of a record the print writes no object for are not printed, and the fidelity report says how many and of which records (rule 2): a view or a grid, say, a type no element of the print uses, and a material. IFC4 relates a classification to a material only by a relationship that no rooted entity references, which buildingSMART's rule IFC105 refuses, so a material's references stay in the model.

A relation's GlobalId is derived from the project's id, as rule 3 derives every GlobalId, and the key classification: followed by the canonical JSON (Part III, 6) of the list [system, edition, code, uri], null for an edition or a uri the reference does not give: classification:["NL-SfB","2005","21.21",null].

1.9 A member between two points, and a slab on a sloped plane (proposed for 0.2)

Proposed for 0.2 with Part II, 6.2.1 and 6.3.1 (the proposals member and slopedSlab), and informative in 0.1, which has neither. A bridge writes these only when it is asked for the proposal.

By the steward's decisions of 30 October 2026 (open questions, 110 and 111). A bridge that writes IFC writes:

  1. A member (Part II, 6.2.1) as an IfcMember of the predefined type NOTDEFINED, typed by an IfcMemberType, NOTDEFINED, as it writes a beam as an IfcBeam and a column as an IfcColumn: its body as Part IV, 6.7 builds it, its type's section an IfcMaterialProfileSet on its IfcMemberType as a beam's and a column's are (section 1, its notes), and its GlobalId and its type's derived as rule 3 of section 1 derives a beam's (<project id>:element:<id>, and type:IFCMEMBER:<type id>). Section 1.4's mapping places its profile on its reference line as it places a beam's, with the cardinal point 5. What it says of a brace, a strut or a tie, a reader takes from its type's name; its record says no more (Part II, 6.2.1, rule 7).
  2. A slab on a sloped plane (Part II, 6.3.1) as it writes a level slab, an IfcSlab of its category's predefined type with its type's IfcMaterialLayerSet, its body each layer's as Part IV, 4.2.1 builds it: the outline between two parallel planes, its sides plumb, written as a pitched roof's layers are. Its openings are a level slab's (section 1.3).

1.10 A provision for a void (proposed for 0.2)

Proposed for 0.2 with Part II, 6.10 (the proposal provision), and informative in 0.1, which has no category provision.

By the steward's decision of 30 October 2026 (open questions, section 4, D9). A bridge that writes IFC4 writes each provision (Part II, 6.10) it prints:

  1. The object. An IfcBuildingElementProxy of the predefined type PROVISIONFORVOID, in its level's storey, its body the provision's (Part II, 6.10, rule 2). Where it has a type, its IfcBuildingElementProxyType carries the predefined type, since IFC4 has a type carry what its occurrences share (buildingSMART's rule OJT001).
  2. Its properties. IFC4's Pset_BuildingElementProxyProvisionForVoid, which IFC4 applies to an IfcBuildingElementProxy of that predefined type: Shape, an IfcLabel, Rectangle or Round; Width and Height, its width and height, for a rectangle, or Diameter, its width, for a round one, and Depth, its provision.depth, each an IfcPositiveLengthMeasure; and System, an IfcLabel, the names of the runs its provision.forIds names, in that order, joined by , , where it names any of the model's.
  3. Nothing else. It voids no element: an IfcOpeningElement is written for what its host's records cut (section 1, rule 1), never for a provision. Its answer is not printed.

2. Reading IFC (proposed for 0.2)

Proposed for 0.2, and informative in 0.1: it adds no rule to 0.1, and no file of 0.1 is held to it. It is the proposal ifc-read, by the steward's decision of 29 October 2026 (open questions, 95).

A reader of IFC starts an OpenEik project from an IFC file: the bridge of section 1, the other way. It is deterministic, with no language model and no judgement of its own in it: the same file gives the same records and the same bodies. It reads a file once, to start a project. It is no live link to the file, and it tracks no revisions between deliveries of it: a later delivery read again is another start. What a record of Part II cannot say of an element without recognising it, its shape, the reader keeps as the file draws it, a body given beside the records; an assistant, or a person, reviews the project afterwards and upgrades it, rewriting each element it recognises in its category's own members (section 2.6).

This first version recognises no element. Every element's shape is a given body, and its record says so (section 2.3).

2.1 What a reader writes

  1. The project. Its id is the IfcProject's GlobalId read as section 2.4, rule 1 says; its name the project's Name, else its LongName; its units mm and its up axis Z; its elevationOffset the first IfcSite's RefElevation, where the file gives one. Its site is left out: in 0.1 a site gives its latitude, its longitude, true north and a time zone together (Part II, 2), and IFC gives no time zone.
  2. Levels. Each IfcBuildingStorey is a level, named by its Name, else its LongName, at the height its placement puts it at in the file's world, else at its Elevation. A file without a storey gives one level, level-0, at 0.
  3. Elements. Each element of the file, an IfcElement that is no feature element (an opening, a projection) and no virtual element, and each IfcSpace, is an element of the category the table below gives its class: the first row whose class it is, or is a subclass of, and whose predefined types, where the row names them, hold its own, else its type's. IfcBuildingElementProxy, and every class the table does not name, is generic (Part II, 6.6), and its type's generic names its class (ifcClass), what it stands for (the IFC type's ElementType, else its Name, else the class's name in words) and its discipline: systems for a distribution element, site for a geographic or a civil element, structure for a member, a plate, a pile, a fastener and reinforcement.
  4. Types. Each type of a product (an IfcTypeProduct, IFC2X3's door and window styles among them) is a type: of the category its elements are read as, else of the category its class's occurrences are; one type for each category, and for generic each class, where its elements are read as more than one. The elements of a category and a class that no type types get a type of the reader's own. A type's name is its Name, else its ElementType, else its class; its material as section 2.2, rule 3 says; its color its material's, else the first colour of its representation's or its first element's body, else #c8c8c8; its width and height its first element's (section 2.3, rule 2), else those of the box round its representation, else 1 mm.
  5. An element's level is the level of the storey that contains it (IfcRelContainedInSpatialStructure), directly, through a space or through the element it is a part of (IfcRelAggregates, IfcRelNests); else the highest level at or under its body's lowest point; else the lowest level.
  6. A door or a window is hosted (hostId) by the element whose opening it fills (IfcRelFillsElement and IfcRelVoidsElement), where that element is read as a wall. One that fills no wall's opening is generic, since Part II, 6.1, rule 5 hosts a door and a window by a wall.
  7. A room's room.number is its space's Name, else its LongName, else its id, and its room.name its LongName.
IFC4 classPredefined typeCategory
IfcSpaceroom
IfcWallwall
IfcSlabROOFroof
IfcSlabany otherslab
IfcRoofroof
IfcFootingfoundation
IfcCoveringCEILINGceiling
IfcBeambeam
IfcColumncolumn
IfcDoordoor, where it fills a wall's opening (rule 6)
IfcWindowwindow, likewise
IfcRailingrailing
IfcPipeSegmentany but GUTTERpipe
IfcDuctSegmentduct
IfcFurnishingElementfurniture
every other element, IfcBuildingElementProxy among themgeneric

The table is the bridge's classes (section 1) read the other way, for the categories whose records a reader can write from what a file says without recognising the element. A gutter, a stair, a component, a terminal, reinforcement and a slab edge element need members of their category's own, such as a gutter's roof edge or a stair's shape, which only recognising the element gives: until a rule does (section 2.6), they are generic, and their class and predefined type are kept (section 2.2, rule 1).

A reader leaves out, and its report says so: an opening, which OpenEik derives from what fills it (Part IV, 4.3), or which is in its host's body where nothing fills it; ports, systems, zones, grids and annotations; and the GlobalIds of the project, its site, its building and its storeys, which 0.1 holds for none of them.

2.2 What it keeps

  1. What each object was. An element and a type carry ifcSource (Part II, 6): the object's globalId; its class, entity, as IFC writes it (IfcWallStandardCase); and, proposed with this section, its predefinedType (an occurrence's own, unless that is unset or NOTDEFINED and its type's is neither, then its type's; a type's own) and, where that is USERDEFINED, its objectType (an occurrence's ObjectType, a type's ElementType).
  2. Its property sets, in ifcSource.properties, with units: "specification" (Part II, 6): each property set (IfcPropertySet) related to an occurrence, and each a type holds; each property's value a text, a number or a truth value: a single value's; an enumerated value's or a list value's one value; a bounded value's set point, else its one bound. A number is converted from the units of the file (its IfcUnitAssignment), or of the property where it gives its own: a length into millimetres, a plane angle into degrees, any other measure into its SI unit; an SI unit's prefix applied, squared for an area and cubed for a volume; a conversion-based unit by its factor (a foot, a degree, a square foot); a derived unit by its elements' factors raised to their exponents; a temperature in degrees Celsius into kelvins. A number of a measure without a unit (IfcReal, IfcInteger, IfcCountMeasure, a ratio) is kept as it is. Left out, and said so: a quantity set (IfcElementQuantity), which is derived data (Part II, 10); a set of the bridge's own (OpenEik_, section 1.5, rule 4); a value of several values, a range, a table, a reference or a complex property; and a value in a unit the reader does not convert.
  3. Materials, by name. A type's material is the name of the material the file associates with the type, else with its first element: a material's name; a layer set's, a profile set's or a constituent set's name, else its first material's; a usage's, its set's. Each such name of at most 80 characters is a material record (Part II, 4), of the colour the file gives the material, else #c8c8c8, and the pattern none; a longer one is its types' material text alone (Part II, 4, rule 2). A layer set's layers are not read.

2.3 Shapes

  1. A given body. A product's shape is a body given in the derived layer (Part IV, 1.1.1, the proposal derived), keyed elements/<id>, and never a record's own geometry: the element's record carries shape: "given", proposed with this section. A result names its element by its hash, and its type too where it places the type's representation, so that a reader uses it only while they hold (Part IV, 1.1, rule 2); its producer is the reader, and its rule ifc-read, else ifc-read, mesher (rule 5). A type's representation that its elements place is a result of its own, keyed types/<id>, and each of those elements' body mapped onto it.

  2. The members that place it. An element whose shape is given still carries the members its category's schema asks for. They place it, and say nothing of its shape: levelId, its level (section 2.1, rule 5); offset, the height of its placement's origin over that level; profile, for a column, a room, a piece of furniture, a door and a window, its placement's origin in plan; for a slab, a roof, a ceiling and a foundation, the corners of the box round its body in plan, counter-clockwise from its least x and y, at least 10 mm a side (a square of 1000 mm from its origin where it has no body); for every other category, its origin and the point 1000 mm from it along its placement's x axis in plan (along +x where that axis is upright); height, the height of the box round its body, and width, the shorter of its sides in plan, else the longer; a door's and a window's their OverallHeight and OverallWidth, where the file gives them; each at least 1 mm and at most 100 000 mm, and 1 mm where it has no body.

  3. What a reader reads. A reader SHOULD read these items of a product's 'Body' representation (else of its 'Facetation'), in the file's length unit made millimetres, each placed by its IfcLocalPlacement chain and by IfcMappedItem, a transformation's scale and mirror included: an IfcExtrudedAreaSolid of a rectangle, a rounded rectangle, a circle, a hollow circle or rectangle, an ellipse, an I, L, U, T or C shape, an arbitrary closed profile with voids or without (a polyline, an indexed poly curve, a composite curve of polylines and of trimmed lines and circles), or a composite, derived or mirrored profile of them; an IfcPolygonalFaceSet and an IfcTriangulatedFaceSet; an IfcFacetedBrep, with voids or without; an IfcShellBasedSurfaceModel and an IfcFaceBasedSurfaceModel. It writes an upright extrusion as an extrusion, a circle's as a cylinder, a circle swept square to itself as a rod, a body that is one IfcMappedItem of its type's first 'Body' representation map, placed with no scale and no mirror, as mapped, and every other as faces. It draws an arc in straight pieces, four a quarter turn, as section 1.4's note draws a section's; a circle that is no cylinder or rod as a polygon of 32 sides; and a parameterised profile as Part II, 5.2 draws its section, an I's, an L's, a U's and a T's root fillets as arcs and their edge radii and slopes not at all: and says each is approximated.

  4. Booleans and voids. A file whose header names a Reference View (ViewDefinition [ReferenceView…]) has its openings cut in its bodies, which a reader takes as they are. In any other, an element that openings void (IfcRelVoidsElement), and one whose shape is a boolean result, needs a cut that a reader without a solid modeller does not make: it is the mesher's (rule 5); without one, a reader writes a difference as its first operand, a union as its two operands and a voided body as the file draws it before the cut, and says each is approximated. An intersection it does not read (rule 5).

  5. What it does not read it leaves to a mesher, where it has one: a tool apart that meshes the products of a file, with a solid modeller, by their GlobalIds. It asks the mesher once, for the products rules 3 and 4 leave, and writes each mesh as faces, in triangles. A product that neither reads is kept without a body, and the report names it, and why. A reader never guesses a shape.

  6. With the proposal. A tool that applies the proposal reads an element whose shape is "given" so:

    • a compiler derives no body from its members and reports it; it cuts nothing with it, a door or a window in its wall nor a shaft in its hosts, and nothing in it;
    • a validator holds shape, ifcSource.predefinedType and ifcSource.objectType to their schemas (schemas/proposed/ifc-read/), and the element to its category's schema, its references and its type's category, and not to the rules a validator checks in code that place a body or size one: Part II, 5.3; 6.1, rules 2, 3, 5 (but that a door or a window is hosted by a wall), 6, 8, 11 and 13; 6.4; 6.6, rules 2 and 3; 6.7; and 12;
    • a bridge prints its given body, as the class and the predefined type it was read with (ifcSource.entity and predefinedType, and objectType for USERDEFINED), where that is an element class it writes, its IFC type's predefined type its type record's; and for a door or a window no opening.

    A tool of 0.1 keeps shape and the two members of ifcSource as members it does not know (Part I, 10), and derives bodies from the members of rule 2, which are no shape. A project read from IFC is read with the proposal until its elements are recognised (section 2.6).

2.4 Determinism

  1. Ids. An element's, a type's and a level's id is its object's GlobalId read as the UUID its 128 bits encode, in lower case with its hyphens, as buildingSMART's converter keeps an object's identity (2JUHrTM_j3UxZiBnyBfByx is 93791d5d-5beb-437b-b8ec-2f1f0ba4bf3b), so that no two ids differ by the case of their letters alone and no two of their files fold to one (Part III, 4.3); the project's id likewise. A GlobalId that an object read before it holds (the storeys first, then the elements and spaces, then the types, each by its instance number) gives its id -2, -3 … after it, and its ifcSource keeps the GlobalId, which a print gives the first (section 1, the note on rule 3); a text that is no GlobalId gives ifc- and the first 32 hexadecimal digits of its SHA-256. A type that is one of several of one IFC type (section 2.1, rule 4) has - and its category after its id, -generic- and its class in lower case for a generic one; a type of the reader's own is ifc- and its category (ifc-generic- and its class in lower case); a material's id is m- and the first 16 hexadecimal digits of the SHA-256 of its name.
  2. Order. Levels by elevation, then by id; materials, types and elements by id, and results by key, in code-unit order; property sets and their properties by name; a result's bodies in the order of the representation's items.
  3. No clock. Nothing of the time of reading is in the records, the bodies or the report, and coordinates are millimetres rounded to a millionth: one file read twice gives the same bytes.

2.5 The fidelity report

A reader MUST publish with what it reads a fidelity report, as a bridge publishes one with its print (section 1, rule 2): the schema and the view the file names, and its length and angle units; the counts of the levels, materials, types and elements, by category; each class of product the file holds, how many, what they are read as, and how many have a body the reader read, a body a mesher gave, and none; the property sets and values kept; and what it approximated (section 2.3, rules 3 and 4; a level an element is put on by its height; a type split, or of its own), what it left out (sections 2.1 and 2.2) and what it kept without a body (section 2.3, rule 5), each with its reason and the GlobalIds of the objects it concerns.

2.6 Recognising elements (later)

Recognising an element is writing its record in its category's own members, so that Part IV derives its body, and dropping its given body and shape. This first version recognises none. Each class's recognition is a rule of its own, held to the bridge's prints and the reference implementation's: a recognised element read back from a print derives the body the print drew. They are to be proposed class by class, in the order the bridge's classes were compared with the reference implementation's (section 1, its notes):

  1. Beams. An IfcBeam extruded along its axis from a parameterised profile: a beam of a type with that section (Part II, 5.2), its line from its 'Axis' representation and its profile's place from its IfcMaterialProfileSetUsage (section 1.4).
  2. Columns. An IfcColumn, likewise, upright.
  3. Footings. An IfcFooting extruded upright from an outline, or from a circle as a round footing.
  4. Slabs. An IfcSlab extruded upright from an outline with its voids, its type's layers its material layer set's.
  5. Walls. An IfcWall's 'Axis' representation as its baseline, its material layer set usage as its type's layers and their offset, and the doors and windows that fill its openings as its hosted openings, which then cut it (Part IV, 4.3).
  6. Roofs. An IfcRoof's, and an IfcSlab ROOF's, planes as a roof's edges and slopes (Part IV, 4.4).
  7. Railings. An IfcRailing's line as a railing's on a line of its own, or along the edge it stands on (Part II, 12.2).
  8. Electrical devices. A device's class and place as an element of the proposal electrical (Part XIII, 10, read the other way).

Until then, an assistant recognises an element as it writes any element, by commands (Part V); with the proposal, an element it rewrites leaves shape out.

3. Other bridges

BridgeDirectionIn the reference implementation
IFCimportas a linked reference model, not as native records; @openeik/tools reads IFC into records, proposed for 0.2 (section 2)
glTF (binary .glb), STEP, STLexport3D files for viewers and other tools, from the derived bodies (model_export, src/core/io/modelExport.ts)
DXF, PDF, SVGexportdrawings and sheets, from views (application features)

0.1 specifies none of them.