OpenEik.org

Draft OpenEik 0.4.0: draft, not final. From commit 9077aa6c of 1 November 2026. Errata: the errata of 0.4. Raw text: /spec/0.4/language.md. Earlier: OpenEik 0.1, OpenEik 0.2, OpenEik 0.3, kept as they were.

On this page

OpenEik 0.4 specification

Part II. Language

This part defines the records. Each record kind has a JSON Schema in schemas/; the schema fixes the members and their types, and this text fixes what they mean and the rules a schema cannot state. A record MUST validate against its schema, and MUST meet the rules of this part.

1. The record kinds

KindOne perStored at (Part III)SchemaGroup
Projectmodelproject/project.jsonproject/project.schema.jsoncore
Levelsmodel (one list)project/levels.jsonproject/levels.schema.jsoncore
Ordermodelproject/order.jsonproject/order.schema.jsoncore
Gridgrid lineproject/grids/<id>.jsonproject/grid.schema.jsoncore
Materialmaterialproject/materials/<id>.jsonproject/material.schema.jsoncore
Typetypetypes/<discipline>/<category>/<id>.jsontypes/<category>.schema.jsoncore
Elementelement<discipline>/<level>/<category>/<id>.jsonelements/<category>.schema.jsoncore and domain profiles
Originalplaced groupproject/originals/<id>.jsonproject/original.schema.jsoncore
Copyplacement of a groupproject/copies/<id>.jsonproject/copy.schema.jsoncore
Viewviewdrawings/<discipline>/views/<kind>/<id>/view.jsondrawings/view-record.schema.json (drawings/view.schema.json with its items inline)documentation profile
View detail chunkup to 256 detail itemsdrawings/<discipline>/views/<kind>/<id>/details/<n>.jsondrawings/view-details.schema.jsondocumentation profile
Scope boxscope boxproject/scope-boxes/<id>.jsonproject/scope-box.schema.jsondocumentation profile
Dimension stylestyleproject/dimension-styles/<id>.jsonproject/dimension-style.schema.jsondocumentation profile
Detail family2D familyproject/detail-families/<id>.jsonproject/detail-family.schema.jsondocumentation profile
Piecefabricated unitfabrication/pieces/<id>.jsonfabrication/piece.schema.jsonfabrication profile
Jointstructural jointfabrication/joints/<id>.jsonfabrication/joint.schema.jsonfabrication profile
Sheet, title block, image, report, render, deviation, area adjustment, linked referencerecordPart III, section 3application-record.schema.jsonapplication records

The order is listed for its file and its schema: it is an index of the record lists, not a record, and the single-file form has none (Part III, 9, rule 5, and 11).

Reference: manifestSchema in src/core/model/model.ts (the lists and their record schemas); recordLists and listFolders in src/core/versions/layout.ts.

2. The project

project/project.json holds:

  • schemaVersion: 1 (Part I, 9.3);
  • project: {id, name, units: "mm", upAxis: "Z"} and optionally elevationOffset (the project's zero above a shared datum, in mm), site (latitude, longitude, true north in degrees clockwise from plan +Y, an IANA time zone), rooms (room measuring rules), excavation and precast (rules for those profiles), standards (drawing standards: an application value) and envelope (the envelope's air leakage and thermal bridges, section 2.1);
  • projectInformation (optional): values shown on drawings.

A model SHOULD give its site with trueNorth: without it, plan +Y is taken as north (section 6.8), which is right only by chance, and an energy balance cannot place the sun. In 0.1 a site gives all four of its members, so a writer that knows true north but not where the site lies cannot give it; from 0.2, trueNorth may be given alone (decided on 22 October 2026; open questions, 44), as section 2.2 says.

A site's latitude and longitude name the plan's origin: the point [0, 0] of every profile, at the project's zero, the height 0 that levels' elevations are measured from (section 3), which lies elevationOffset above the shared datum. True north lies trueNorth degrees clockwise from the plan's +Y about that point (section 6.8). 0.1 names no coordinate reference system, the map grid in which a surveyor gives eastings and northings, nor a vertical datum: a bridge that places the plan on such a grid takes them from its caller (Part VIII, 1). By the steward's decision of 26 October 2026 (open questions, 79), 0.2 adds an optional site.crs that names both by their EPSG codes, as section 2.2 says.

project.id is the project's identity. Like every id it is stable for the life of the project (Part I, 6): every version, alternative and copy of the folder keeps it. IFC GlobalIds are derived from it (Part VIII), so an element keeps its GlobalId across them.

2.1 The envelope

envelope is {airLeakage?, thermalBridges?}, the values of the thermal envelope as a whole that an energy balance needs and no element carries:

MemberUnitMeaning
airLeakagel/(s·m²)the envelope's design air leakage at a pressure difference of 50 Pa, q50, per m² of the envelope's area (EN ISO 9972)
thermalBridgesa list, at most 1000, of {kind, psi, name?}: the linear thermal transmittance psi in W/(m·K), from −2 to 5, of the junctions of one kind (EN ISO 14683, EN ISO 10211)
dimensionswhich dimensions the psi values refer to: internal, overall-internal or external (EN ISO 13789). A ψ differs with them, so it SHOULD be given with thermalBridges

The lengths are not stored: a tool measures them from the elements where the junctions are (section 10), in the dimensions dimensions names. A thermal bridge's kind is a plain string, and two thermal bridges MUST NOT have the same kind: a tool would not know which ψ to take. So that every tool measures the same junctions, a kind SHOULD be one of these names:

KindThe junction
wall-ground-slaban exterior wall on a slab on the ground
wall-roofan exterior wall to a roof
wall-intermediate-flooran exterior wall past an intermediate floor
opening-in-wallaround a window or a door in an exterior wall
outer-corner, inner-cornerwhere two exterior walls meet, outside or inside
balconya balcony slab through an exterior wall

A kind may also be a designation of EN ISO 14683 with its number (GF1, W8), the junction of that standard's catalogue: a tool reads it only if it knows the catalogue. A corner is better named outer-corner or inner-corner, which says which it is without the catalogue. A tool that does not know a kind SHOULD say so, and not guess its length. The envelope is set with project.envelope (Part V, 2.1).

2.2 The site

Part of 0.2. OpenEik 0.1 had it as the proposal site: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a site gives all four of its members, and crs is a member a schema of 0.1 does not know, which a reader keeps (Part I, 10). The section adds one member, site.crs, and no file.

By the steward's decisions of 22 October 2026, asked by an analysis application (open questions, 44: rules 1 and 2), and of 26 October 2026 (open questions, 79: rules 3 to 5).

  1. True north alone. A site MAY give trueNorth alone. Its latitude, longitude and timeZone are its location: a site gives the three together or none of them, and a site that gives its location gives trueNorth too. So a site is {trueNorth}, or all four. A validator reports a site that gives part of its location, or its location without trueNorth, as its schema's error.

  2. A site without a location says which way north is, and nothing of where the site lies. Its true north is read as section 6.8 reads it. A tool that needs the location, an energy balance that places the sun or a bridge that places the plan on a map, has none, and SHOULD say so rather than assume one. A bridge that writes IFC writes the true north of its context and no latitude, longitude or map conversion.

  3. Its coordinate reference systems. A site that gives its location MAY give crs, {horizontal, vertical?}, each the code of a coordinate reference system in the EPSG registry, written EPSG: and the number without a leading zero: horizontal the projected system, the map grid, in which a surveyor gives eastings and northings, and vertical the vertical system in which heights are given. A site MUST NOT give crs without its latitude and its longitude; a validator reports it, and a code of another form, as its schema's error. OpenEik lists no systems, and does not check that a code is one the registry gives: its publisher, the registry, does.

  4. What it says. The latitude and the longitude stay the site's place: the plan's origin [0, 0] (above). crs.horizontal names the grid a bridge projects that place into, and where the site gives crs.vertical, elevationOffset is the height of the project's zero in that vertical system. A writer that is given a surveyor's eastings and northings in a grid turns them into a latitude and a longitude, and names the grid in crs.horizontal, so that the model holds one place: it holds no eastings and northings beside the latitude and the longitude, which could disagree with them.

  5. A bridge that places the plan on a map projects the latitude and the longitude into crs.horizontal, and turns the plan's x axis by true north and by the grid's convergence there, the angle from true north to the grid's north; Part VIII, 1.7 says how IFC writes it. A bridge that cannot project the system says so in its fidelity report, and writes no map conversion. Which systems a bridge projects is the bridge's: a code it does not know is no error of the model.

A tool that reads a plan drawing with a north arrow, and nothing of where the building stands, writes "site": {"trueNorth": -12.5}: north lies 12.5° counter-clockwise from the plan's +Y. A project placed in SWEREF 99 TM, its heights in RH2000, writes "site": {"crs": {"horizontal": "EPSG:3006", "vertical": "EPSG:5613"}, "latitude": 57.70887, "longitude": 11.97456, "timeZone": "Europe/Stockholm", "trueNorth": 0}, and "elevationOffset": 12500 when its zero lies 12.5 m above RH2000's.

3. Levels

project/levels.json is the list of levels in their stored order, at least one. Each is {id, name, elevation}. Level ids MUST be unique. Level order in the file is the order a writer keeps; readers MUST NOT infer elevation order from it.

4. Materials

A material is {id, name, color, pattern, revision} with an optional category (section 4.2), from 0.4 an optional masonry (section 4.3), an optional surface (a projected surface pattern: brick, boards, panels and others) and the optional physical values of section 4.1. id matches ^[A-Za-z0-9_-]{1,60}$.

  1. A layer names its material by name: a layer's material text matches the material whose name is equal to it, ignoring case and surrounding white space.
  2. A layer whose material text matches no material is valid: its material is that text alone.
  3. A layer of a type that has an insulation layer SHOULD have a design thermal conductivity: its own (thermal.lambda, section 5.1) or its material's lambda. A membrane and an air layer (one with thermal.air) need none: EN ISO 6946 gives an air layer its own resistance. In 0.1 an insulation layer marks a part of the thermal envelope, since a model does not yet say which parts face the outside.
  4. The cut pattern clt (of 0.2; section 14). A material's pattern, the hatch its cut is drawn with, and a layer's own (section 5.1), MAY be clt, cross-laminated timber's, beside the patterns of 0.1. A drawing draws it as it draws the others: how a hatch looks is the drawing application's. In 0.1 it is a value the schemas refuse; a validator of 0.2 takes it (open questions, 115).

Reference: materialNamed in src/core/assemblies/materialLibrary.ts and its use in src/core/geometry/geometry.ts.

4.1 Physical values

The values that building physics, structure, fire and climate tools read from a material, all optional. Where a value comes from a standard's classes, it is a plain string (section 8, rule 3).

MemberUnitMeaning
lambdaW/(m·K)the design thermal conductivity, as a calculation uses it (EN ISO 10456); greater than 0, at most 1000
densitykg/m³the density ρ of the material itself (EN ISO 10456): plain concrete's, not reinforced concrete's; greater than 0, at most 30 000. It is no structural unit weight (below)
specificHeatJ/(kg·K)the specific heat capacity c; greater than 0, at most 20 000
vapourResistancethe water vapour diffusion resistance factor μ (EN ISO 10456, EN ISO 13788); from 1 to 10⁹. A membrane whose thickness is nominal gives its layer's thermal.sd instead
solarAbsorptanceα of an outer surface, from 0 to 1
emissivityε of a surface that faces an air layer, from 0 to 1 (EN ISO 6946, annex D)
lightReflectancethe visible light reflectance of an inner surface, from 0 to 1, for daylight
criticalMoisture%the critical relative humidity at which the material risks mould or damage, greater than 0, at most 100: the critical moisture state that a moisture safety design checks against
fireReactionthe reaction to fire class (EN 13501-1), such as A1, B-s1,d0 or Dfl-s1
strengthClassthe strength class or grade (EN 338, EN 14080, EN 206, EN 10025, EN 10080, and for masonry EN 771 and EN 998-2, section 4.2), such as C24, GL30c, C30/37, S355, B500B or M10. A member's section states its own grade, which decides where the two differ (section 5.2, rule 6)
climate{gwpA1A3, unit, source?}: the global warming potential of modules A1–A3 in kg CO₂e per declared unit, the unit (kg, m3, m2, m or piece), and where it comes from (an EPD's identifier, EN 15804, or a climate database's)

A writer that has a value from a declaration rather than a design value says so in the material's name or in an extension member (Part I, 11); lambda is the value a calculation uses.

density is the material's own density, the ρ that a heat capacity, a moisture calculation or a mass reads. It is no structural unit weight: a self-weight for structural design includes what the material carries as built, such as the reinforcement in concrete, and is a weight per volume, in kN/m³.

4.2 A material's category

Part of 0.3. OpenEik 0.2 had it as the proposal materialCategory (open questions, 121): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).

A material says what kind of material it is, so that a tool that analyses, prices, prints or exports a model reads it rather than telling it from a strength class or from words in a name, which differ by language and by writer. In a model of 0.2 that does not ask for the proposal, a material's category is a member the schemas of 0.2 do not know, which a reader keeps and a validator may warn about (Part I, 10).

  1. A material's category. A material MAY carry category, one value of rule 2's list. A validator MUST report any other value as an error (schema-invalid).

  2. The list. IFC 4's eleven recommended values of IfcMaterial's Category, as IFC gives them, and seven more:

    ValueThe material
    concreteconcrete, cast in place or precast, plain or reinforced
    steelsteel, carbon or stainless, its sections, plates, sheets and bars
    aluminiumaluminium and its alloys
    blockmasonry blocks: of concrete, of lightweight aggregate, of aerated concrete
    brickbricks, of fired clay or of calcium silicate
    stonenatural stone
    woodtimber and every product made from it: sawn, glued laminated, cross-laminated, laminated veneer lumber, and boards of wood such as plywood, OSB and fibreboard
    glassglass
    gypsumgypsum plaster and plasterboard
    plasticplastics, cellular plastics such as EPS, XPS and PIR among them, and plastic membranes
    earthsoil, clay and fill
    masonrymasonry whose units are not said: units and mortar as one material
    metala metal other than steel and aluminium, such as copper, zinc and lead
    mineralFibremineral wool, of glass or of stone, and other mineral fibre
    bitumenbitumen and bituminous felts and membranes
    ceramicceramic tiles and other fired ceramic that is no brick
    mortarmortar and render, of cement, of lime or of both
    othera material none of these values names
  3. Engineered timber is wood. A glued laminated or cross-laminated timber, or another engineered timber, is wood, and no category of its own: its strengthClass, such as GL30c (EN 14080), or a cut pattern clt (section 4, rule 4) says which. A second place that said it could disagree with the first, as a member's grade and its material's strength class can (section 5.2, rule 6).

  4. In IFC. A bridge writes a material's category as its IfcMaterial's Category (Part VIII, 1), the value as rule 2 gives it, and a material without one with no Category. A reader of IFC (Part VIII, 2) reads an IfcMaterial's Category that is one of rule 2's values, its case ignored (Concrete is concrete), as its material's category, and leaves out any other.

  5. The strength classes of masonry. A material's strengthClass (section 4.1) MAY also give a masonry unit's class by EN 771, or a mortar's class by EN 998-2, such as M10, a plain string as the other standards' classes are (section 8, rule 3).

Reference: none in the reference implementation, which tells a material's surface from its name (namedSurface in src/core/walk/walkSurfaces.ts). In @openeik/tools: MATERIAL_CATEGORIES and categoryOfIfc in packages/openeik/src/material-category.js; the validator holds a material's category to schemas/0.2/proposed/materialCategory/category.schema.json by the proposal's index (proposed03Members in check.js); ifcOfModel writes and eikOfIfc reads the Category; for a model of 0.3, and for one of 0.2 asked for with proposals: ["materialCategory"].

4.3 Masonry's design values

Part of 0.4. OpenEik 0.3 had it as the proposal masonry (open questions, 127): a model of 0.3, read by 0.3's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).

A material of masonry says what a design of masonry by EN 1996-1-1 reads of it, which no strength class says: its units, its mortar, how it is laid, and what tests declare of it. In a model of 0.3 that does not ask for the proposal, a material's masonry is a member the schemas of 0.3 do not know, which a reader keeps and a validator may warn about (Part I, 10).

  1. Which materials. A material whose category is masonry, brick, block or stone (section 4.2) MAY carry masonry, its design values. A validator MUST report masonry on a material of another category, or of none, as an error (masonry-category). A layer of masonry names one such material, which holds its units and its mortar both.
  2. Its units. unit is {standard, fb?, group?, category?}, and required: standard, the part of EN 771 its units are made to, one of EN 771-1 to EN 771-6, which says their material (clay, calcium silicate, aggregate concrete, autoclaved aerated concrete, manufactured stone, natural stone); fb, their normalised mean compressive strength, in N/mm², more than 0; group, 1 to 4, their group by EN 1996-1-1, Table 3.1; and category, I or II, the category of their manufacture's control.
  3. Its mortar. mortar, where given, is {kind, class?, fm?, designed?}: kind, general, thin or light, a general purpose, thin layer or lightweight mortar (EN 998-2), a thin layer's bed joints being 0.5 to 3 mm thick as EN 1996-1-1 says, so that no member gives their thickness; class, its compressive strength class, such as M10, a plain string (section 8, rule 3); fm, its compressive strength, in N/mm², more than 0; and designed, true for a designed mortar and false for a prescribed one (EN 998-2).
  4. How it is laid. executionClass, 1 to 5, the class of execution by which a national annex sets the partial factor; and joints, {perpends, longitudinal}: perpends, filled or unfilled, the perpend joints (EN 1996-1-1, 3.6.2), and longitudinal, true where a joint runs along the wall through its thickness (3.6.1.2).
  5. What tests declare. declared, {fk?, fvko?, fxk1?, fxk2?, creep?, KE?}, values a test gives in place of those EN 1996-1-1 derives: the characteristic compressive strength fk, the initial shear strength fvko and the flexural strengths fxk1 and fxk2, each in N/mm² and more than 0; the final creep coefficient creep, 0 or more; and KE, more than 0, the factor that gives the modulus of elasticity, E = KE · fk.
  6. What it is not. strengthClass stays a plain string (section 4.1, and 4.2, rule 5), for a model that has only a name. A wall's effective height and the restraint of its edges are the wall's and the analysis's, and no member of a material.

Reference: none in the reference implementation. In @openeik/tools: the proposal's schema, schemas/0.3/proposed/masonry/masonry.schema.json, which the validator holds a material's masonry to by the proposal's index; masonryChecks in packages/openeik/src/check.js (masonry-category); asked for with proposals: ["masonry"] on a model of 0.3.

5. Types

A type is the shared definition its elements follow, as a class is for its instances. Every type has:

MemberMeaning
id, nameidentity and display name
categoryone of the thirty-one element categories; only elements of this category may use the type
material, colorthe type's material text and display colour (#rrggbb)
width, heightthe type's nominal size in mm, positive. The size its elements take only where the type sets it (section 5.3): a door's or window's type with a windowFamily or openingFamily, a type with a component (other than a contour plate's, a bolt group's or a weld's) or an insert, a round footing type. A door or window whose type has neither family keeps its own size
layersoptional: the compound layers (section 5.1)
revisionoptional; absent means 0

and at most a few payloads, each allowed only for some categories:

PayloadAllowed forWhat it defines
layersslab, wall, foundation, roof, ceilingcompound layers
sectionbeam, columna member section: rectangle, I, T, RHS, CHS, round, L, 2L, U, sheet or custom, with width, depth, web, flange, and a custom section's outline (section 5.2)
framingbeam, slabEdgea parametric framing family (section 5.4)
edgeProfileslabEdgean edge beam's or an edge form's section (section 5.6)
gutterProfilegutter (required there)a gutter's section (section 5.6)
windowFamilywindowa window family; the profiles of its frame and its mullions are 0.2's (Part IV, 4.3.1, rule 15)
openingFamilydoor, windowa door or window family; the opening's kind decides which. Its material is its frame's: wood, aluminium, aluClad (aluminium-clad wood) or steel (open questions, 89); a door's leafMaterial, its leaves' where it is not the frame's, is 0.2's (Part IV, 4.3.1, rule 14), and so are the profiles of its frame, its mullions and its sashes (rule 15)
outlinetypes with an openingFamilya polygonal opening shape
wallAttachwalldefaults for attached wall tops
footingfoundationa round footing; the type's width equals its diameter
component, voidForm, edgeDetail, insertcomponenta component, a void, an edge detail or a cast-in item; a component type MUST have one of them
room, shaft, stair, railing, pipe, duct, terminal, furniture, planting, fire, road, parkingtheir categoriesthe domain data of those categories
genericgeneric (required there)what the type stands for, its IFC class and its discipline (section 6.6)
thermalslab, wall, foundation, roof, ceiling, door, windowdeclared thermal values (section 5.5)
acousticwall, slab, doordeclared sound insulation (section 5.5)
assembly, catalogSource, handbookanyapplication values (section 9.2)
declaredGeometryanya supplier's geometry at a declared level of detail (section 12.1)

A type MUST NOT carry both openingFamily and windowFamily. The category-specific schemas types/<category>.schema.json encode the table above.

A terminal type's terminal.kind is one of the kinds types/type.schema.json lists, from air terminals and units to sanitary fixtures, appliances and radiators; chilledBeam is an active chilled beam. unitHeater is a unit heater, and the kinds of components in a pipe or a duct are section 6.9's. A terminal type's terminal.mount is ceiling, wall, floor, roof, duct for a component in a duct, or pipe for a component in a pipe (section 6.9).

A component's family. A component type's component.family says what its elements are: plate a steel plate, bolt a bolt group, weld a weld, angleBracket an angle bracket, joistHanger a joist hanger and timberSection a piece of a timber section; toilet and basin are sanitary fixtures of before 0.1, read in 0.1 and deprecated in 0.2, which a terminal now stands for (open questions, 73). Its depth is its third size in mm, beside the type's width and height: a plate is width by depth in its own x and y, and its thickness is its height. A bracket's or a hanger's sheet is thickness thick, a hanger's flange is the width of each of its nail flanges, folded outwards where it is positive and inwards where it is negative, and a punched connector's nails are in the pattern nails. A plate type's holes go through each of its plates, each centred at x and y in the plate's own frame, diameter across, and slotted where it gives a length, its length overall, along angle degrees from x, its ends round. Part VIII, 1 names each family's IFC class.

A bolt. A bolt type holds its bolt, component.bolt, which every bolt of its groups is: its diameter, in mm; its grade, 4.6, 5.6, 8.8 or 10.9, and 8.8 where it gives none; its kind: bolt, a hex bolt with its head, where it gives none; anchor, an anchor bolt, a rod cast into concrete with a nut on top and an anchor plate at its foot; or rod, a threaded rod; its washers: under the head and the nut (2), under the nut (1, where it gives none) or none (0), and a rod's under both its nuts, or under the far one or its one nut; a rod's nuts: on both faces of the grip (2, where it gives none: through-bolted, or a base plate on levelling nuts), on the face at z = 0 only (1: into an anchor sleeve, or glued in) or none (0); and its finish, hdg hot-dip galvanized or black.

Reference: the type checks in validateManifest (src/core/model/model.ts), validateOpeningFamily (src/core/elements/openings.ts), validateFootingType (src/core/elements/footings.ts), validateInsertType (src/core/elements/inserts.ts).

Part of 0.2; OpenEik 0.1 had it as the proposal components. By the steward's decision of 26 October 2026 (open questions, 73), the families toilet and basin are deprecated (Part I, 9.6). A reader reads a component type of either family, and its elements, as 0.1 does. A writer SHOULD NOT write one: it writes a terminal instead (section 6.9), its type's terminal.kind wc for a toilet and washbasin for a basin, as the reference implementation turns them on load. A validator SHOULD warn about each such type (component-retired).

5.1 Layers

A layer is {id, name, function, material, thickness, color, core} with optional pattern, inset, extent and domain rules (framing, joists, precast).

  1. function is one of structure, substrate, insulation, finish, membrane.
  2. thickness is in mm. Only a membrane MAY have thickness 0; the sum of a type's layer thicknesses MUST be positive.
  3. Layer ids MUST be unique within the type.
  4. core marks the core layers. The layers before the first core layer are the exterior layers.
  5. inset (mm, 0–3000) is allowed only on slab and roof types; extent (where a roof layer stops in plan) only on roof types.
  6. Order: wall layers exterior first, left of the directed baseline; horizontal layers top first (Part I, 5.1).
  7. When a type has layers, the element's thickness is the sum of the layer thicknesses, and the element's own width (walls) or height (horizontal elements) is ignored for its body (Part IV).
  8. thermal (optional) is {lambda?, air?, sd?}: the layer's own design thermal conductivity in W/(m·K), which a calculation uses instead of its material's (a framed layer's, say, or a product's); air, which makes the layer an air layer and says whether it is closed, slightly-ventilated or ventilated (EN ISO 6946, 6.9; behind a ventilated air layer the layers outside it do not count); and sd, the water vapour diffusion-equivalent air layer thickness in m, from 0 to 100 000, of a membrane or foil (EN ISO 13788).

Reference: validateManifest (layer checks), basicSolids (thickness ?? e.width, thickness ?? e.height).

5.1.1 A layer's grain and an element's main direction

Part of 0.2. OpenEik 0.1 had it as the proposal grain: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), grain and mainDirection are members a schema of 0.1 does not know, which a reader keeps (Part I, 10). The section adds these two members and no file.

By the steward's decision of 28 October 2026 (open questions, 90). A layer of cross-laminated timber (CLT), and any layer stiffer one way than the other, such as LVL with cross veneers, a glulam deck or a profiled sheet, has a grain: its stiffness, its strength, its deflection, its vibration and its fire resistance depend on which way its fibres run. These rules let a model say so, so that two tools that read one building read one layup.

  1. A layer's grain. A layer of a type of the category slab, wall, roof, ceiling or foundation MAY carry grain: "along" where its fibres, or a sheet's strong direction, run along the element's main direction (rules 2 to 4), and "across" where they run square to it in the layer's plane. A layer without grain states none: it is isotropic, or its grain is not said. grain takes no other value, and no other angle.
  2. A slab's, a ceiling's or a foundation's main direction is a direction in plan. Where the element has panels (section 6.3), it runs square to their direction, as the panels do: (direction + 90) mod 180 degrees, counter-clockwise from the model's x axis. Where it has no panels, it MAY carry mainDirection, its main direction in degrees counter-clockwise in plan from the model's x axis. Where it carries both, mainDirection MUST be (direction + 90) mod 180, the two compared modulo 180 within 0.01°: a validator reports one that is not (main-direction). Where it has neither, the model states no main direction for it, and its layers' grain is not placed. On a slab with a plane (section 6.3.1), its main direction is that direction in plan projected vertically onto the plane, as a roof's mainDirection is (rule 4).
  3. A wall's main direction is its mainDirection: "vertical", up the wall, or "horizontal", along its baseline, in the wall's plane; "vertical" where it gives none.
  4. A roof's main direction lies in the plane of each of its faces: along the fall line of the slope-defining edge whose plane the face is (Part IV, 4.4, rule 1), square to that edge in plan, unless the roof carries mainDirection, degrees counter-clockwise in plan from the model's x axis, which every face takes, projected vertically onto its plane. A face of no slope lies level, and its main direction is mainDirection; without it, the model states none.
  5. Which elements carry it. An element MUST NOT carry mainDirection unless its category is slab, ceiling, foundation, roof or wall: a number in degrees on the first four, and "vertical" or "horizontal" on a wall. A validator reports mainDirection on an element of another category (main-direction), and a value of the wrong kind as its schema's error.
  6. The order of the layers is rule 6 of section 5.1: a horizontal type's top first, a wall's exterior first. So a reader knows which face each layer is on, and so where a layup is not symmetric.
  7. What it does not cover. A layer's lamella width, its edge gluing, a product's declared values and its European Technical Assessment are product data, which applications keep in their extension members (Part I, 11.2) until a later version has a product record. A layer's strength class is its material's strengthClass (section 4.1), so a type whose layers are of two strength classes names two materials.
  8. Bodies and prints. The grain and the main direction change no body: a compiler derives the same bodies with them or without them (Part IV), and the IFC4 print of Part VIII, 1 is the same until a later version says how IFC carries them.

A CLT floor type of five lamellas 40 mm thick, C24 along and C14 across, top first, has the layers {"function": "structure", "material": "CLT C24", "thickness": 40, "grain": "along"}, then "CLT C14" "across", "CLT C24" "along", "CLT C14" "across" and "CLT C24" "along", each with its id, name, colour and core. A floor of it whose panels, 2.4 m wide, lie side by side along y and span along x has "panels": {"width": 2400, "direction": 90}: its main direction is 0°, along x, and so are the fibres of its outer layers. A layup of seven layers with two along at each face, which no rule of alternation gives, is along, along, across, along, across, along, along. A wall over a wide opening whose outer layers run horizontally, so that it spans the opening as a beam does, has "mainDirection": "horizontal".

5.2 Sections

A section gives a member's cross-section: its shape, one of rectangle, I, T, RHS, CHS, round, L, 2L, U, sheet and custom, and its sizes. For a beam, width runs across the member and depth up; for a column, width runs along x and depth along y before the column's turn, the section's top towards +y. Except for a T, a 2L, a round and a custom section, which have only their own rules (rules 1 to 3, and 8 to 10), web < width and 2 × flange < depth MUST hold; an L section needs web < width and flange < depth; a sheet section's width and depth follow its profiled sheet (cover width and profile height).

  1. A T section is a flange on top of a web, which stands under the middle of the flange. width is the flange's width, depth the overall depth, web the web's thickness and flange the flange's. web < width and flange < depth MUST hold.

  2. A 2L section is two identical angles back to back: their vertical legs stand gap apart, and their horizontal legs point outward at the top. width is the overall width, both horizontal legs and the gap; depth is a vertical leg's length, web a vertical leg's thickness and flange a horizontal leg's. Each horizontal leg is (width − gap) / 2 long. gap < width, web < (width − gap) / 2 and flange < depth MUST hold. The angles' legs may be equal or unequal: a horizontal leg, (width − gap) / 2, need not be as long as a vertical one, depth.

  3. A round section is a solid round bar: width and depth are both its diameter, and MUST be equal, within 10⁻⁶ mm. Its web and flange are not read, and no rule applies to them.

  4. gap is the distance between the backs of a 2L's angles, in mm, from 0 to 10 000; absent, it is 0. A section of any other shape MUST NOT carry it.

  5. On a T, radius is the radius of its two root fillets, one each side of its web under its flange; on a 2L, that of each angle's root fillet, where its vertical leg meets the underside of its horizontal leg. On an RHS it is the radius of its four outer corners, and its inner corners are rounded to radius less the thicker of its web and flange, square where that is not more than 0; without a radius, or with 0, all eight corners are square. On a rectangle it rounds the four corners (rule 7). It is not read for a round or a custom section.

  6. designation names the section as its product standard does, such as IPE 200, HEA 160 or L 100 × 100 × 10, and grade the grade of the members' material, such as S355J2, GL30c or C24: plain strings (section 8, rule 3). A type can state a grade twice, in its section's grade and in the strengthClass of the material its material names (section 4.1). Where the section's grade is not empty, it is the members' grade: a reader that reads a member's grade MUST take it, and where the two differ a validator MAY warn (decided on 22 October 2026; open questions, 42).

  7. A rectangle with a radius is a solid rectangle with its four corners rounded: each corner a quarter circle of that radius, tangent to the two sides that meet there, as a timber section with rounded corners is, such as wane-edged timber hewn flat on its faces so that the courses of a wall bear flat on each other. Without a radius, or with 0, its corners are square. radius MUST NOT be more than half the smaller of width and depth, within 10⁻⁶ mm: at half, its two shorter sides are half circles, and a section as wide as it is deep is a circle. Its area is width × depth − (4 − π) × radius² and its centroid its middle; its area, its second moments of area and its members' volume and weight are the rounded shape's, its arcs exact. A command that writes a larger radius is refused (Part V). A reader that reads a model holding one, without the whole-model check (Part III, 12), takes the section's corners as rounded to half its smaller side, and so does a compiler or a bridge that meets one.

  8. A custom section is the region its outline gives: outline.points, the contour of its outside, less each of outline.voids, each {points}, the contour of a hole through it. A custom section MUST carry outline, and a section of any other shape MUST NOT. A contour is a list of entries in mm in the section's own plane, x running as the section's width does and y as its depth does: for a beam, x across the member towards its left, seen from its start, and y up, before its roll; for a column, x and y before its turn. A contour is closed by its last side, from its last entry back to its first, and does not repeat its first point; it may run either way round. Each entry is one of:

    • [x, y], a corner;
    • [x, y, r], a corner rounded by an arc of radius r mm, tangent to the two straight sides that meet there, each of which it takes r × tan(θ / 2) off, θ being the angle through which the contour turns at the corner; with r 0, the corner is square;
    • {"through": [x, y]}, a point that the side from the entry before it to the entry after it passes through: that side is the arc of the circle through the three points.

    A wane-edged log 200 mm wide and 250 mm deep, its corners rounded to 50 mm, is {"points": [[0, 0, 50], [200, 0, 50], [200, 250, 50], [0, 250, 50]]}, the section that a rectangle with a radius of 50 is too (rule 7); a D-log 150 mm wide and 200 mm deep, flat on its inner face, its top and its underside and round on its outer face, is {"points": [[0, 0], [150, 0, 100], [150, 200, 100], [0, 200]]}; glulam 90 mm × 315 mm with its edges chamfered 10 mm, {"points": [[10, 0], [80, 0], [90, 10], [90, 305], [80, 315], [10, 315], [0, 305], [0, 10]]}; and a round log 300 mm across, hewn flat and 240 mm wide on its underside, {"points": [[-120, 0], [120, 0], {"through": [0, 240]}]}, 300 mm wide and 240 mm deep. A void is written as the outline is: a bored core 40 mm across in the middle of the wane-edged log is {"points": [[80, 125], {"through": [100, 105]}, [120, 125], {"through": [100, 145]}]}.

  9. A custom section's contours MUST each hold three entries or more, at least two of them corners, and no two through entries one after the other (the last entry and the first are one after the other). Within a contour:

    • no entry lies less than 0.1 mm from the entry before it, the first from the last, and no through entry lies within 0.01 mm of the straight line between the corners beside it;
    • a corner has a radius only where the contour turns, and both its sides are straight: neither the entry before it nor the one after it is a through;
    • each straight side is long enough for the corners at its ends: what their radii take off it, together, is not more than its length, within 0.01 mm;
    • no side crosses or touches another, the arcs of rounded corners and of through entries among them: two sides that follow one another share their one point and meet nowhere else.

    Each void MUST lie inside the outline, with no side of it crossing or touching a side of the outline or of another void, and no void inside another. A custom section's width and depth MUST be the width and the height of its outline's bounding box, its arcs included, within 0.1 mm. Its web, flange and radius are not read, and no other rule of this section applies to them.

  10. A custom section is placed by the middle of its outline's bounding box, as every section is placed by the middle of its width and depth (Part IV, 6.5): a point [px, py] of its outline lies px − mx across a beam and py − my + depth / 2 up from its underside (Part IV, 6.2, rule 3), and (px − mx, py − my) from a column's centre before its turn (Part IV, 6.1), (mx, my) being that middle. Its area, its centroid and its second moments of area are those of its region, its arcs exact and its voids taken out; its members' volume is that area along their length, and their weight that volume by their material's density (section 4.1). Plans and sections cut it as its region, its voids open, with its type's material and hatch, as they cut any section. A compiler MAY draw an arc as straight pieces: the arc is the rule, and the pieces are the compiler's, as a pipe's circle is (Part IV, 12.3, rule 5). Part VIII, 1.4 says how a bridge writes a custom section and a rectangle with a radius.

Reference: validateMemberSection (src/core/elements/sectionRules.ts), which validateManifest calls for every type; partGrade (src/core/pieces/pieces.ts), which takes a section's grade before its type's material (rule 6); steelCells (src/core/geometry/steelProfiles.ts), an RHS's corners (rule 5). On the branch named above: rectangleRadius, roundedRectangleArea and roundedRectangleInertia (src/core/geometry/roundedRectangle.ts) and refuseRectangleRadius (src/core/elements/sectionRules.ts), rule 7.

5.3 Sizes a type sets

A door's or window's type with a windowFamily or openingFamily, a component type with a component (other than for a contour plate, a bolt group or a weld, which keep their own sizes), a round footing type and a cast-in item's type set their elements' width and height.

A beam whose type has a section takes its depth from the section: its height MUST equal section.depth.

No other type sets an element's width or height. A door or a window whose type has neither a windowFamily nor an openingFamily has its own width and height, whatever its type's are, and so has an element whose type is a void form or an edge detail (voidForm, edgeDetail).

  1. A writer MUST write such an element's width and height equal to its type's (for a sectioned beam, its height equal to the section's depth).
  2. A command that sets a different width or height on such an element MUST be refused (Part V). An element file that says so fails the whole-model check (Part III, 12, rule 5): a validator MUST report it as an error, and a reader that performs the check refuses the folder (Part III, 14, rule 3).

Reference: typeSizes and typeSetSizes in src/core/model/typeSets.ts; refuseTypeSetSizes in run; typeSetInFiles in src/core/versions/eikFolder.ts.

5.4 Framing families and their expression language

A framing payload is a parametric 2D family, swept across its plane: {name, previewLength, parameters, parts} with optional trussStyle, edgeAnchor and tail. Its numbers are expressions: strings in the language below. This language is normative: every implementation that derives framing bodies MUST evaluate it exactly as stated.

Grammar (ISO 14977 EBNF; white space between tokens is ignored):

expression = sum ;
sum        = product , { ( "+" | "-" ) , product } ;
product    = unary , { ( "*" | "/" ) , unary } ;
unary      = ( "+" | "-" ) , unary | primary ;
primary    = number | call | name | "(" , sum , ")" ;
call       = function , "(" , sum , { "," , sum } , ")" ;
function   = "sqrt" | "abs" | "min" | "max" | "sin" | "cos" | "tan" | "atan" ;
number     = digit , { digit } , [ "." , { digit } ] | "." , digit , { digit } ;
name       = letter , { letter | digit | "_" } ;
letter     = "A" | … | "Z" | "a" | … | "z" ;
digit      = "0" | … | "9" ;

Tokens. A source is split into numbers, names and the characters ( ) + - * / ,. Any other character except white space makes the expression invalid. An expression is 1 to 240 characters and at most 160 tokens.

Names. A name not followed by ( is a value from the scope:

NameValue
Lengththe member's length in mm. For a beam: the length of its set-out line (Part IV, 6.2), hypot(plan length, endRise)
a family parameterthe element's framingParameters[name] if the parameter's scope is instance and the element gives it, else the parameter's value
Overhang, TipTopin a tail cut only: that tail's overhang, and the height of the tail tip's top over the bearing plane

A name followed by ( MUST be one of the eight functions. An unknown name or function makes the expression invalid.

Functions. Angles are in degrees.

FunctionArgumentsValue
sqrt(x)1√x; invalid when x < 0
abs(x)1|x|
min(a, b, …), max(a, b, …)2 to 8the least or greatest argument
sin(x), cos(x)1of x degrees
tan(x)1of x degrees; invalid when |cos x| < 10⁻⁹
atan(x)1the angle in degrees whose tangent is x

Evaluation. + - * / are IEEE 754 double operations, left to right within a precedence level; unary signs bind tighter than * and /. The expression is invalid when its value is not finite or its magnitude exceeds 1 000 000.

Parameters. A family has at most 32 parameters {name, value, scope, min, max}. A parameter's name matches ^[A-Za-z][A-Za-z0-9_]*$, is unique, is not Length and is not a function name. min ≤ max, and the value used (the type's, or the element's instance override) MUST lie in [min, max]. An element's framingParameters MAY name only parameters whose scope is instance.

The family plane of a beam: x runs along the member's set-out line from its start, z at right angles to it, upwards, from that line (the element's start point at level + offset, rising by endRise over the span; Part IV, 6.2). A family point (x, z) lies at plan position start + u (x cos α − z sin α) and elevation level + offset + z cos α + x sin α, where u is the unit plan direction and α the line's slope. Each part is swept across the plane by its width, centred on the plane.

Parts. A member part is a straight member from point a to b with a width and a depth; a profile part is a polygon outline with a width; a plate part is a convex nail plate on the members it lies on. when makes a part exist only while its value is greater than 0. cuts and cutBy cut a part by lines or by other parts. Part IV, 6.6, of 0.2, says exactly how: where each part lies and how it is cut, a tail's cuts and a slab edge element's plane, and what a family that cannot be built is.

Reference: framingSchema, framingExpression and framingProfiles in src/core/elements/framing.ts; the placement in src/core/geometry/framingGeometry.ts.

Example, from a rafter family in the reference implementation's examples: a profile part of width: "Width" whose outline runs from ["-Overhang", "-Rise*Overhang/Length"] through ["0", "0"], ["0", "Notch"], ["Seat", "Notch"] and ["Length", "Rise"] to ["Length", "Rise+Depth"], with parameters Width and Depth of scope type and Rise, Overhang, Seat and Notch of scope instance.

5.5 Building physics on types and rooms

A type MAY carry the values its elements have as a whole, declared by a manufacturer or a test, which a calculation uses instead of deriving them:

MemberOnMeaning
thermal.uthe layered categories, door, windowthe thermal transmittance U in W/(m²·K), greater than 0, at most 20: of a door or a window as a whole, its frame included (EN ISO 10077); of a layered type tested as a whole, such as a prefabricated element, whose U replaces the one its layers give (EN ISO 6946)
thermal.gdoor, windowthe total solar energy transmittance of the glazing, from 0 to 1 (EN 410)
thermal.frameFractiondoor, windowthe share of the opening's area that is frame, from 0 to less than 1
thermal.lightTransmittancedoor, windowthe glazing's visible light transmittance τv, from 0 to 1 (EN 410)
acoustic.rwwall, slab, doorthe weighted sound reduction index Rw in dB, from 0 to 120 (EN ISO 717-1)
acoustic.lnwslabthe weighted normalized impact sound pressure level Ln,w in dB, from 0 to 120 (EN ISO 717-2)
  1. A type of a layered category carries thermal.u only, and a wall's or a door's acoustic carries rw only.
  2. A door's or a window's type SHOULD carry thermal.u; one with glazing SHOULD carry thermal.g too.
  3. An element's fire resistance class stays the fire profile's fireClass (section 8), and a material's reaction to fire is its fireReaction (section 4.1).
  4. A roof window has the values of the window type it names (section 6.3, roof.windows).

A room type's domain data, room (section 5), MAY also say:

  1. heated: whether its rooms are heated, true for above 10 °C, as a heated floor area such as BBR's Atemp counts them;
  2. temperature: the rooms' design indoor temperature in °C, from −50 to 60;
  3. use: what the rooms are used for, a plain string of at most 80 characters (section 8, rule 3), such as residential, office or a code of a national list of uses.

5.6 Slab edge and gutter sections

By the steward's decision of 26 October 2026 (open questions, 74 and 75), so that Part IV, 4.6 and 4.7 derive these elements' bodies from the records alone.

A slab edge type's edgeProfile gives its section: its shape, a rectangle, an L or a U; its width and depth; an L's or a U's web, the thickness of its upstands, and flange, the thickness of its bottom leg (a rectangle carries both, and they mean nothing); its anchor, slabTop or structureBottom, the height its section hangs from; and, where it has them, a facing (a board of a material and a thickness on its outer face, as deep as the section or its own depth), a beam (a width and a depth to which it thickens its host under its edge) and a chamfer (a width and a height cut from its upstand's top inner corner). A gutter type's gutterProfile gives its section: a halfRound of an inner diameter, or a box of an inner width and depth, both thickness thick; and its horizontalOffset and verticalOffset, which its elements' own add to (section 6.3).

  1. An L's or a U's flange MUST be less than its depth, and its web (a U's two webs together) less than its width, so that the section has an inside.
  2. A chamfer MUST be on an L or a U, its width at most the web and its height at most the depth less the flange: it is cut from the upstand, above the bottom leg.
  3. A halfRound's gutterProfile MUST give its diameter, and a box's its width and depth.

Reference: chamferProblem in src/core/elements/edgeChamfer.ts and the type checks in validateManifest (src/core/model/model.ts: "Profile web and flange must leave an open interior", "A half-round gutter needs a positive diameter", "A box gutter needs a positive width and depth").

6. Elements

An element is one placed thing of the building: a wall, a slab, a door. Every element has:

MemberMeaning
id, nameidentity and display name (non-empty)
categoryone of the thirty-one categories (section 6.2)
typeIdits type, of the same category
levelId, offsetits level and the offset from it (Part I, section 4)
height, widthpositive, at most 100 000 mm; their meaning depends on the category
profile1 to 500 plan points; its meaning depends on the category
revisionPart I, section 8

Everything else is optional. The members of section 6.3 belong to the categories it lists; the others that the element schema names, declaredGeometry (section 12.1) among them, may be on any element, though most mean something for a few categories only (section 6.3 describes some).

An element or a type imported from IFC may carry ifcSource, {globalId, entity, units?, properties?}: the GlobalId and the IFC class it was imported from, the units of its numbers, and the property sets it was imported with, {<set name>: {<property name>: value}}, each value a text, a number or a truth value. units has one value, "specification": every number of properties is in this specification's units, a length in millimetres and an angle in degrees (Part I, 3), any other measure in its SI unit. A writer that imports a file SHOULD convert its numbers so and say so with units. Where units is absent, the record does not say which units its numbers are in, as records written before 29 October 2026 do not, and a reader does not convert them. A bridge keeps the GlobalId (Part VIII, 1, rule 3) and prints the property sets back, typed by IFC4's templates where IFC4 defines the set, a number of a measure with a unit only where units says which units it is in, and under a name that does not start with Pset_ where it does not (Part VIII, 1, rule 5, and 1.5). Reading IFC (Part VIII, 2) adds the predefined type and the user-defined type an object was read with (ifcSource.predefinedType, objectType), and shape: "given" on an element whose shape is the body its reader gave rather than its members.

6.1 Element rules every reader needs

  1. An element's typeId MUST name a type of the same category, and its levelId a level.
  2. A wall's and a beam's profile MUST be exactly two points at least 10 mm apart in plan.
  3. A slab's, ceiling's, roof's or shaft's profile MUST be an outline of three points or more, with an area of at least 100 mm², no two consecutive points less than 0.1 mm apart, and no edge crossing or touching another. Two edges that follow one another share a point, and meet there without touching in this rule's sense; they MUST NOT meet anywhere else, as they do where an outline turns back along itself. A foundation's profile is such an outline, or, for a round footing type, one point: the centre.
  4. A room's profile is one point (its boundary is found from the walls) or an outline of three points or more drawn by hand; never two points. A room MUST carry room with its number, unique on its level. Its boundary found from the walls, its ceiling and its quantities are 0.2's, in Part IV, 7.5.
  5. A door or window MUST be hosted by a wall (hostId). It MUST fit its host: along ≥ 0, along + width ≤ the host's length, offset ≥ 0, and its top offset + height at most the host's height (or under the host's attached top where that rises higher). Where its top is over the host's height, each corner of its rectangle, or each point of its type's outline placed at the opening (Part IV, 4.3), MUST lie at or under the host's attached top at that point's distance along the host's centreline, the top that Part IV, 5.3, rules 4 and 5 derive. Two openings in one wall MUST NOT overlap. Two openings overlap when their shapes in the wall's elevation share an area: their rectangles, from along to along + width and from offset to offset + height, or, where a type gives an outline, that outline placed at its opening. Openings that only touch, along an edge or at a corner, do not overlap.
  6. A slab edge element MUST be hosted (hostId) by a slab or a foundation. A rebar element MUST be hosted by a slab, a foundation, a beam, a column, a slab edge element or a wall (section 6.1.1), and a mesh element by a slab, a foundation or a wall. A rebar element carries reinforcement or bars; a mesh element reinforcement or sheet. The rules that place reinforcement hold in its host's own frame, in a beam, a column or a slab edge element as in a slab. Where they name the host's thickness, a beam's, a column's or a slab edge element's is its section's depth across the plane the bars lie in. A slab edge element's hostEdgeId MUST name an edge of its host's outline (rule 10), the edge it runs along (Part IV, 4.6); a round footing has none. Where slab edge elements thicken their host (Part IV, 4.6, rule 9), the thickened edges MUST fit it, as Part IV, 4.6, rule 11 says.
  7. A stair's profile is two points (its start and a point up its first run, or for a circular stair its centre and its start), and it carries stair. Its numbers and the layout of a straight, L or U stair are 0.2's, in Part IV, 7.3.
  8. A component's profile is two distinct points (its insertion point and orientation) unless it is a cast-in item placed by insertAt.
  9. A room separator's and a property line's profile is two points or more.
  10. The outline's boundary edge ids (edgeIds) of a slab, foundation, ceiling, roof or shaft, when present, MUST be unique and one per profile point.
  11. A railing MUST meet the host and edge rules of section 12.2: it stands on a stair, on the outline of a slab or a shaft, or on a line of its own.
  12. A generic element's profile is one point, or two distinct points; it has no hostId, and its shape is declared geometry, its own or its type's (section 6.6).
  13. A gutter MUST be hosted (hostId) by a roof, and its hostEdgeId MUST name an edge of that roof's outline (rule 10), the edge it hangs on (Part IV, 4.7). Its profile is not used for placement.

The category schemas elements/<category>.schema.json encode the rules a schema can state (point counts, required members, members that do not belong to the category). The rest a validator checks in code.

Reference: validateElement in src/core/model/model.ts and the validators it calls; for rule 6's bar sets, validateBars and barHosts in src/core/elements/bars.ts.

6.1.1 A rebar element in a wall

Part of 0.3. OpenEik 0.2 had it as the proposal wallRebar (open questions, 122): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).

A wall hosts a rebar element as it hosts a mesh element: a cast wall's vertical and horizontal sets, its bars round an opening and its starter bars name the wall they lie in. In a model of 0.2 that does not ask for the proposal, a rebar element hosted by a wall is reported (host-category), as section 6.1, rule 6 says.

  1. A wall hosts a rebar element. A rebar element MAY be hosted (hostId) by a wall, beside the hosts section 6.1, rule 6 names. It carries reinforcement or bars, as in every host.
  2. A wall's own frame. The rules that place reinforcement hold in a wall's own frame, as they do in a slab's: its directed baseline (Part I, 5.1) from its start at its base, z0 = elev(levelId) + offset (Part IV, 4.1); x along the baseline, y across it to its left, the side of its first layer and its exterior (Part I, 5.1, rule 2), and z up. A bar set's origin, xAxis, yAxis and along are in the model's axes, as in every host.
  3. Its thickness. Where the rules that place reinforcement name the host's thickness, a wall's is its thickness: its type's layers' total, T, or its width where its type has no layers (Part IV, 4.1). Its concrete is the layers its type marks structure, or the whole wall where it marks none.
  4. The frame a set was written in. A bar set in a wall MAY give anchor, {at, dir}: the wall's frame its placement was written in, at the wall's start at its base and dir the unit direction of its baseline in plan. A writer that moves or turns the wall SHOULD carry such a set with it, turned about the vertical and moved from the anchor's frame to the wall's new one, and write the new frame as its anchor. A reader reads the placement as it is written.
  5. Its print. A bridge prints such a set as it prints one in its other hosts (Part VIII, 1): an IfcReinforcingBar.

Reference: barHosts, barAnchor, followedBars and wallBars (the set wall of rebar.quick) in src/core/elements/bars.ts. In @openeik/tools: hostsOf in packages/openeik/src/check.js, for a model of 0.3, and for one of 0.2 asked for with proposals: ["wallRebar"].

6.2 The categories

CategoryDiscipline folderProfileheight, widthNotes
wallarchitecturecentreline, 2 pointsheight above the base; thickness from the type's layers, else widthtop: section 6.4
slabarchitectureoutlinethickness from layers, else heighttop face at level + offset
foundationstructureoutline, or 1 point (round footing)as slab
ceilingarchitectureoutlineas slab
roofarchitectureoutlineas slab; pitched by roofPart IV, 4.4
beamstructureplan axis, 2 pointsdepth and width without a section; the section's otherwiseunderside at the start
columnstructurecentre, first pointlength; side of a square column without a sectionbase at level + offset
door, windowarchitecturenot used for placementthe opening's sizehosted by a wall
slabEdgestructurenot used for placementthe edge beam's sizehosted on a slab edge
rebar, meshstructureas their payload saysbar or sheet sizeshosted
componentarchitecture or structure (Part III)insertion point and orientationfrom the typeplates, brackets, bolts, welds, cast-in items
gutterarchitecturenot used for placementfrom the typehosted on a roof edge (hostId, hostEdgeId)
room, roomSeparatorarchitecturepoint or outline; lineroom heightno body
shaftarchitectureoutlineheight to its topopens the slabs, ceilings and roofs it passes, and a stair's shaft foundations too (Part IV, 4.2, rule 8); no body
stair, railingarchitecturestart and direction; line or host edgesfrom type and payloadderived runs and balusters
pipe, duct, terminalsystemsrun of points; a terminal's point, and the way it faces (section 6.9)sizes from type and payloada run's body: Part IV, 12; a component in a pipe or a duct is hosted on it (section 6.9)
terrain, grading, propertyLinesitepoints, outline, boundarysurfaces; no body
furniture, planting, road, parking, firearchitecturepoint or linefrom the type
genericarchitecture, or the one its generic.discipline namesinsertion point, or insertion point and orientationnot used: its shape is its declared geometrya thing no other category fits; never hosted (section 6.6)

A category that is not in this table is not one of 0.1's: an element or a type of such a category, one a later version adds, is kept as Part I, 10, rule 5 says. 0.2 adds member, a member between two points, which section 6.2.1 gives.

  1. A category MUST be a plain name: a lower-case ASCII letter, then ASCII letters and digits, at most 32 characters in all (^[a-z][A-Za-z0-9]*$), and no device name of a common operating system, con, prn, aux, nul, com0 to com9 or lpt0 to lpt9, in any case. The thirty-one are such names, in lower camel case (slabEdge), and so is every category a later version adds. elements/element.schema.json and types/type.schema.json state the rule, so a record whose category breaks it does not validate (Part III, 3, rule 2).

A category names a folder of a record's path (Part III, 4.2), and is not escaped (Part III, 2): rule 1 makes it a name that every common file system takes as it is written, as Part III, 2 makes a name of an id.

6.2.1 A member between two points

Part of 0.2. OpenEik 0.1 had it as the proposal member: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), an element or a type of the category member is of a category 0.1 does not have, which a reader keeps (Part I, 10, rule 5). The section adds the category member and one member of its elements, end, and no file: its elements and types are records of Part III, 4's layout.

By the steward's decision of 30 October 2026 (open questions, 110), asked by an analysis application: one rule for linear members in any direction, and a category for what is neither a beam nor a column. A beam is set out on its underside and needs a run in plan (section 6.1, rule 2; Part IV, 6.2), and a column stands plumb on its base (Part IV, 6.1), so neither records a brace, a strut or a tie that runs steeply, or plumb, from one point to another. A member's record gives the two ends of the line through the middle of its section, and Part IV, 6.7 builds it as Part IV, 6 builds a beam.

  1. The category. member is a linear structural member that is neither a beam nor a column: a brace, a strut, a tie, a member of a truss or of a frame, a raking post. Its discipline folder is structure (Part III, 4.1), and in the owners' layout it is structure's (Part III, 4.4, rule 2). Its type is of the category member.
  2. Its type MAY carry a section (section 5.2), as a beam's type does: its width runs across the member and its depth square to it, before the member's roll (Part IV, 6.7). It carries no other payload of section 5's table but those any type may carry; its schema sets each to false.
  3. Its line. Its profile is two points in plan, its start and its end, which MAY be one point, so that the member stands plumb. Its start is its profile's first point at elev(levelId) + offset, and its end its second point at elev(end.levelId) + end.offset: it MUST carry end, {levelId, offset}, whose levelId MUST name a level of the model (Part I, 7). The two points lie on the member's reference line, the line through the middle of its section (Part IV, 6.5), and MUST be at least 10 mm apart in space (member-length).
  4. Plumb. A member is plumb where its two plan points lie within 10⁻⁶ mm of each other. A writer SHOULD write a plumb member's two plan points equal, and a validator SHOULD warn of a member whose plan points lie less than 1 mm apart but not within 10⁻⁶ mm (member-plumb): its section turns with the direction from one to the other (Part IV, 6.7, rule 2), which so short a run in plan hardly sets.
  5. Its size. With a section, the section sizes the member (Part IV, 6.3): its height MUST equal section.depth, as a sectioned beam's does (section 5.3), and its width is not read. Without a section, its section is a rectangle width across and height deep.
  6. Its members. It MAY carry roll, taper and joins, which mean for it what they mean for a beam (section 6.3; Part IV, 6.4 and 6.7); a taper needs a rectangular section, as a beam's does. It MUST NOT carry endRise, turn, base, connection or another member that section 6.3 gives other categories: its schema sets each to false.
  7. What its record does not say. A member's record says where it is and what its section is. How its ends are supported, released or connected in an analysis is no member of it: an application keeps that in its own records, or in extension members of its own (Part I, 11.2), which a member carries as a beam does. Its joins say only where its body is cut (Part IV, 6.4).
  8. One element for one piece. A beam and a column keep their records and their meaning: a beam with an endRise and a column are read as in 0.1, and Part IV, 6.7, rule 9 reads either's reference line as a member's line. A writer writes each linear piece as one element, a beam, a column or a member, and never as two. Which is its choice: a beam is set out by its underside, a column stands on its base, and a member is set out by the middle of its section, in any direction.
  9. Commands. A command processor makes and changes a member and its type as any element and type (Part V, 1, rule 5), holding each to this section's schemas. It refuses an end whose levelId names no level (rule 3), and a height other than its type's section's depth (rule 5), as it refuses a top constraint's level and a beam's height. A member's end on a level reaches to it as a top constraint does: level.delete is refused while one does, unless moveTo names the level they move to, and the end moves to it keeping its height (Part V, 5, rule 4).

A brace of a square hollow section 100 × 100 × 5 from the foot of a column at [0, 0] on level 0, elevation 0, up to [6000, 0] 150 mm under level 1, elevation 3600, is {"category": "member", "profile": [[0, 0], [6000, 0]], "levelId": "level-0", "offset": 0, "end": {"levelId": "level-1", "offset": -150}, "width": 100, "height": 100, …}, its type {"category": "member", "section": {"shape": "RHS", "width": 100, "depth": 100, "web": 5, "flange": 5, "designation": "SHS 100×100×5", "grade": "S355"}, …}: its line runs from (0, 0, 0) to (6000, 0, 3450), through the middle of its section. A hanger plumb from [2000, 1000] at level 1 down to 900 mm under it has the profile [[2000, 1000], [2000, 1000]], the offset 0 and the end {"levelId": "level-1", "offset": -900}.

6.3 Members that belong to some categories

An element MUST NOT carry these members unless its category is listed. The element category schemas set each forbidden member to false.

MemberCategoriesMeaning
topwall, shaft, staira wall's top constraint and attachment (section 6.4); a shaft's or stair's top is only a constraint
wallOutline, edges, framingTop, framingJoints, framingCorners, framingStuds, framingTees, framingStart, framingOffsetwallan elevation outline, edge details, timber framing rules (laid out by Part IV, 7.4)
roomBoundingwall, columnfalse when the element does not bound rooms
turncolumnturn about its centre (its reference line, Part IV, 6.5) in degrees, counter-clockwise in plan
basecolumnbase attachment (Part IV, 5.2)
roll, endRise, taperbeam; roll and taper on a member too (section 6.2.1)roll about the reference line (Part IV, 6.5); rise of the end over the start; a linearly varying depth
connection, joinsbeam, column; joins on a member too (section 6.2.1)a schematic end plate; member joins (Part IV, 6.4)
bottomPlatedoor, windowwhether a framed wall's bottom plate runs through
roofroofslope-defining edges, overhangs, roof windows, the roofs it joins (joinGroup, below)
joints, panelsslab, foundation, ceilingmovement joints, panel division
edgeAttachmentsslab, foundation, ceiling, roofedges of the outline trimmed to a wall's face (section 6.7)
roomroomnumber, name, department, unit, finishes
stair, railingstair, railingshape and own numbers
meppipe, ductheights, size, system and insulation (section 6.9)
terminalterminalits flow and its heat (section 6.9)
barsrebara bar set
sheetmeshmesh sheets
terrain, grading, propertyLinetheir categoriespoints, pads, setbacks
genericgenericwhat the element stands for, its IFC class and its discipline, where they differ from its type's (section 6.6)
notchesslabEdgestretches where its outside upstand is cut down; of 0.2 (Part IV, 4.6, rules 12 to 14), and in 0.1 a member the schemas do not know

A panel division. panels, on a slab, a foundation or a ceiling, is {width, direction?, start?, prefix?}: a rule that divides the element into strips width mm wide (100 to 10 000), which a plan draws and numbers and a schedule counts. It is no geometry, and changes no body. The strips lie side by side along direction, in degrees counter-clockwise in plan from the model's x axis, 0 where it is absent: their widths add up along that direction from the point of the outline least far along it, moved on by start mm (0 where it is absent), and the lines between them run square to it. Each strip is labelled prefix (P where it is absent) followed by its number, counted from 1 along direction. So a floor of panels laid side by side along y, each spanning along x, has the direction 90.

Some members may be on any element, and mean something for a few categories only. Four of them, two members of the roof payload and the terrain payload's points mean this:

  • componentInverted, on a component: true hangs its body down from its insertion height, from z − height to z, where it otherwise stands up from it, from z to z + height (z is its level's elevation plus its offset). It changes nothing for a component placed upright, by axes of its own or as a cast-in item.
  • axes, on a component: {x, y}, two vectors in the model's axes, x not zero and y not parallel to it. They are the component's own frame, and take the place of its orientation and upright. Its x is x made a unit vector, its y is y made square to x and a unit vector, and its z is x × y, or y × x where the component is flipped. The frame's origin is the insertion point, the profile's first point, at its level's elevation plus its offset. A plate lies in its x–y plane: its outline is in x and y, and its thickness, the element's height, runs along z from the face, from 0 to height. A bolt group's bolts run along z, and a weld along x. With axes, the profile's second point, which rule 8 of section 6.1 still asks for, orients nothing, and componentInverted changes nothing.
  • miterEnds, on a slabEdge: true mitres both its ends where its host's outline turns, so that it meets the edge element round the corner on the corner's bisector, and false keeps both ends square. Without it, an end is mitred where the edge round that corner carries an edge element that mitres by itself, one whose miterEnds is not false and whose type is no framing family (section 5.4), and where its own type is no framing family either. Part IV, 4.6, rule 6 says where a mitre is cut, and where an end stays square all the same.
  • horizontalOffset, on a slabEdge or a gutter, in mm: moves its section square to its host's edge, a slab edge's in from the edge, into the host's outline, and a gutter's out from its roof's edge, added to its type's gutterProfile.horizontalOffset. A negative value moves it the other way. verticalOffset, on either, raises its section, a gutter's added to its type's gutterProfile.verticalOffset (Part IV, 4.6, rule 4, and 4.7, rule 4).
  • terrain.points, on a terrain: points in plan with a height, [x, y, z], z above its level's elevation plus its offset. Between them, the ground is the Delaunay triangulation of the points in plan, clipped to the element's outline (its profile): a triangulated irregular network, as LandXML's surfaces and IFC's IfcTriangulatedIrregularNetwork are. Where four or more points lie on one circle, any of the triangulations is the surface. Where two terrains overlap, which ground is on top is not defined in 0.1, since the order of elements has no meaning (Part III, 9, rule 3); a writer that wants one ground there clips the other terrain's outline.
  • roof.windows, on a roof: its roof windows, each {id, edgeId, center, width, length, openingOnly?, reveal?, typeId?}. A roof window lies on the face of the edge edgeId names (Part IV, 4.4), centred over center in plan, width across the slope and length along it. openingOnly makes it an opening with no window in it, and reveal says how the opening runs through the roof. A covering and the infill under it that cut one opening carry the same window id. typeId, by the steward's decision of 21 October 2026, names the window's type: its product, and its U, g, frame fraction and light transmittance (section 5.5). It MUST name a type of the category window where it names a type of the model, and SHOULD name a type of the model (Part I, 7, rule 3). It belongs on the entry that holds the window, not on an openingOnly one.
  • roof.joinGroup, on a roof: a name that the roofs of one group share. Roofs of one group join: where another roof of the group lies over a face, that part of the face is cut away, and a gutter along an edge that lies under another roof of the group is left out there.

A plate, a bolt group and a weld, components of a type of the family plate, bolt or weld (section 5), carry their own data in three members:

  • plate, on a plate: its own shape over its type's. Its outline, three or more points in its own x and y in mm, around its insertion point, makes it a contour plate of that outline in place of the rectangle its type gives, and its holes, each as a plate type's hole is (section 5), go through it beside its type's. The holes of the bolts through it come with their bolt group.
  • bolts, on a bolt group: its bolts, each the bolt its type holds (section 5), all along its z. positions are their centres in its x and y, in mm. grip is how far they pass through the parts along its z, from the face the head, or an anchor bolt's nut, bears on, at z = 0, to the face the nut bears on. through names the elements they pass through, each drilled for them, plates and members. length is a bolt's length from under its head, hole the holes' diameter, and embed an anchor bolt's reach past the grip into the concrete, or a rod's past the grip, its far nut included. Where a group gives no length, hole or embed, the reference implementation works them out from tables of ISO bolts and hole clearances that no record holds (open questions, 73).
  • weld, on a weld: a fillet weld, its root along its x for its width, its length, and its legs along its y and z. throat is its throat, a, in mm, and its legs a·√2. Its kind is fillet, the one kind of 0.1, where it gives none. site is true for a weld made on site rather than in the shop, and parts names the one or two elements it joins.

What componentInverted, axes, plate, bolts, weld and roof.joinGroup do to a roof's or a component's body is no class-A rule (Part IV, 2): how a compiler derives it is reference behaviour in 0.1, and a component's frame, a plate, a weld's fillet and each bolt's shank become rules in 0.2 (open questions, 73 and 99), which Part IV, 7.6 gives. What miterEnds, horizontalOffset and verticalOffset do to a slab edge element's or a gutter's body, and where a roof of its group buries a gutter, are class-A rules since 26 October 2026 (Part IV, 4.6 and 4.7; open questions, 74 and 75).

Reference: validateElement, validateSlabJoints, validateSlabPanels and slabPanels (src/core/elements/slabMarks.ts), validateRoof (roofs.ts), validateBars (bars.ts), validateSheet (meshSheets.ts), validateSiteElement (src/core/site/siteElements.ts), validateMemberJoins (memberJoins.ts), validateWallEdges (edgeDetails.ts), validatePlateType and validatePlateShape (components.ts), boltSpecSchema and validateBoltGroup (bolts.ts), validateWeld (welds.ts); for the four members above, plateFrame (components.ts), edgePlacement (edgePlacement.ts), gutterSolids and unburiedSegments (gutters.ts) and roofSolids (roofs.ts).

6.3.1 A slab on a sloped plane

Part of 0.2. OpenEik 0.1 had it as the proposal slopedSlab: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), plane is a member a schema of 0.1 does not know, which a reader keeps (Part I, 10), and the slab is level. The section adds this one member and no file.

By the steward's decision of 30 October 2026 (open questions, 111), asked by an analysis application: a slab may lie on a sloped plane, as a ramp, a sloped floor or deck, or a plate of an analysis model does. Its plane is given as a roof's slope-defining edge gives one, and Part IV, 4.2.1 builds it by the plane rule of a pitched roof (Part IV, 4.4), so that roofs and sloped slabs share one plane rule and one quantity rule.

  1. The plane. A slab MAY carry plane, the plane of its top face. It gives the plane's gradient one way and its direction one way:

    • its gradient as slope, in degrees from 0 to 75, as a roof edge's slope is (section 6.3); or as rise over run, both in mm, run more than 0, its slope atan(rise/run), which MUST NOT be more than 75 degrees;
    • its direction as edgeId, an edge of the slab's outline (Part I, 6, rule 6; section 6.1, rule 10), from whose line its top rises into the outline, as a roof's top rises from a slope-defining edge (Part IV, 4.4, rule 1); or as fall, the direction in plan in which its top falls, in degrees counter-clockwise from the model's x axis.

    It carries exactly one of slope and rise with run, and exactly one of edgeId and fall.

  2. Where it lies. The slab's elev(levelId) + offset, z1, is its top's height along the line of the edge edgeId names, or at its profile's first point where it gives fall. With σ its slope, its top at a point p in plan is z(p) = z1 + tan σ · (n · (p − a)), where a is the edge's start and n its unit normal into the outline, or z(p) = z1 − tan σ · (f · (p − p0)), where f = (cos fall, sin fall) and p0 is the profile's first point.

  3. Either form is one plane. Each gives the plane's slope, the direction in plan in which its top falls, −n or f, and its top's height at the profile's first point, z(p0). A reader derives these from either form, and so either form from the other where it can be written: an edge form where the outline has an edge square to the fall.

  4. Its top face. The plane is the slab's top face, as a level slab's top is elev(levelId) + offset (Part I, 4) and a pitched roof's planes are its top (Part IV, 4.4). Its thickness, its type's layers' total or else its height (Part IV, 4.2, rule 1), is measured square to the plane, as a roof's is (Part IV, 4.4, rule 4), and its layers lie under it, top first.

  5. A level slab has no plane, or one of slope 0: its top is z1 everywhere, and nothing of it changes.

  6. Which slabs. Only a slab carries plane. A validator reports plane on an element of another category, an edgeId that names no edge of the outline, and a rise over run steeper than 75 degrees (slab-plane). A slab whose plane slopes, of a slope over 0, MUST NOT host a slab edge element in 0.2 (slab-plane-edge; Part IV, 4.2.1, rule 6).

  7. Its grain. A sloped slab's main direction (section 5.1.1), from its panels' direction or its mainDirection, is a direction in plan projected vertically onto its plane, as a roof's mainDirection is (section 5.1.1, rule 4). A panel division (section 6.3) stays one in plan.

A ramp 6000 mm long and 2000 mm wide, [[0, 0], [6000, 0], [6000, 2000], [0, 2000]] on level 0, elevation 0, with the offset 0, rising 300 mm from its west edge, its fourth, ramp-a:edge:3, to its east edge, has "plane": {"edgeId": "ramp-a:edge:3", "rise": 300, "run": 6000}: its top is z = x/20, from 0 at x = 0 to 300 at x = 6000. The same plane by its fall is {"fall": 180, "rise": 300, "run": 6000}, since the profile's first point lies on the low edge. A triangular landing whose fall runs square to none of its edges has only the second form.

6.4 A wall's top

top is {constraint, attach?, extendExteriorLayers?, cutRoofId?, cutRoofFace?}:

  • constraint is "unconnected" (the top is base + height) or {levelId, offset} (the top is that level's elevation plus offset, which MUST lie above the base).
  • attach is {hostId, face, offset?, mode?}: the wall's top conforms to a face of a roof, floor, foundation or ceiling: its "underside", its "top", or {layerId}, the underside of one of the host's layers. mode is "raking" (the default: the top follows the face) or "level" (flat at the lowest point of the face over the wall's core).
  • height and wallOutline keep their unconnected values while a wall is attached.

The derivation is in Part IV, section 5.3.

Writers MUST NOT write the legacy members topRoofId, topCeilingId and roofCutId. A reader SHOULD read them as top (topRoofId and topCeilingId as an attachment to that host's underside, roofCutId as cutRoofId).

Reference: wallTopSchema, legacyWallTop in src/core/elements/wallTop.ts; normalizeElement in src/core/elements/edges.ts folds the legacy members into top on load; run refuses them in patches.

6.5 Hosted openings

A door or window names its wall in hostId. along is the distance in plan from the wall's first profile point to the opening's near edge; width and height are its size; offset is the height of its bottom above the wall's base. flipped turns it to face its wall's interior rather than its exterior (section 6.8), and handFlipped mirrors its handing, and the shape its type's outline gives it, along its wall (Part IV, 4.3).

How it opens. By the steward's decision of 26 October 2026 (open questions, 71), as the reference implementation reads these members. A door's or a window's type with an openingFamily (section 5) says how its leaves or its lights open: operable whether they open, hinge the side they hinge on, swing the side they open to, and, for a door, operation how a leaf moves. With the element's flipped and handFlipped:

  1. The hinge side. A leaf or a light hinges at the end of its opening nearer its wall's first point, its start, at along, where hinge is left, and at its other end, at along + width, where it is right; handFlipped swaps the two. top and bottom hinge a window's light at its head or its sill, whatever handFlipped says.
  2. The side it opens to. It opens to its wall's exterior, the left of the wall's baseline (Part I, 5.1), where swing is outward, and to its interior where swing is inward; flipped swaps the two.
  3. Several leaves or lights. A door of the kind door has lightsX leaves side by side, and one of the kind garageSideHinged its panels. Of n leaves counted from the opening's start, leaf i (from 0) hinges at the start where i < n/2 and at the end otherwise, whatever hinge and handFlipped say. A window's lights stand in its grid, lightsX across and lightsY up, or as its mullions divide it; where they hinge at a side and stand in more than one column, column i of n hinges at the start where i < n/2 and at the end otherwise.
  4. How a door's leaf moves, its operation, on a family of the kind door: swing, or none, turns it on its hinges to the side it opens to. slide runs it along its wall: a single leaf hangs on the face of the wall on the side it opens to and travels its own width towards its hinge side; of several leaves, counted from the hinge side, the first stays, the second slides over it towards the hinge side, the third stays, and so on, on two tracks 6 mm apart, each leaf that slides on the track on the side the door opens to. pocket slides a leaf into the wall on its hinge side, and parts several leaves, each into the wall at the end of its own half (rule 3).
  5. A family that does not open. Where operable is false, its leaves and its lights do not open: rules 1 to 4 say nothing of it.
  6. A family of the kind door or garageSideHinged SHOULD hinge left or right: one that hinges at its top or bottom has no operation in 0.1. operation SHOULD be slide or pocket only on a family of the kind door, and a reader ignores it on another. A validator SHOULD warn of both.

A garage door of the kind garageSectional runs up in its sections, one of the kind garageRoller rolls up, and one of the kind garageFolding folds its panels to both ends; hinge and operation say nothing of them. Part VIII, 1.2 maps each operation to IFC4's. The frame, the leaves or sashes and the glass become class-A bodies in 0.2 (Part IV, 4.3 and 4.3.1); a body drawn for a print, a clash check or a quantity is then closed, open, which the element schema allows, being a pose for showing.

Reference: openingSolids (hinge, swingToward) and pocketSide in src/core/elements/openings.ts; slidingLeaves, leafSlides and slideSide in src/core/elements/slidingDoors.ts; lightSpans in src/core/elements/mullions.ts.

6.6 Generic elements

The category generic is for a thing that no other category fits: a piece of equipment, a lift, a sculpture, signage, temporary works, a scanned part of an existing building. It is a disciplined way out: a thing that a category fits is modelled in that category, and nothing is made to look like something else, a box component or a piece of furniture standing in for a lift.

  1. What it stands for. A generic type MUST carry the payload generic, {standsFor, ifcClass?, discipline?}:

    • standsFor, required: a non-empty text that says what the thing is (passenger lift, sculpture, scaffold tower);
    • ifcClass: the IFC entity a bridge exports it as, written as IFC writes it (IfcTransportElement); absent, IfcBuildingElementProxy;
    • discipline: the discipline folder it goes in, one of architecture, structure, systems and site (Part III, 4.1); absent, architecture.

    A generic element MAY carry generic too, with any of the three members: each member it gives wins over its type's.

  2. Its shape is declared geometry, and nothing else: its type's declaration, in the frame of a category placed at a point, its own, or both, under the rules of section 12.1. A generic element MUST have declared geometry, its own or its type's, so it has at least one body, and its declaration is authoritative for its envelope. OpenEik defines no other geometry for it.

  3. Its placement. Its profile is its insertion point, or its insertion point and a second, distinct point that gives its orientation, as a component's (section 6.1, rule 8); with one point, its x axis is the project's. elev(levelId) + offset is its base, z = 0 of its frame (Part I, 4). Its width and height are not used for its shape.

  4. Not hosted, cutting nothing. A generic element has no hostId, and in 0.1 it cuts no other element.

  5. A category that fits comes first. Writers and agents SHOULD use a category that fits before generic. A validator MAY warn when a generic element looks like an element of a category that fits (as the model-quality warnings of Conformance, 2 do for other categories), and an agent host SHOULD say how many of a model's elements are generic.

  6. Its size. A record's declared geometry SHOULD hold fewer than 10 000 faces in all, counting each face of a polyhedron or a mesh, n + 2 for a prism of n points, and 3 for a cylinder or a rod. Larger meshes, kept as binary assets beside the records, are planned and are not part of 0.1.

  7. Promotion. When a category comes to fit (a later version adds one, or the thing is understood), the element moves to it by a batch of a delete and a create with the same id (Part V, 7): each command sees the model as the one before it left it, so the id is kept and no other command kind is needed. As any delete does, the delete removes what depends on the element (Part V, 5).

  8. From 0.2 a generic type or element MAY also carry classification references (section 11).

  9. A person. A figure of a person placed in a model, to show its scale or to check the free height over a place, is a generic element whose type stands for a person: the type's generic.standsFor is person, and its shape is declared geometry (rule 2), a body as tall as the person it stands for. There is no category person (decided on 30 October 2026; open questions, section 4, D9). How an application draws such a figure, a model of a body or a pose, is its own, in an extension member (Part I, 11.2).

A generic type standing for a passenger lift, and an element of it (compacted; on disk each member is a line):

{
  "category": "generic", "color": "#8a9199", "height": 2600, "id": "type-lift-8",
  "material": "Steel", "name": "Passenger lift, 8 persons", "width": 1600,
  "declaredGeometry": {
    "authoritativeFor": ["envelope", "clearances"],
    "clearances": [{"body": {"bottom": 0, "kind": "prism", "profile": [[0, -1500], [1600, -1500], [1600, 0], [0, 0]], "top": 2100}, "id": "landing", "purpose": "operation"}],
    "envelope": [{"bottom": -1200, "kind": "prism", "profile": [[0, 0], [1600, 0], [1600, 2100], [0, 2100]], "top": 3500}],
    "level": "interface",
    "tolerances": {"default": 10}
  },
  "generic": {"discipline": "systems", "ifcClass": "IfcTransportElement", "standsFor": "passenger lift"}
}
{
  "category": "generic", "height": 2600, "id": "lift-a", "levelId": "level-0", "name": "Lift A",
  "offset": 0, "profile": [[4000, 2000], [4000, 3000]], "revision": 0, "typeId": "type-lift-8", "width": 1600
}

The element stands at [4000, 2000] on level 0, its x axis pointing along +y; the type's envelope reaches 1200 mm into the pit below its base, and the landing in front of its doors must stay free. The type goes in types/systems/generic/type-lift-8.json, the element in systems/level-0/generic/lift-a.json.

6.7 Outline edges trimmed to a wall's face

edgeAttachments is a list of {edgeId, hostId, face}. Each entry trims an edge of a slab's, foundation's, ceiling's or roof's outline to a face of a wall:

  • edgeId names the edge. Edge n runs from the outline's point n to the next; it is named by edgeIds[n], or <id>:edge:<n> where the element has no edgeIds (Part I, 6.6).
  • hostId names the wall.
  • face is "interior" or "exterior": the exterior face is on the left of the wall's directed baseline (Part I, 5.1), the interior face on its right.

The outline stays as it is recorded. The trim is derived (Part IV, 4.5), so the edge follows the wall when the wall moves, turns or changes thickness, and neither record changes.

  1. Each edgeId MUST name an edge of the element's outline, and an edge MUST NOT be named twice. A round footing (section 6.1, rule 3) has no edges to name.
  2. hostId MUST name a wall.
  3. The edge MUST be parallel to the wall's baseline within 5°.
  4. Each end of the edge MUST lie no more than 1 mm beyond the face, on the side away from the wall. An attachment trims an edge back to the face; it never moves an edge out to meet it.
  5. The outline trimmed at every face its attachments name (Part IV, 4.5) MUST keep an area of at least 100 mm², as an outline must (section 6.1, rule 3).

A ground slab whose south edge is trimmed to the interior face of the south wall (compacted):

{
  "category": "slab", "edgeAttachments": [{"edgeId": "slab-ground:edge:0", "face": "interior", "hostId": "wall-south"}],
  "height": 250, "id": "slab-ground", "levelId": "level-0", "name": "Ground floor", "offset": 0,
  "profile": [[0, 0], [10000, 0], [10000, 7000], [0, 7000]], "revision": 1, "typeId": "type-slab-250", "width": 250
}

The wall runs from [10000, 0] to [0, 0], so its interior face is on its right, the north side; its type is 300 mm thick, so the face is the line y = 150. The slab's edge 0, from [0, 0] to [10000, 0], lies on the wall's centreline. The slab's bodies start at the face and measure 10 m by 6.85 m in plan. A wall 400 mm thick moves the face to y = 200, and one 200 mm thick to y = 100; the slab follows, and neither record changes.

Reference: edgeAttachmentSchema, validateEdgeAttachments and footprintTrims in src/core/elements/wallTop.ts; validateFooting in src/core/elements/footings.ts; the edge.attach and wall.flip branches of run in src/core/model/store.ts.

6.8 Which way an element faces

By the steward's decision of 21 October 2026. An energy balance, a daylight study or a sun study sums walls, doors and windows by the way they face: their facing, a direction in plan, and its bearing, that direction's angle from true north.

  1. A wall faces its exterior: the left of its directed baseline (Part I, 5.1). Its facing is its left normal n = (−u_y, u_x), where u is the baseline's direction (Part IV, 3).
  2. A door or a window faces its host wall's exterior, n. With flipped: true it faces the wall's interior, −n.
  3. A roof window faces down the face it lies on, the plane of the edge edgeId names (Part IV, 4.4, rule 1). Its facing is that edge's normal out of the roof's outline, −n_i, and its tilt is the edge's slope σ_i. Where that edge defines no slope, the window lies level: its tilt is 0, it faces straight up, and it has no bearing.
  4. A facing f has the bearing (atan2(f_x, f_y) · 180/π − trueNorth) mod 360: its angle in degrees, from 0 to less than 360, clockwise from true north, where trueNorth is the project's site.trueNorth, true north's angle clockwise from plan +Y (section 2).
  5. Without site.trueNorth, plan +Y is north: bearings are taken with trueNorth 0. A validator SHOULD warn when such a model holds a door, a window or a roof window, whose bearings then rest on it.

In the one-storey house, the south wall runs from [10000, 0] to [0, 0]: its facing is (0, −1), south in plan. Its site gives trueNorth 15, so the wall's bearing is 180 − 15 = 165°, and so is the bearing of each of its windows that is not flipped.

Reference: exteriorFacing = !e.flipped in src/core/elements/openings.ts; facadeInWalls in src/core/elements/wallFacing.ts; roofFaces in src/core/elements/roofs.ts.

6.9 Heating, ventilation and plumbing

By the steward's decisions of 23 and 24 October 2026, from the reference implementation's plan 606 (open questions 45 to 51). Pipes, ducts and terminals (section 6.2) are the building services of a model. Their systems, a run's insulation, a heating terminal's heat and the components in a pipe or a duct are data of their records; their views and drawings are section 13's, and their bodies Part IV, 12's.

Systems. A run's mep.system, a type's pipe.system, duct.system and terminal.system, and the system of a terminal type's connection or outlet are systems of one enumeration:

SystemWhat it carriesFalls by gravityCarries air
sewagewaste wateryesno
stormstorm wateryesno
greaseWastewaste water that carries grease, from a kitchenyesno
oilWastewaste water that carries oil, from a garage or a workshopyesno
coldWaterdomestic cold waternono
hotWaterdomestic hot waternono
hotWaterCirculationdomestic hot water's circulationnono
heatingheating waternono
districtHeatingthe primary water of district heating, supply and return as one systemnono
brinebrine, for cooling or a heat pump's collectornono
compressedAircompressed airnono
gasfuel gasnono
supplyAirsupply airnoyes
extractAirextract airnoyes
outdoorAiroutdoor airnoyes
exhaustAirexhaust airnoyes
kitchenExhausta kitchen hood's exhaustnoyes

A run's mep.number, 1 to 99 and 1 when absent, is its system's number on the drawings: the heating system 20, which Swedish drawings write VS20. A system's code on drawings (KV for cold water, VS for heating, L for compressed air), its name and its colour are not data of the standard (section 8, rule 3): an application gives them, and a project may give its own in its standards, an application value (section 9.2). Whether a system falls by gravity, or carries air, is the system's, as the table says.

Insulation. A run's insulation is a code that a drawing writes after the run's size, and a thickness in mm that its body takes (Part IV, 12.1, rule 3). A pipe type and a duct type give them alike:

MemberMeaning
pipe.insulationCode, duct.insulationCodethe type's insulation code, 1 to 12 characters, a national value: R5DF40-C on a Swedish pipe drawing (class R5, hanging D, material F, 40 mm, surface C), B11 for a duct's fire insulation of class EI15
pipe.insulation, duct.insulationthe insulation's thickness in mm, from 0 to 200

A run's own mep.insulation gives its own, where it differs from its type's, each member optional:

MemberMeaning
codeits insulation code, as a type's
thicknessits insulation's thickness in mm, from 0 to 200, beside its own code
nonetrue: the run has no insulation, whatever its type gives
btideprecated (rule 4): a pipe's code by its parts

A run's insulation code is its own code, else its type's insulationCode; its thickness its own thickness, else its type's insulation, and 0 without either; with none it has none. What a code means, its kind, its text and a thickness, is the project's legend, an application value: a run's thickness is the model's, never only a code table's.

A code by its parts, deprecated. The reference implementation writes a pipe's code by the parts of one national code, a type's pipe.bti and a run's mep.insulation.bti: {class, hanging, material, thickness, surface?}, each part 1 to 8 characters and a national value, the thickness in mm from 1 to 200. {"class": "R5", "hanging": "D", "material": "F", "surface": "C", "thickness": 40} is the code R5DF40-C and 40 mm. 0.1 reads it, deprecated (Part I, 9.6): rule 4.

The heating pipe of this section's examples, a copper pipe on level 0, 22 mm, in the heating system 20, insulated with 30 mm of the code R4SF30:

{
  "category": "pipe",
  "height": 22,
  "id": "pipe-heating",
  "levelId": "level-0",
  "mep": {
    "insulation": {
      "code": "R4SF30",
      "thickness": 30
    },
    "number": 20,
    "size": 22,
    "system": "heating",
    "z": [2400, 2400, 2400]
  },
  "name": "Heating supply",
  "offset": 0,
  "profile": [
    [1000, 1000],
    [5000, 1000],
    [5000, 4000]
  ],
  "revision": 0,
  "typeId": "type-pipe-heating-cu",
  "width": 22
}

Heat. A heating terminal, a radiator, a towel rail or a unit heater, has a power, and so a water flow at its design temperature difference (Part IV, 12.7). A terminal type's terminal.power is its power in W, from 1 to 500 000: what its elements give, unless one gives its own. A terminal's own terminal, beside its flow, gives:

MemberMeaning
powerits power in W, from 1 to 500 000, where it differs from its type's
deltaTits design temperature difference in K, from 1 to 80: the difference between its supply and its return water at which its flow is worked out
kvits valve's flow coefficient, kv, in m³/h at a pressure drop of 1 bar, from 0 to 1000
presetits valve's presetting, as its maker writes it, 1 to 12 characters, a plain value

A terminal's power is its own terminal.power, else its type's. A unit heater, a fan that blows air over a heating coil, is a terminal type of the kind unitHeater. A heating system's supply and return temperatures, from which an application takes a terminal's design difference where the terminal gives none, are the project's standards, an application value (open question 47).

The unit heater of a workshop, 3500 W at a design difference of 15 K; its type gives 3900 W:

{
  "category": "terminal",
  "height": 500,
  "id": "unit-heater-a",
  "levelId": "level-0",
  "name": "Unit heater",
  "offset": 2500,
  "profile": [
    [6000, 3000],
    [6000, 2000]
  ],
  "revision": 0,
  "terminal": {
    "deltaT": 15,
    "power": 3500
  },
  "typeId": "type-unit-heater",
  "width": 600
}

Where a terminal stands. A terminal's profile is its point in plan, and, where it gives a second point, the way it faces: from the first point toward the second. A terminal whose profile is one point faces plan +y. Its offset is the height of its base over its level (Part I, 4): a terminal stands from there up, whatever its mount, as the reference implementation places it (a ceiling outlet's base is where it hangs from the ceiling, a duct part's that names no duct half its height under the duct's axis). Its body is its declared geometry, its own or its type's, where it has one, else a box of its type's size (Part IV, 12.6). A component in a pipe or a duct stands where its run puts it instead (below).

Components in a pipe or a duct. A component is a valve, a pump, a strainer, a meter, an expansion vessel, a backflow preventer or an air vent in a pipe, or a damper, a silencer, a fan or a flow regulator in a duct: a terminal whose type's terminal.mount is pipe or duct, hosted on its run, the pipe or the duct it is in, at a distance along it. It follows its run. It has no connections of its own: the run stays one run through it, for its joins, its body and its quantities (Part IV, 12), and for what is derived from them. A component in a pipe is of one of these kinds, or a waterMeter:

KindThe component
isolatingValvean isolating valve, such as a ball valve
checkValvea check valve
balancingValvea balancing valve, which measures its flow and is preset
controlValvea control valve with its actuator
safetyValvea safety valve
pumpa circulation pump
strainera strainer
energyMetera heat or energy meter
expansionVesselan expansion vessel
backflowPreventera backflow preventer
airVentan air vent

A component in a duct, by the steward's decision of 24 October 2026 (open question 51), is of one of these kinds, which Part VIII, 1.1 exports as the IFC4 classes the table gives:

KindThe componentIFC4: class, predefined type
dampera damper that controls or shuts off the air flowIfcDamper, CONTROLDAMPER
fireDampera fire damperIfcDamper, FIREDAMPER
smokeDampera smoke damperIfcDamper, SMOKEDAMPER
flowRegulatora flow regulator, which holds the air flow through it at the flow it is set toIfcDamper, BALANCINGDAMPER
silencera silencerIfcDuctSilencer, ROUND in a round duct and RECTANGULAR in a rectangular one
ductFana fan in the ductIfcFan, USERDEFINED, its ObjectType INLINE

The kinds may have other mounts too: a pump on a floor is a pump mounted on the floor, and stands where its profile puts it, as any terminal does. A component in a pipe is its type's terminal.width across the pipe, terminal.depth along it and terminal.height up; a component in a duct is its size across (below) across the duct and up, and its type's terminal.depth along it (Part IV, 12.6). A component's type's code is the code its elements are tagged with, unless an element gives its own mark. Its system is not read: a component's system is its run's, with the run's number.

MemberMeaning
hostIdits run: its pipe, or its duct (rule 6)
alongits distance from its run's first point, in mm, along the run's points in 3D: a vertical piece counts, as in the run's length (Part IV, 12.4, rule 2). At least 0; a component with no along is at 0
levelId, profile, offsetwritten from its run (below)
width, heightin a pipe, its type's; in a duct, its size across, written from its duct (below)
markoptional: its own tag, such as V201
terminaloptional: a balancing valve's kv or preset

Where a component stands. Let the run's points be P_0 … P_(m−1) (Part IV, 12.1, rule 1), and s = min(along, L), where L is the run's length. The component's point is the point at the distance s from P_0 along the run's points: on the piece where it falls, at the share s′ / l of the piece from its start, s′ the distance left and l the piece's length. Its way is the direction in plan of that piece; on a vertical piece, the way of the last piece before it that runs more than 1 mm in plan; and +y where none does. Its levelId is its run's. Its profile is two points: its point in plan, and the point 1000 mm from it along its way. Its offset is its point's height above that level less half its body's height, its type's terminal.height in a pipe and its size across up in a duct, so that its body is centred on the run's axis. The run and along are the intent; the three are the writer's copy of where they put it, as a hosted railing's line is (section 12.2, rule 4).

Its size across in a duct. A component in a duct is at least as large across as its duct, and stands 10 mm proud of it on each side, as a casing does. Let W and H be the duct's outer width and height, its size with its insulation (Part IV, 12.1, rule 3; a round duct is W across both ways). The component's size across is the larger of its type's terminal.width and W + 20 wide, and the larger of its type's terminal.height and H + 20 high, in mm. So one damper type fits a duct of any size, and a type that is larger, a silencer's casing or a fan's box, keeps its own size. Its length along the duct is its type's terminal.depth. The duct and the two types are the intent; the component's width and height are the writer's copy of its size across, as its place is.

When its run changes, a component keeps its along, and stands where the changed run puts it: a writer rewrites its levelId, profile and offset in the same command, and in a duct its width and height (rule 8). When the run gets shorter than its along, it stands at the run's end, and its along stays as it was: the run lengthened again puts it back. A command that moves a component's point and leaves its along slides it along its run. When its run is deleted, it is deleted with it, as every hosted element is (Part V, 5, rule 1).

A duct part that names no duct. A terminal whose type mounts on a duct and that gives no hostId is a terminal like any other: it stands where its profile and offset put it, its body is its type's box from its base up (Part IV, 12.6), and it does not follow a duct. The reference implementation placed its silencers, fans and fire dampers so before its work 676, and leaves them where they were put: such a model stays valid, and a writer hosts one by giving it a hostId and an along. Which systems it goes with in a view is section 13.1's.

A valve 2000 mm along the heating pipe above, on its first piece, which runs +x at 2400 mm above level 0; its type is 70 mm high:

{
  "along": 2000,
  "category": "terminal",
  "height": 70,
  "hostId": "pipe-heating",
  "id": "valve-a",
  "levelId": "level-0",
  "mark": "V201",
  "name": "Isolating valve",
  "offset": 2365,
  "profile": [
    [3000, 1000],
    [4000, 1000]
  ],
  "revision": 0,
  "typeId": "type-isolating-valve",
  "width": 60
}

Its type:

{
  "category": "terminal",
  "color": "#b08d57",
  "height": 70,
  "id": "type-isolating-valve",
  "material": "Brass",
  "name": "Isolating valve",
  "revision": 0,
  "terminal": {
    "code": "V101",
    "depth": 80,
    "height": 70,
    "kind": "isolatingValve",
    "mount": "pipe",
    "width": 60
  },
  "width": 60
}

A damper 1500 mm along a rectangular supply air duct, duct-supply, 400 mm wide and 200 mm high without insulation, whose first piece runs +x from [1000, 5000] at 2700 mm above level 0. Its type is 145 mm across and 145 mm high, smaller than the duct, so its size across is the duct's and 10 mm each side, 420 mm by 220 mm, and its base is 110 mm under the duct's axis:

{
  "along": 1500,
  "category": "terminal",
  "height": 220,
  "hostId": "duct-supply",
  "id": "damper-a",
  "levelId": "level-0",
  "mark": "D201",
  "name": "Damper",
  "offset": 2590,
  "profile": [
    [2500, 5000],
    [3500, 5000]
  ],
  "revision": 0,
  "typeId": "type-damper",
  "width": 420
}

Its type:

{
  "category": "terminal",
  "color": "#9aa0a6",
  "height": 145,
  "id": "type-damper",
  "material": "Steel",
  "name": "Damper",
  "revision": 0,
  "terminal": {
    "code": "D101",
    "depth": 200,
    "height": 145,
    "kind": "damper",
    "mount": "duct",
    "width": 145
  },
  "width": 145
}
  1. A run's mep.system, a type's pipe.system, duct.system and terminal.system, and the system of a terminal type's connection or outlet MUST be a system of the table above. The schemas state this rule.
  2. An insulation code, a type's insulationCode or a run's code, and each part of a bti are national values: a validator MUST NOT refuse one because a code list does not hold it (section 8, rule 4). The schemas mark them "x-eik-values": "national".
  3. A run's mep.insulation that gives none SHOULD give nothing else. A duct's SHOULD NOT give bti. A run that gives its own code SHOULD give its own thickness.
  4. bti is deprecated (Part I, 9.6): a writer SHOULD NOT write it, and SHOULD write the code and the thickness instead; a writer that does removes the bti. A bti MUST give class, hanging, material and thickness. A reader reads one as a code and a thickness: its thickness is the thickness, and its parts written together are the code: class, hanging, material, the thickness in mm as a whole number, and then - and surface where it has one. Where a type or a run gives a bti, it stands in place of the code and the thickness beside it, as the reference implementation reads it. A validator SHOULD warn about each bti a model holds, naming the code and the thickness to write instead.
  5. terminal.power, terminal.deltaT, terminal.kv and terminal.preset MUST be within the ranges of the tables above. The schemas state this rule.
  6. A terminal whose type's terminal.mount is pipe MUST name a pipe of the model in hostId. A terminal whose type's terminal.mount is duct and that gives a hostId MUST name a duct of the model there; one that gives none is a duct part that names no duct (above).
  7. A component's along SHOULD be at most its run's length. A component whose along is past its run's end stands at the end.
  8. A component's levelId, profile and offset SHOULD be where its run and along put it: its level the run's, its point within 1 mm in plan, its way within 1°, its offset within 1 mm. The width and height of a component in a duct SHOULD be its size across, each within 1 mm. A writer rewrites them in the same command whenever the run, along or the component's type changes. A reader MUST follow the run and along, and the size across, where they disagree.
  9. A terminal type whose terminal.mount is pipe or duct SHOULD NOT have connections or outlets: a component is in its run, which stays one run through it, and no run's end joins it.

Reference: mepSystems and systemIds in src/core/elements/mepSystemList.ts (Stomra work 610); btiSchema and runInsulationSchema in src/core/elements/mepInsulationSchema.ts, and runInsulation in src/core/elements/mepInsulation.ts (615); terminalHeatTypeFields and terminalHeatFields in src/core/elements/mepHeatSchema.ts (616); onPipe, onDuct, alongRun, alongOf, componentBody (the size across, with DUCT_COLLAR), placedOnRun and followedRunComponents in src/core/elements/runComponents.ts, which was pipeComponents.ts (633, 676); componentMount and componentHost in src/core/elements/componentHost.ts (676); validateTerminal (rule 6), runOuter and terminalSystem in src/core/elements/mep.ts; and the types in src/core/elements/mepComponentPresets.ts and mepPresets.ts (633, 676). Plan 606 was built in five waves, on Stomra's main from 21 to 22 October 2026 (merge requests !240, !244, !249, !260 and !280); this text was written from its main at 43384047. Components in a duct are its work 676, live since 24 October 2026 (main f02ba07e); their text was written from its main at b7318113.

6.10 Provisions for voids

Part of 0.2. OpenEik 0.1 had it as the proposal provision: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a reader keeps a record of the category provision as a later version's (Part I, 10, rule 5). The section adds a category, provision, and the member provision of its elements, and no file; schemas/0.2/elements/provision.schema.json and schemas/0.2/types/provision.schema.json hold their schemas.

By the steward's decision of 30 October 2026 (open questions, section 4, D9; question 85, point 4), from the reference implementation's requests for openings (its work 670, step 6.1). A run that crosses an element another discipline owns, a duct through a wall, a pipe through a slab, a cable tray through a beam, needs a void there, which the element's owner makes. A provision records that need as a record of its own: its asker places it without changing the element, and the element's owner answers it. IFC calls it a provision for void.

A provision is an element of the category provision. Besides the members every element has (section 6), it carries provision:

MemberMeaning
hostIdrequiredthe element it goes through, its host (rule 3)
axisrequired[x, y, z], a unit vector: the way it runs through its host
uprequired[x, y, z], a unit vector square to axis: the way its height is measured
depthrequiredmore than 0, at most 20 000 mm: its length along axis
roundoptionaltrue for a round void of diameter width; absent or false, a rectangle
forIdsoptional1 to 20 element ids: the runs it is for (rule 4)
clearanceoptional0 to 1000 mm: the space kept round its runs on each side, which its size includes
noteoptionalat most 400 characters: what it is for, in words for people (Supply air 400 × 200)
answeroptionalits host's owner's answer (rule 5); absent, it waits for one

answer is {state, by, at, discipline?, text?, cut?}:

MemberMeaning
staterequiredcut, the void is made; or declined, it is not
byrequired1 to 80 characters: who answered, as the project names them
atrequiredwhen, an RFC 3339 date and time in UTC (2026-10-30T09:15:00Z)
disciplineoptionalthe discipline they answered for, one of the eight of Part XVI, 2, rule 4
textrequired where state is declined1 to 1000 characters: why, in a sentence; optional where it is cut
cutrequired where state is cut1 to 20 entries, what the cut made: {"recess": "<id>"}, a recess of its host's recesses by its id, or {"element": "<id>"}, an element the cut made, such as an opening's own record; none where state is declined
  1. Its place and its frame. Its profile is one point, its centre in plan; its centre stands at its level's elevation plus its offset (Part I, 4). axis and up are unit vectors, their lengths within 10⁻⁶ of 1, and square to each other, their product within 10⁻⁶ of 0. With across, up × axis, they are its frame: across, up and axis as x, y and z, right-handed. A validator MUST report a provision whose axis or up is no unit vector, or whose up is not square to its axis, as an error (provision-frame).
  2. Its shape and its body. A rectangular provision is width along across by height along up, centred on its centre; a round one is a circle of diameter width, and its height is not read. It runs depth along axis, half each way from its centre. Its body is that prism: a box, or a cylinder from its centre less axis × depth/2 to its centre plus axis × depth/2.
  3. Its host. hostId names a wall, a slab, a foundation, a ceiling, a roof, a beam or a column. A validator MUST report a host of another category as an error (provision-host). A host that is not in the model is a reference that does not resolve (Part I, 7, rule 3), which a validator SHOULD warn of: the provision waits for its owner to remove it.
  4. Its asker. forIds names the runs it is for, each a pipe, a duct or a cable carrier (Part XIII, 5). A validator MUST report an element of another category as an error (provision-run), and SHOULD warn of one that is not in the model. A provision is its asker's: in the owners' layout its owner is its owner, else the owner of the run its forIds names first, else setup (Part III, 4.4, rule 2). From 0.4, forIds may also name a terminal, or a component on a pipe or a duct, which section 6.10.1 says.
  5. Its answer is its host's owner's to give, and nothing else of the provision is theirs. cut says that the host's owner made the void with the host's own records, and answer.cut names what the cut made: a recess of the host, or an element. declined says that they did not, and text says why. A writer gives an answer once, and changes or removes it only for the host's owner. A validator MUST report an answer cut without cut, and an answer declined without text or with cut, as errors (the schema states this), and SHOULD warn of an entry of cut that names a recess its host does not have, or an element that is not in the model (provision-cut).
  6. It cuts nothing. A provision is no void of its host, answered or not: the host's body is what the host's own records make it (Part IV). Its body (rule 2) is what a view, a print or a check shows of the void asked for. It counts in no quantity, cuts no other body, and a clash check does not take it for a part of the building. A view MAY leave out a provision whose answer is cut, which what the cut made shows.
  7. In IFC, a bridge writes a provision as Part VIII, 1.10 says: an IfcBuildingElementProxy of the predefined type PROVISIONFORVOID, its body rule 2's, with its size, its depth and its runs in IFC4's property set for a provision for a void.
  8. Commands. A command processor makes and changes a provision and its type as any element and type (Part V, 1, rule 5), holding each to this section's schemas. It refuses a command that gives a provision a host of a category rule 3 does not list (provision-host), or a run of a category rule 4 does not list (provision-run). It keeps a provision whose host or run is not in the model, which the whole model's check warns of: provision.hostId is no hostId, so a delete of its host leaves the provision as it is (Part V, 5, rule 1), waiting for its owner to remove it (rule 3).

A duct's provision through an external wall, 450 mm wide and 250 mm high, its centre 1500 mm over level 0, asked by ventilation and not yet answered (compacted; on disk each member is a line):

{
  "category": "provision", "height": 250, "id": "provision-a", "levelId": "level-0", "name": "Opening for supply air",
  "offset": 1500, "profile": [[2000, 0]], "revision": 0, "typeId": "type-provision", "width": 450,
  "provision": {"axis": [0, 1, 0], "clearance": 25, "depth": 220, "forIds": ["duct-supply-1"], "hostId": "wall-a",
                "note": "Supply air 400 × 200", "up": [0, 0, 1]}
}

Its host's owner cuts it as a recess of the wall, wall-a's recesses entry provision-a-cut, and answers:

{"answer": {"at": "2026-10-30T09:15:00Z", "by": "A. Architect", "cut": [{"recess": "provision-a-cut"}], "discipline": "architecture", "state": "cut"}}

Reference: provisionSchema and provisionAnswerSchema in src/core/elements/provisionSchema.ts; provisionHosts, provisionRuns, validateProvision, provisionSolids, requestsFor, cutRecess, cutCommands and declineCommands in src/core/elements/provisions.ts; elementOwner in src/core/access/owners.ts (its asker); exportIfc in src/core/io/ifcExport.ts. In @openeik/tools: provisionChecks, provisionBody and provisionProperties in packages/openeik/src/provision.js.

6.10.1 A provision for a terminal

Part of 0.4. OpenEik 0.3 had it as the proposal provisionTerminals (open questions, 129): a model of 0.3, read by 0.3's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).

A provision is asked by what passes through its host, a terminal or a fitting set in it as well as a run.

  1. What it is for. forIds names the elements a provision is for, each a pipe, a duct or a cable carrier, as rule 4 of section 6.10 has it, or a terminal, or a component whose hostId names a pipe or a duct, such as a damper (section 6.9). A validator MUST report an element of another category, or a component on no pipe or duct, as an error (provision-run).
  2. Whose it is. Its owner, where no owner is given, is the owner of the element forIds names first, as rule 4 says of a run (Part III, 4.4, rule 2), and its answer, and what its cut makes, are its host's owner's, as rule 5 says: the void is the host's owner's to make, whatever asks for it (open questions, 129, the steward's confirmation).
  3. In IFC, as rule 7 says: a provision's system names in IFC4's property set for a provision for a void are the names of what its forIds names, a terminal's or a damper's among them.

Reference: none in the reference implementation, whose opening requests take a terminal as what they are for (its work 670). In @openeik/tools: askerOf and provisionChecks in packages/openeik/src/provision.js; the command processor's checkProvisionRefs; asked for with proposals: ["provisionTerminals"] on a model of 0.3.

7. References between elements

These element members name other elements. The list is the reference implementation's registry; a new member that names an element MUST be added to it.

PathNames
hostIdthe host: a wall for an opening, a slab or a foundation for a slab edge element, a slab, a foundation, a beam, a column, a slab edge element or a wall for a rebar element, a slab, a foundation or a wall for a mesh element (section 6.1, rule 6), a stair for its shaft, a stair, a slab or a shaft for a railing (section 12.2), a pipe or a duct for a component in it (section 6.9), a gutter's roof, the gutter a downpipe was made from (Part V, 2.3)
top.attach.hostId, top.cutRoofIdthe wall's attachment host and its cutting roof
base.attach.hostIdthe column's base host
edgeAttachments[].hostIda wall whose face trims an outline edge
trussLayout.bearingWallIds[]bearing walls of a truss layout
mep.joins.start.terminalId, mep.joins.end.terminalIdthe terminal a run's end joins
joins[].tothe member or plate a member end is joined to
framingTop.floorIdthe floor a framing top follows
bolts.through[]the elements a bolt group passes through and drills (section 6.3); of 0.2
castWiththe element it is cast with in one pour (section 14.1); of 0.2
topRoofId, topCeilingId, roofCutIdlegacy (section 6.4)

hostId of an opening, top.attach.hostId, base.attach.hostId, edgeAttachments[].hostId, joins[].to, and the hosts of slab edges, gutters, rebar and mesh MUST resolve: the reference validator refuses the model otherwise. So MUST a railing's hostId and each id in its railing.edges, which names an edge of its host (section 12.2, rule 3), a component's hostId, which names a pipe or a duct (section 6.9, rule 6), and a top constraint's level, top.constraint.levelId of a wall, a shaft or a stair: its height cannot be worked out without it (Part IV, 5.1). The others SHOULD resolve. In 0.2, each id of a bolt group's bolts.through MUST resolve too, as the reference validator refuses a group whose through names no element (section 6.3, the note), and an element's castWith SHOULD.

Reference: ELEMENT_REFERENCES in src/core/model/copies.ts; validateElement, findAttachHost (wallTop.ts), findBaseHost (columnBase.ts), validateMemberJoins.

8. Profiles

A profile groups the members and payloads of one domain, so that a reader can say which domains it understands.

ProfileMembers (elements)Payloads (types)
corethe members of section 6, hostId, along, voids, top, edgeAttachments, edgeIds, status, original, flipped, handFlipped, roomBounding, wallOutline, recesses, roof, joinEnds, declaredGeometry, genericlayers, section, windowFamily, openingFamily, outline, footing, wallAttach, declaredGeometry, generic
structure and fabricationendRise, turn, roll, taper, base, connection, joins, holes, upright, axes, plate, bolts, weld, jointId, insertAt, edgescomponent, insert, edgeDetail, edgeProfile; pieces and joints
timber framingframingParameters, framingTop, framingJoints, framingCorners, framingStart, framingOffset, framingStuds, framingTees, bottomPlate, frameHost, trussLayoutframing, the layers' framing and joists
reinforcementreinforcement, bars, sheet
systemsmep, terminalpipe, duct, terminal
siteterrain, grading, propertyLine, road, parking, plantingroad, parking, planting
interiorsroom, kitchenroom, furniture
circulationstair, railingstair, railing, shaft
firefire, fireClassfire, fireClass
documentationviews, detail items, scope boxes, dimension styles, detail families
  1. A reader that does not implement a profile MUST keep that profile's members and records unchanged when it writes the model back.
  2. Profile members are flat members of the element, as the reference implementation stores them, and they stay flat (decided 11 October 2026). A model does not declare its profiles yet.
  3. No national code lists. OpenEik carries no national classification or code list (BSAB, CoClass, AMA, SS 21054 or any other) and defines no regional profiles. A field whose values come from such a list in practice is a plain string: a room type's area class (room.ssClass), a view's area plan (display.areaPlan), the shape codes and grades of bars and cast-in bars, the designations and grades of mesh sheets, the precast reinforcement grades and meshes of the project, and the insulation codes of pipes and ducts and their parts (section 6.9). The schemas accept any non-empty string there and mark the field "x-eik-values": "national". A building-part catalogue (a type's assembly) is an application value (section 9.2).
  4. A reader MUST keep these values unchanged and MUST NOT need a code list to read them. A validator MUST NOT refuse a value because a code list does not contain it.

Why no regional profiles: a code list belongs to its publisher, who sets its licence and its editions. A standard that listed codes would have to license them, follow their editions and choose which countries' lists to carry, and an implementation would need them to conform. Leaving the values to their publishers keeps OpenEik usable with any system, in any country. Classification references (section 11) let a record name a code of any system without the standard licensing or listing it.

9. Application records and values

9.1 Application records

Title blocks, images, reports, renders, deviations, area adjustments and linked references are records of the application that wrote the model. OpenEik carries them but does not define them.

In 0.1, drawing production stays with the application: sheets, title blocks and images remain application records and do not join the documentation profile (decided 11 October 2026). In 0.2, sheets and issued drawings join the standard (the steward's decision of 15 October 2026): a sheet is a record of Part XIV, 2, and a sheet of 0.1 that is not one stays an application record (Part XIV, 2.1, rule 1; Part I, 9.8, rule 5). Title blocks and images stay application records.

  1. Each is a JSON object with an id; application-record.schema.json checks only that.
  2. A reader MUST keep application records unchanged when it writes the model back, and MUST NOT require them.
  3. A writer MAY omit them.

9.2 Application values

Some members of defined records hold application values: a type's assembly (a building-part catalogue), catalogSource and handbook, the project's standards, the font choices of views and dimension styles, a symbol's name and a port's, and the rows of a tag template (section 13). The schemas mark them "x-eik-application": true and accept any value of the right JSON type. A reader MUST keep them unchanged. They are the application's, not the standard's: OpenEik does not define a catalogue, a handbook, a drawing standard or a font (decided 11 October 2026). An application's own data beside them goes in extension members and in its own folder (Part I, 11).

10. Derived data is not a record

Bodies, meshes, joins found from geometry, room boundaries and areas, quantities, framing members derived from a rule, stair runs, fittings and the elements of placed copies are derived (Part IV). They MUST NOT be stored as records. Two kinds of record come from derived data because a person or a command made them so:

  • materialised members: framing members or trusses written as elements by a command (framing.materialise, trusses.materialise) carry frameHost and are ordinary elements from then on;
  • originals and copies: a placed group is stored as its original's elements plus a copy record; the elements of every instance but the one it keeps are derived, named as section 10.1 says, <copy id>/<base id> for a copy, and are never stored.

A view is a record (its settings and its 2D items); the drawing it shows is derived.

Reference: storedOf and workedOut in src/core/model/copies.ts; migrateManifest drops stored copy elements on load.

10.1 A group's kept instance and its instances' ids

Part of 0.3. OpenEik 0.2 had it as the proposal groupIds (open questions, 126): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).

With Part I, 6.1. A group is an original (project/originals/<id>.json) and its kept elements, the elements whose original names it, placed once for each of its instances: its first instance, whose id is the original's own, and each copy (project/copies/<id>.json) whose of names it. These rules say which instance's elements the kept ones are, and the id each element has in each instance. In a model of 0.2, a validator applies it only where the proposal is asked for.

  1. The kept instance. A group keeps its elements once, as the elements of one of its instances, where that instance stands: the original's frame, the id of that instance, or, without frame or where it is the original's own id, its first instance. The original's origin, turned and lifted say where the kept elements stand. A frame that is not the original's own id MUST name a copy whose of names that original and which stands there: its at the original's origin, its turn the original's turned and its lift its lifted, each 0 where it is absent, and no levelId. The first instance has a copy record of its own, whose id and of are the original's id, only while it is not the kept instance; while it is, it has none. A validator MUST report a frame that breaks this rule, and a first instance's copy record while its elements are the kept ones (group-frame).
  2. An element's base id. Each kept element was drawn in one instance: its born, or, without it, the first instance. Its id in that instance is its base id, and in any other instance its id is that instance's id, a / and its base id. Its stored id is its id in the kept instance: its base id where it was drawn there, else the frame, a / and its base id. A validator MUST report a kept element drawn in another instance than the kept one whose id does not begin with the frame and a / (group-id).
  3. The instances' elements. Every instance but the kept one derives its elements from the kept ones (section 10), each under its id in that instance (rule 2), and none is stored: a copy's are <copy id>/<base id>, but for those drawn in it, which have their base ids; and the first instance's, where it is not the kept one, have their base ids, but for those drawn in another instance. The kept instance derives none: its elements are the stored ones.
  4. No stored id is a derived one. No stored element has an id an instance derives (rule 3). A validator MUST report a stored element whose id one does (id-derived), naming the instance and the group; where that id begins with the instance's id and a /, Part I, 6.1, rule 2 reports it already (id-slash).

Reference: frameOf, bornIn, baseId, idIn and instancesOf in src/core/model/groups.ts; workedOut and validateCopies in src/core/model/copies.ts. In @openeik/tools: groupRules in packages/openeik/src/check.js, for a model of 0.3, and for one of 0.2 asked for with proposals: ["groupIds"]. It derives no instance's elements: a compiler of 0.2 does not expand copies (Part IV, 1).

11. Classification references

Part of 0.2. OpenEik 0.1 had it as the proposal classifications, and this section was informative: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), classifications is a member a schema of 0.1 does not know, which a reader keeps (Part I, 10). The section adds this one member and no file.

By the steward's decisions of 11 October 2026 (open questions, 4) and 27 October 2026 (open questions, 86, "C ja"): a record names codes of classification systems that OpenEik does not list, so that any system can be used without the standard licensing or listing it.

{
  "classifications": [
    {
      "code": "<code>",
      "edition": "<edition, optional>",
      "system": "<name or URI of the system>",
      "uri": "<the code's URI, optional>"
    }
  ]
}
  1. Which records. Any record MAY carry classifications, a list of 1 to 100 references, at its top: the project, in its project; a level; and each record of the record lists (Part III, 9), whatever its kind: a grid, a material, a type, an element, an original, a copy, a view and the rest. The order, which is no record, and a view's detail items, which are parts of their view, carry none.
  2. A reference is {system, code, edition?, uri?}. system names the classification system, by its name or a URI, 1 to 200 characters; code is a code of that system, 1 to 100 characters; edition names the system's edition, 1 to 100 characters, where it matters which; and uri names the code itself, by the URI its publisher gives it, such as its URI in buildingSMART's Data Dictionary (bSDD): an absolute URI (RFC 3986), at most 2000 characters. A writer SHOULD give uri where the publisher gives one, so that a reference names its code the way buildingSMART's IFCX names a node's class, by a bSDD URI. A member of a reference that this section does not define is one a reader keeps, as in any record (Part I, 10).
  3. Several references. A record MAY carry several references, of one system or of several. It MUST NOT carry one reference twice. The order of the list has no meaning.
  4. No list of systems or codes. OpenEik does not list systems or codes, and a validator does not check that a code is one of its system's: the system's publisher defines its codes and their licence, and the writer of the record says which system it uses. A validator MUST NOT refuse a reference because it does not know its system or its code.
  5. Beside, never in place of. The plain strings and application values of sections 8 and 9.2 stay as they are: a classification reference is added beside them, never in their place, and a reader reads neither from the other.
  6. A bridge that writes IFC writes each reference of a record it prints an object for as an IfcClassificationReference of its system (Part VIII, 1.8), its Identification the code and its Location the uri, and says in its fidelity report which references it does not print.

The class 21.21 of NL-SfB, as buildingSMART's IFCX examples classify a wall: {"system": "NL-SfB", "edition": "2005", "code": "21.21", "uri": "https://identifier.buildingsmart.org/uri/nlsfb/nlsfb2005/2.2/class/21.21"}. A wall may carry it beside a code of another system, {"system": "Uniclass", "edition": "2015", "code": "EF_25_10"}, which gives no URI here.

11.1 An estimate's members

Part of 0.3. OpenEik 0.2 had it as the proposal estimate (open questions, 116): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).

The records say what an estimate sorts and prices a building by: the mark an element is called by, the tender part it is bought in and what is done to it, and the codes that cost books price a type and a room's finishes by. A code is an identifier only: a model holds no price and no cost book, and OpenEik lists no cost book's system or codes (section 11, rule 4). In a model of 0.2 that does not ask for the proposal, these members are members its schemas do not know, which a reader keeps and a validator warns of (Part I, 10).

  1. A mark. An element and a type MAY carry littera, the mark an element is called by on drawings, in schedules and in an estimate: a type's, such as D1, F3 or IV2, for every element of it, and an element's own, over its type's. It is 1 to 20 characters, with no white space at its start or its end. The mark an element is called by is its own littera, else its type's, else none.
  2. A tender part and an action. An element MAY carry tenderPart, the part of a tender it is bought in, such as Frame or Stage 2, 1 to 40 characters, and action, what is done to it where its status (existing, new or demolished) says too little, such as Replace or Remove and refit, 1 to 60 characters; each with no white space at its start or its end. Their values are the writer's words: OpenEik lists none, and a reader compares them as they are written.
  3. A cost code. A code that a cost book prices a record by is a classification reference (section 11) of the cost book's system, in the record's classifications: a type's above all, or a room's finish's (rule 4). Beside the members of section 11, rule 2, such a reference MAY carry:
    • measure, the quantity the code is priced by: area, areaGross (the area before its openings are taken out), length, count, volume or mass; absent, the quantity the reader prices the record's category by;
    • size, the size the code is for, as the drawings write it, such as 110 or 400×200, 1 to 20 characters: a code of a pipe's, a duct's or a cable carrier's type for that size alone, where a code without one is for every size that has none;
    • on a pipe's or a duct's type, item, what of its runs the code prices besides the runs themselves: bend, branch, reduction, transition or insulation, where a code without one prices the runs; angle, a bend's angle in degrees, more than 0 and at most 180, on a code whose item is bend; insulation, the insulation code (section 6.9) of the runs whose own insulation the code prices, 1 to 12 characters, on a code whose item is insulation; and includes, what the runs' own code prices with their length, fittings, insulation or both, on a code with no item. A validator reports an angle on a code whose item is not bend, an insulation on one whose item is not insulation, and includes on one with an item (cost-code-member). A reference with none of these members is a classification reference as any other.
  4. A room's finishes. A room, and a type of rooms, MAY carry classifications in each of its finishes, room.finishes.floor, skirting, wall, ceiling and other: the codes the finish is priced by, as rule 3 says, and any other classification of its work, as section 11 says.
  5. Its print. A bridge that writes IFC writes a type's littera as the property Reference of the property set IFC4 defines for its class, the set Pset_ … Common that IFC4 applies to the type object and that defines Reference (Part VIII, 1.5, rule 2), and an element's own littera so on its object, where IFC4 applies such a set to it. It writes a mark that no such set takes, and an element's tenderPart and action, in a property set of its own, OpenEik_Estimate, as Littera, TenderPart and Action. It writes a cost code as any classification reference (section 11, rule 6): IFC has no place for a reference's measure, size, item, angle, insulation or includes, which it does not write.

Reference: elementEstimateShape, typeEstimateShape, costCodeSchema and litteraOf in src/core/model/estimateFields.ts (Stomra works 758, 768 and 772); finishSchema in src/core/elements/roomTypes.ts. In @openeik/tools: estimateKnown and estimateChecks in packages/openeik/src/estimate.js; for a model of 0.3 the schemas of 0.3, and for one of 0.2 asked for with proposals: ["estimate"] the proposal's index, which proposed03Members in packages/openeik/src/check.js holds; and markSetOf in packages/openeik/src/ifc.js.

12. A supplier's parts

The records of this section serve the supplier proposal flow, which the steward approved on 11 October 2026: a supplier's declared geometry (12.1) and railings for product systems (12.2). Their principles were decided on 11 October 2026 and proposed for 0.2; on 14 October 2026 they were adopted into 0.1 (open questions 17 and 18). types/type.schema.json and elements/element.schema.json define declaredGeometry and the members of railing, and the category schemas add the rules of each category.

In the supplier proposal flow, the supplier of a building part (railings first, then any part) is invited to a project. It reads a named version, finds its parts by category, and checks them with its own tool. It proposes changed records as typed commands (Part V), with a quote. The client merges one proposal. The quote lives with the proposal on the host: it is not part of the model.

12.1 Declared geometry: a part at a declared level of detail

The principle. A supplier's records can carry geometry at any level of detail, the supplier's choice. It ranges from the interface only up to the full manufacturing model:

  • The interface is what other trades and checks meet: the exact outer envelope, the fixing and connection points, clearances, positions such as posts and fixings, the loads the part puts on the structure, and the declared tolerances. It is enough for exact geometric checks and layout control, and it is a valid minimum, not the rule.
  • The implementation is the supplier's own to keep back or to share: internal profiles, joints, machining and cutting data, article numbers. A supplier that shares none of it publishes the interface, not the implementation; one that shares all of it publishes its manufacturing model.
  • Costs, prices and quotes are never part of a record. In the supplier flow the quote lives with the proposal on the host.

A record says how far it goes: it declares its level and what its geometry is authoritative for. As in a code base, a library may ship only its interface, its declarations, and keep its implementation: a compiler checks every use against the interface exactly and assumes nothing about the code behind it.

Where it lives. A type or an element MAY carry declaredGeometry:

  • On a type, for geometry that is the same for every element of the type: a window, a door, a component, a railing's section. Its coordinates are in the type's frame:
    • a category placed at a point (component, furniture, terminal, column, generic): the element's insertion point at its base, x along its orientation, z up; a component with axes, the frame they give (section 6.3);
    • a hosted opening (door, window): x along the host wall from the opening's near edge, y across the wall from its centreline towards the wall's exterior side (Part I, 5.1), z up from the opening's bottom;
    • a category placed along a line (railing, beam, slabEdge, gutter): a section square to the line (x across it, z up from the line's base), swept along the whole line.
  • On an element, for geometry that depends on where the element is: the posts and fixings of a railing along given edges, a stair kit in a given shaft. Its plan coordinates are those of the element's profile (the project's, in mm); its heights are measured from the element's base, elev(levelId) + offset (Part I, 3.3 and 4).

For each aspect, an element's declaration wins over its type's; the aspects an element does not declare come from its type's.

A type of a category that this list does not name (a stair's, a slab's, a wall's …) has no frame in 0.1. Its declaration is kept and checked as any declaration is, but placed nowhere: a compiler derives nothing from it (Part IV, 11.1), and a check cannot use it. Such a part declares its geometry on its element, in the world frame (rule 7). From 0.2 a stair's type and a wall's type have a frame (section 12.1.1).

A window type declaring its interface, in its own frame (compacted; on disk each member is a line):

{
  "declaredGeometry": {
    "authoritativeFor": ["envelope", "fixings", "clearances"],
    "clearances": [
      {"body": {"bottom": 0, "kind": "prism", "profile": [[0, -45], [1200, -45], [1200, -650], [0, -650]], "top": 1200}, "id": "sash-swing", "purpose": "operation"}
    ],
    "envelope": [
      {"bottom": 0, "kind": "prism", "profile": [[0, -45], [1200, -45], [1200, 45], [0, 45]], "top": 1200}
    ],
    "fixings": [
      {"at": [0, 0, 200], "bearsOn": "reveal", "direction": [-1, 0, 0], "id": "fx-1"},
      {"at": [0, 0, 1000], "bearsOn": "reveal", "direction": [-1, 0, 0], "id": "fx-2"},
      {"at": [1200, 0, 200], "bearsOn": "reveal", "direction": [1, 0, 0], "id": "fx-3"},
      {"at": [1200, 0, 1000], "bearsOn": "reveal", "direction": [1, 0, 0], "id": "fx-4"}
    ],
    "level": "interface",
    "tolerances": {"default": 2, "fixings": 1}
  }
}

The window's profiles, its sash and its glazing are not declared: a check knows the space the window takes, where it is fixed and what must stay free in front of it, and nothing about what is inside.

The levels. level is one of three, in order:

LevelThe geometry isIt MAY be authoritative for
interfacethe part as other trades meet it: the space it takes, where it is fixed, what must stay free, and the positions and loads that matter to othersenvelope, fixings, clearances, positions, loads
assemblythe interface, and each separate part as installed (posts, rails, panels, brackets, a sash), with its outer body and its placethe above, and parts
fabricationthe full manufacturing modelthe above, and fabrication

What it is authoritative for. authoritativeFor lists the aspects the geometry is exact for, from this closed vocabulary:

TermThe declaration states, exactly within its tolerance
envelopethe outer envelope: the whole space the part takes as installed, as bodies
fixingsthe fixing and connection points: where the part is fixed to another record or connects with it, in which direction, and which face it bears on
clearancesthe zones that must stay free: to install, operate (a sash's or a leaf's swing), maintain or replace the part
positionsthe set-out points or lines of the part's distinguishable members (posts, brackets, mullions, hinges), without their shapes
loadsthe loads the part puts on the structure at each fixing, per named load case, as characteristic forces in kN and moments in kN·m in the model's axes
partsthe separate parts as installed, each with an id, a name and its outer body
fabricationfabrication data: internal profiles, joints, machining, cutting and assembly data. OpenEik does not define its form: it is the supplier's application value (section 9.2), or the supplier's kept files (Part III, 7) named by path and hash

Rules:

  1. level and authoritativeFor are required. authoritativeFor MUST contain envelope, MUST name each term once, and MUST name only terms its level allows. envelope with any of fixings, clearances, positions and loads is a complete declaration at the interface level.
  2. A declaration MUST carry data for every term it lists, and MUST NOT carry data for a term it does not list.
  3. tolerances gives, in mm, how far the built part may lie from what is declared: a default, and a value per term where it differs; it has no other members. Every geometric term listed (envelope, fixings, clearances, positions, parts) MUST have a tolerance, its own or the default.
  4. Bodies are of the body kinds of Part IV, 8, written with these kind names: prism {profile, bottom, top}; polyhedron {vertices, faces} (convex); mesh {vertices, faces} (closed); cylinder {centre, radius, bottom, top}; rod {from, to, diameter}; and, on a type placed along a line, swept {section: [[x, z], …]}, with x across the line towards the walking side, swept piece by piece along each segment. In the line frame a body is mapped point by point, so it bends only at its vertices: a line-frame body SHOULD stay within one segment of the line. The other records: a clearance is {id, purpose, body}; a position is {id, member?, at, to?}, with to for a position along a line; a load is {fixing, case, force, moment?}, in kN and kNm, acting at the declared fixing whose id its fixing names; a part is {id, name, bodies}. A writer that mirrors a declaration (rule 6) MUST reflect a load's force, and reflect its moment and then change its sign, since a moment is an axial vector: with the reflection R, F' = R·F and M' = −R·M (mirroring x → −x turns M (4, 5, 6) into (4, −5, −6)).
  5. An element's declaration SHOULD name its basis: the id and the hash (Part III, 6: SHA-256 of the record's canonical JSON, the function the version tree uses) of each record it was made against, such as the host and the edges it follows. A reader reports a basis whose hash has changed, keeps the declaration, and treats it as unconfirmed.
  6. A writer that moves, turns, mirrors or copies an element (the transforms of Part V, 2.3) MUST move, turn, mirror or copy its declared geometry with it.
  7. The frame of an element's declaration. frame is world (absent: the project's plan coordinates, as above) or, for a category placed along a line, line: a point is [s, w, z], with s the distance in plan along the element's line from its first point, w the distance across it towards the walking side, and z the height over the element's base. A declaration in the line frame follows its host's line the way railing.posts.at does.
  8. A fixing gives its id, its point at and its direction (a unit vector), and SHOULD say what it bearsOn (reveal, edgeFace, slabTop and so on). It MAY give its kind (bolt, anchor, bracket, screw …) and its size in mm.
  9. Who declared it. A declaration MAY name its source: who made it and with what, as {"by": …, "tool": …, "version": …}. It says nothing about responsibility; a host's signed merge records that.

The schemas state rules 1 to 3 and the shapes of rules 4, 5 and 7 to 9: fabrication is marked as the supplier's application value, a swept body is allowed only in a type of a category placed along a line, and the line frame only on an element of such a category. A validator checks the rest in code, such as that a load acts at one of the declaration's fixings.

Reference: declaredGeometrySchema in src/core/model/declaredGeometry.ts (rules 1 to 3, the shapes of rule 4, rules 5 and 7 to 9); mappedDeclaration there, called by the transforms (rule 6); unconfirmedBasis (rule 5).

How readers, checks and compilers treat it.

  1. Exact for what it declares, silent on the rest. A reader, a check or a compiler MUST use declared geometry only for the aspects its authoritativeFor lists, and MUST NOT infer from it anything about the others: an envelope says nothing about the parts inside it.
  2. Readers keep it unchanged (Part I, 10). A viewer MAY draw the declared envelope, or the declared parts, in place of the body a compiler derives from the type's payload, and SHOULD say that it shows a supplier's declared geometry, at which level.
  3. Checks use what is declared. A clash check uses the declared envelope, grown by its tolerance, in place of the derived body, and treats each clearance, grown by its tolerance, as space no other body may enter. A layout check uses the declared positions and fixings: a fixing MUST lie on the face it names, within its tolerance. For an aspect that is not declared, a check uses the derived body and says so in its result. A check that needs an aspect that is not declared (the gaps between balusters need parts) MUST report that the declaration cannot decide it: it neither passes nor fails it.
  4. Compilers derive nothing beyond what a record declares itself authoritative for (Part IV, 11.1).
  5. A changed basis. When a record named in basis has changed since (its hash differs), the declaration was made for another version of that record. Readers keep it and checks still use it, but they report it as unconfirmed until the supplier confirms or replaces it. It is never discarded.

Declared, not derived. Section 10 and Part IV, 1, keep derived data out of records. Declared geometry is not derived data:

Derived data (Part IV, 1)Declared geometry
Made bya compiler, from the model's records, by the rules of this specification or a reference rulethe supplier, from its own product and its own rules, which are not in the model
Made again from the records?yes: deleting it loses nothingno: deleting it loses the supplier's statement
Where it livesnever in a record; at most in .eik/derived/, keyed by the hashes of its sources (Part IV, 1.1)in the record, a type or an element, written by a command and merged by a person like any other change
When its sources changediscarded and derived againkept, and reported as unconfirmed (rule 5 above) until the supplier confirms or replaces it
Authoritythe rule that made itexact for what it declares, within its tolerances; silent on the rest

A declaration is source, as a door type's sizes are (section 5.3): what a supplier states about its product. Nor is it a materialised member (section 10): those are written from the model's own derived data, and the model's rules could make them again; nothing in the model can make a declaration again. What a compiler works out from a declaration (its bounds, a section through its envelope) is derived as before, and is not stored.

12.1.1 Nominal sizes, and a stair kit's and a prefabricated wall's frame

Part of 0.2. OpenEik 0.1 had it as the proposal declared: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a declaration on a stair's or a wall's type is placed nowhere, as section 12.1 says. The section adds no file and no member.

By the steward's decision of 21 October 2026 (open questions, 17), on the two questions deferred on 15 October 2026. Suppliers ship a stair kit and a prefabricated wall whole, and declare them on their type, as they declare a window.

  1. Nominal sizes. The sizes a type sets (section 5.3) need not agree with its declared envelope. They stay its nominal sizes, which schedules and tags show, and the declared envelope is the part's geometry where a body stands for the space it takes (Part IV, 11.1, rule 2). A validator MAY warn where a size differs from the envelope's extent in the type's frame by more than the declaration's tolerance for envelope, its own or its default (declared-size): width from the extent along the frame's x, height from the extent along its z.
  2. A stair's type, a stair kit's: its declaration's frame has its origin at the stair's start, its profile's first point, at its base, elev(levelId) + offset; x runs up its first run, towards the profile's second point in plan; y to its left, looking up the stair; z up. It is the stair's frame of Part IV, 7.3.
  3. A wall's type, a prefabricated wall's: the origin at the baseline's first point, at the wall's base, elev(levelId) + offset; x along the baseline; y to its left, the wall's exterior (Part I, 5.1); z up. The wall's top, attached or of its own outline (Part IV, 5.3), moves nothing in the frame.
  4. Other categories. A type of a category that neither section 12.1 nor rules 2 and 3 give a frame keeps none: its parts declare their geometry on their element, in the world frame (section 12.1, rule 7).
  5. A compiler places a stair's and a wall's type's declaration in these frames, as section 12.1 places the others' (Part IV, 11.1): its envelope stands where the element's body stands for the space it takes.

A prefabricated wall element 2400 mm long, 3000 mm high and 210 mm thick declares on its type the envelope {"bottom": 0, "kind": "prism", "profile": [[0, -105], [2400, -105], [2400, 105], [0, 105]], "top": 3000}, centred on the baseline as a wall's body is (Part IV, 4.1), its fixings at its foot, [600, 0, 0] and [1800, 0, 0], and its tolerances {"default": 3}. Each wall of the type places it at its own baseline's first point, along its baseline, its exterior face at y = 105.

12.2 Railings for product systems

A railing supplier's tool details railings on the edges of slabs and makes them. Without the members of this section, a railing's records are too thin for a product system, and two implementations place the posts of one railing differently. In a study of 11 October 2026, a railing on three edges of a balcony slab, written with the reference implementation's commands, got eight posts from the reference implementation and eight from a railing supplier's tool: at the corners in one, 200 mm from them in the other. This section adds what a product system needs.

The members. An element's railing is {side?, edges?, inset?} and the members added below. A type's railing has thirteen members, kind (balusters, glass, panel or handrail), height, postSpacing, post, railWidth, railDepth, baluster, gap, clearance, panel, handrail, handrailDiameter and material, each required by the kinds that use it (the table below), and the members added below. A railing's line, posts and infill are reference behaviour (Part IV, 7.2), except the line of a railing on an outline and its given posts (Part IV, 11.2).

Host and edge rules (section 6.1, rule 11, and section 7). The reference implementation checks them.

  1. A railing's type MUST have a railing payload.
  2. A railing stands on a stair, on the outline of a slab or a shaft, on a wall's top, along a slab edge element, or on a line of its own:
    • on a stair: hostId names the stair, and railing.side (left or right, looking up the stair) MUST be given;
    • on an outline: hostId names a slab or a shaft, and railing.edges MUST be given: ids of the host's edges (its edgeIds, or <host id>:edge:<n> where it has none; Part I, 6.6) that follow one another round the outline as one run. Naming every edge makes a closed run;
    • on a wall's top or along a slab edge element: hostId names the wall or the element, and railing.side, railing.edges and railing.insets MUST NOT be given (below);
    • with no host: its profile, two points or more, is its line, and its base is elev(levelId) + offset.
  3. A railing's hostId (a stair, a slab, a shaft, a wall or a slab edge element) and each id in railing.edges (an edge of its host) join the references that MUST resolve (section 7).
  4. A railing on an outline stands on its host's top along its line (Part IV, 11.2); a railing on a stair is measured from the stair's nosing line (reference behaviour). A hosted railing's profile, levelId and offset are written from its host: a writer rewrites them in the same command whenever the host changes. The profile is the railing's line in plan, the level is the host's (for a shaft with a top constraint, that constraint's level), and the offset is 0. The host and the edges are the intent; the profile is the writer's copy of the line. A reader MAY use it for a railing on an outline, MUST follow the host where the two disagree, and gets no heights from it for a railing on a stair. From 0.2 the copy of a railing on an outline is deprecated, and such a railing may leave it out (section 12.2.1).
  5. railing.side has a meaning only on a stair, and railing.edges and railing.insets only on an outline.

Reference: validateElement in src/core/model/model.ts ("A railing needs a railing type.", "A railing on a stair names its side: left or right.", "A railing on an outline names the edges it runs along."); railingLine and chain in src/core/elements/railings.ts; followedRailing in src/core/elements/hostedFollow.ts.

The members added for product systems, all optional, so that a railing written without them stays valid and keeps its shape. On the type's railing:

MemberMeaning
mounttop: the posts stand on the host's top (the default, and the behaviour of a railing without this member). side: the posts are fixed to the host's edge face and reach belowBase below its top
postWidth, postDeptha rectangular post: its size along the line and across it, in mm. Without them a post is post by post
postEndDistancehow far the first and the last post stand from the ends of the line, in mm, where the element gives no posts.at. Absent: 0
belowBasehow far the posts and the infill reach below the base, in mm, for side mounting. Absent: 0
topRailthe top rail's shape: rectangular (railWidth by railDepth, the default), round (diameter railWidth), halfRound or flat
panelPatterna panel's pattern, for the kind panel: solid, perforated or corrugated
cornerhow rails and infill meet at a corner of the line: mitre or square. Absent: the rails run past each other

On the element's railing:

MemberMeaning
insetsper edge, by edge id: that edge's inset where it differs from inset (outline hosts only)
posts.atthe posts' positions: distances in mm along the railing's line from its start, measured in plan, in increasing order. They do not move when an inset changes
posts.omita list: ["start"], ["end"] or both: the ends that have no post of this railing, because a neighbouring railing's post or a wall carries them. With posts.at, the list of posts is complete, and omit only says which ends a neighbour carries

Not every member for every kind. A type's railing MUST give the members its kind requires, and need not give the others. (Until 14 October 2026 every railing type gave all thirteen, even those its kind does not use: panel for balusters, handrailDiameter without a handrail.)

MemberRequired for
kind, height, postSpacing, railWidth, railDepth, materialevery kind
postevery kind, unless postWidth and postDepth are both given
baluster, gapbalusters
clearancebalusters, glass, panel
panelglass, panel
handrailDiametera type whose handrail is more than 0
handrailnone: absent means 0, no handrail on the walking side

A member a kind does not use MAY be present, and readers ignore it for that kind. gap is the widest clear opening between balusters, not their spacing centre to centre. The type schemas state this table; the element schema states that posts.omit names each end once.

Posts are normative when given. If railing.posts.at is present, a post stands at each distance it lists along the railing's line, and nowhere else: every reader, compiler and check places them there (Part IV, 11.2). Otherwise the post layout is reference behaviour, within the type's postSpacing and postEndDistance. A supplier's tool writes its post layout with an update of posts.at, as framing members are materialised (section 10): the product's rules stay in the supplier's tool, and their result is in the record, so every tool shows the same posts. When the line changes later (the host is reshaped), posts.at stays as written, and a check reports a post that is off the line or a span longer than postSpacing.

A side-mounted railing with balusters on three edges of a balcony slab, the railing of its type and of its element (compacted; a synthetic example):

{
  "baluster": 20, "belowBase": 300, "clearance": 0, "corner": "mitre", "gap": 98,
  "height": 1150, "kind": "balusters", "material": "Aluminium", "mount": "side",
  "postDepth": 50, "postEndDistance": 200, "postSpacing": 1200, "postWidth": 30,
  "railDepth": 35, "railWidth": 50, "topRail": "halfRound"
}
{
  "edges": ["slab-balcony-a:edge:3", "slab-balcony-a:edge:0", "slab-balcony-a:edge:1"],
  "inset": -25,
  "insets": {"slab-balcony-a:edge:1": -40},
  "posts": {"at": [200, 1308, 1525, 2633, 3525, 4642, 5675, 5900]}
}

The type gives no panel, handrail or handrailDiameter: its kind does not use them. The supplier's product identity can travel in the type's catalogSource (section 9.2); its article numbers stay in its own folder, or in a declaration authoritative for fabrication if it chooses to share them (section 12.1).

Details decided with the reference implementation (11 October 2026):

  • A corner where two edges have different insets is where the two moved edge lines cross; if they are parallel, or cross farther than four times the larger inset from the corner, it is halfway between their ends. When every edge of the run has the same inset, a corner moves along its bisector instead (Part IV, 11.2, rule 3): the two rules agree except at turns sharper than about 151°, where the moved lines cross farther than four times the inset from the corner, and the bisector rule stops there, at four times the inset.
  • A post rises to the underside of the top rail, and the top rail carries the railing to its height (Part IV, 11.2).
  • A position in posts.at beyond the line's end is not refused: a check reports it, as it reports a span longer than postSpacing.
  • panelPattern is a property of the panel; a compiler need not draw it.

Reference: railingTypeSchema and railingElementSchema in src/core/elements/railings.ts (the members, and what each kind requires).

On a wall's top and along a slab edge element. By the steward's decisions of 21 and 24 October 2026 (open questions, 18), in 0.1 since 24 October 2026, as the reference implementation builds them since Stomra 643. Part IV, 11.2 derives the line (its rules 8 to 11).

  1. A railing on a wall's top names the wall in hostId; a railing along a slab edge element names the element. Both join the references that MUST resolve (section 7).
  2. On these hosts, railing.side, railing.edges and railing.insets MUST NOT be given (rule 2 above). railing.inset and railing.posts (at, omit) mean what they mean on an outline.
  3. railing.inset, from −1000 to 1000 mm, is the distance of the railing's line from its host's outer face towards its inside: from a wall's exterior face (Part I, 5.1), from a slab edge element's outer face. Without it, the line runs along the middle of the host's top.
  4. Its levelId is the wall's level, or the level of the slab whose edge the element follows; its offset is 0. Its line is derived: its profile is not read. 0.1's schemas ask for a profile of one point or more, so a writer writes one point there, which means nothing; from 0.2 it may leave the profile out, as the derived line of a railing on an outline lets it (open questions, 18).
  5. Its walking side is a wall's interior (the right of its directed baseline), or the side of the slab. It has no member of its own: a wall turned round turns the railing with it, as its layers turn (Part I, 5.1), and a given inset and posts.at are read from the turned wall's faces and ends.
  6. When its host is deleted, the railing goes with it, as a hosted element does (Part V, 5, rule 1).

12.2.1 A hosted railing's profile

Part of 0.2. OpenEik 0.1 had it as the proposal railings: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), every railing carries a profile, as 0.1's element schema asks, and rule 4 of section 12.2 stands. The section adds no file and no member.

By the steward's decisions of 21 and 24 October 2026 (open questions, 18). The line of a railing on the outline of a slab or a shaft, on a wall's top or along a slab edge element is derived (Part IV, 11.2), a class-A rule; from 0.2 nothing of it is copied into the record.

  1. On an outline. The profile of a railing on the outline of a slab or a shaft is no longer its writer's copy: the copy is deprecated (Part I, 9.6). A reader derives the line from the host and its edges, and ignores a profile it finds; a writer does not rewrite it when the host changes, and SHOULD NOT write it. Such a railing MAY leave out profile. Its levelId and offset stay as rule 4 of section 12.2 writes them: the host's level, for a shaft with a top constraint that constraint's level, and 0.
  2. On a wall's top or along a slab edge element. A railing MAY leave out profile, which means nothing there (section 12.2, "On a wall's top and along a slab edge element", rule 4): the one point 0.1's schema asks for is deprecated, and a writer SHOULD NOT write it.
  3. On a stair. A railing on a stair keeps its copy: its profile, levelId and offset are written from its host as rule 4 of section 12.2 says, and a reader gets no heights from them, until a stair's flights have a class-A rule its line can follow. A railing with no host has its profile as its line, as before.
  4. What a validator checks. A railing that names a host and no railing.side, which so stands on no stair (rule 2 of section 12.2), need not carry a profile. A validator SHOULD warn about the profile of a railing on an outline, a wall's top or a slab edge element (railing-profile), and a writer that reads one removes it.

A railing on three edges of a balcony slab is {"category": "railing", "hostId": "slab-balcony-a", "levelId": "level-1", "offset": 0, "railing": {"edges": ["slab-balcony-a:edge:3", "slab-balcony-a:edge:0", "slab-balcony-a:edge:1"], "inset": -25}, …}, with no profile; its line is the run of those edges, moved by its inset.

13. Views and detail items of building services

Views and their detail items, the 2D items of a view, are records of the documentation profile (sections 1 and 8; Part III, 10), held to their schemas. This section says what the members of heating, ventilation and plumbing drawings mean, by the steward's decisions of 23 and 24 October 2026 (open questions 48 to 50): a view's filter by system, frames and the white ground of texts and tags, the tags of pipes, ducts and terminals, the room tag, and symbols with lines joined to their ports. What a view shows is derived, and drawing it is the application's (section 10).

13.1 A view's filter by system

A view's visibility.systems shows some systems and hides the others: {mode, ids}.

MemberMeaning
moderequiredshow: the view shows the systems named, and hides the others; hide: it hides the systems named, and shows the others
idsrequired1 to 40 systems: a system of section 6.9 (coldWater), or a system and its number after a colon (heating:20)

A system without a number names that system under every number; with one, under that number only. heating:1 names the heating runs whose mep.number is 1 or absent.

  • A pipe or a duct is hidden when its system and number are.
  • A component in a pipe or a duct (section 6.9) is hidden when its run is.
  • A duct part that names no duct (section 6.9) goes with the duct it stands on, where it stands on one, and is otherwise another terminal; the reference takes the nearest duct within 300 mm in plan.
  • Another terminal is hidden when every system it serves is hidden: the systems and numbers of the runs whose ends join it (mep.joins), else the systems of its type and its connections, under any number.
  • Their tags go with them, and so do their bodies in a section or a 3D view.

A dependent view takes its parent's filter with the rest of its visibility. How a drawing shows what the filter hides, left out or in halftone, is the application's.

13.2 Frames and the white ground

A detail item's textFrame draws a frame round its text: {shape, margin?, weight?}.

MemberMeaning
shaperequiredrectangle, rounded (a rectangle with rounded corners), or none: no frame, where the view would give one
marginoptionalthe gap between the text and the frame, in paper mm, from 0 to 10; absent, 1
weightoptionalthe frame's line width, in paper mm, from 0.05 to 2; absent, 0.25

A view's display.textFrame is the frame of its texts, leaders and tags that give none of their own; display.mepTagFrame the frame of the plan's automatic tags of pipes, ducts and terminals (13.3). An item's own frame wins over its view's, and none leaves out the view's. The frame is measured from the text's extent, so it follows the text, its size and the view's scale: its sides lie margin outside the text's box, and a rounded frame's corners have a radius of half its height, at most 1.5 paper mm. A leader leaves from the frame's edge.

A detail item's textBackground, opaque or transparent, says whether its text stands on a white ground that hides what lies under it; a view's display.textBackground is that of its texts, leaders and tags that give none of their own, and without either the ground is transparent. A dimension style has its own (textBackground), as in 0.1.

A frame and a ground mean something on a text, a leader and a tag; on another item they are kept, and draw nothing.

13.3 Tags of pipes, ducts and terminals

The plan's automatic tags. A plan tags its pipes, ducts and terminals by itself: a run's designation (its system, number, material code and size) and a terminal's code, as the application writes them. A view's display.mepTags says how many: each, the default, tags every run and terminal; perRun tags one run of each chain of runs joined end to end with one designation and size, the longest in plan, and every terminal; none tags none. A view's mepTagEdits keeps, by an element's id, what was done to that element's automatic tag in the view: offset, where it stands, in the view's mm from where the plan puts it; hidden, true where it is not drawn; and elbow, true where its leader runs at right angles, along to the tag's height and then across to its edge. The tag stays the element's: it follows the element, and can be moved one by one.

Lined-up tags are drawing behaviour: an application's command (the reference's tags.lineUp) stands the tags of chosen elements in a column or a row outside the building, and writes what it did as each tag's offset and elbow in mepTagEdits. It adds no member.

A terminal type's terminal.tagSize, false, leaves its size out of its elements' tags; it is 0.2's (section 14.6).

A tag placed by hand is a detail item of the kind tag (section 1). Its tag.field mep reads the element's automatic tag: a run's designation, a terminal's code.

Tag templates. A view's display.mepTagTemplates says what its tags of pipes, ducts and terminals read, {pipe?, duct?, terminal?}, each {rows}: one to three rows, each of 1 to 120 characters. A template is the view's data: it travels with the view, so that another application reading it knows what the view's tags are to read. Its rows are written in the application's own language of fields, an application value (section 9.2; open question 49). A tag's text, the template filled in, is derived, and never stored.

13.4 The room tag

A tag of the field room tags a room: its tag.elementId names the room. It stands at the room's label point, follows the room, and may have a frame (13.2). The tag is model data, a detail item of its view as any tag is; what it reads is derived: the room's number and name; the codes of the fixtures in it, counted; its air terminals by code and size; and its supply and extract air flows summed, in l/s. A view that hides the ducts leaves the air out, and one that hides the pipes the fixtures. Components in a pipe or a duct, and duct parts that name no duct, are not the room's.

13.5 Symbols

A symbol is a detail item that places a symbol of a library: a valve, a pump, a heat exchanger, a fan, a damper, as a flow schematic draws them. It is drawn at paper size, the same size on the sheet at any scale, with its fill and its texts. Its ports are the points where lines join it. Symbols are made for schematics, drawn in drafting views; a symbol may stand in any view.

A detail item of the kind symbol stands at its a, and its b is the same point. Its symbol:

MemberMeaning
namerequiredthe symbol, by its name in the application's library: 1 to 40 characters, an application value
rotationoptionalits turn about a, in degrees counter-clockwise, from −360 to 360; absent, 0
mirroroptionaltrue: mirrored across its own vertical axis before it is turned
textsoptionalits texts, by the library's slots: each slot's name 1 to 20 characters, its text at most 40, such as {"tag": "P201"}

The library is the application's. A library gives each symbol its shapes, its texts' slots and its ports, in paper mm about its insertion point, y up; OpenEik lists none (section 8, rule 3; open question 50). A point (x, y) of a symbol stands in the view at a + scale · R(rotation) · (±x, y): −x where it is mirrored, R the turn counter-clockwise, scale the view's. An application that does not know a symbol's name keeps the item, and may draw it as a box with its name.

Lines joined to ports. A line's connect joins its ends to ports: {start?, end?}, each {detailId, port}, the symbol item and the name of its port in the library (1 to 40 characters, an application value). The line's first point is its start, and its last its end.

  • A joined end stands on its port. Its stored point is the port's, where the view draws it: the stored line is what is drawn.
  • When a symbol moves, turns or is mirrored, or its view's scale changes, a writer moves each end joined to its ports with them.
  • When an end is moved off its port, or the symbol goes, the end lets go: the writer takes it out of connect and leaves the end where it is.
  • An end put on a port, within 0.6 paper mm of it, MAY be joined to it by a writer.

The line from a pump's outlet to a valve's inlet, in a schematic at the scale 1:

[
  {
    "a": [100, 100],
    "b": [100, 100],
    "color": "#000000",
    "id": "pump-a",
    "kind": "symbol",
    "symbol": {
      "name": "pump",
      "texts": {
        "tag": "P201"
      }
    }
  },
  {
    "a": [150, 100],
    "b": [150, 100],
    "color": "#000000",
    "id": "valve-b",
    "kind": "symbol",
    "symbol": {
      "name": "isolatingValve"
    }
  },
  {
    "a": [104, 100],
    "b": [146, 100],
    "color": "#000000",
    "connect": {
      "end": {
        "detailId": "valve-b",
        "port": "in"
      },
      "start": {
        "detailId": "pump-a",
        "port": "out"
      }
    },
    "id": "line-a",
    "kind": "line"
  }
]

13.6 Rules

  1. Each of a view's visibility.systems.ids MUST be a system of section 6.9, alone or with a number from 1 to 99 after a colon, written without a leading zero. The schemas state this rule: ^(<system>|…)(:[1-9][0-9]?)?$.
  2. A textFrame, a display.textFrame and a display.mepTagFrame MUST give shape, and their members MUST be within the ranges of 13.2. A textBackground MUST be opaque or transparent. The schemas state this rule.
  3. display.mepTags, mepTagEdits and display.mepTagTemplates MUST be of the shapes of 13.3; the schemas state it, and mark each row of a template "x-eik-application": true. A reader MUST keep a template's rows unchanged, and MUST NOT need to read them: an application that does not know a template's language draws the tags as it would without it.
  4. The element that a tag of the field room names MUST be a room of the model.
  5. A detail item of the kind symbol MUST carry symbol, and a detail item of another kind MUST NOT. The schemas state this rule.
  6. A detail item of another kind than line MUST NOT carry connect. The schemas state this rule.
  7. Each detailId of a line's connect MUST name a detail item of the kind symbol of the line's own view.
  8. A writer MUST keep a joined end on its port, as 13.5 says, or let it go.
  9. A symbol's name, a port's name and a text's slot are the application's: a validator MUST NOT refuse one because a library does not hold it, and a reader keeps them unchanged.

Reference: systemFilterSchema in src/core/views/systemFilterSchema.ts and systemFilterHides in src/core/views/systemPlans.ts (Stomra work 611), withSystemFilterIds and looseSystemIds in systemFilterSchema.ts (669); textFrameSchema, frameOf and frameBox in src/core/annotations/textFrame.ts (608); mepTagModes, mepTagEditSchema and taggedRuns in src/core/elements/mepTagSchema.ts and mepTags.ts (its work 540); viewTagTemplatesSchema in src/core/elements/mepTagTemplateSchema.ts and the filling in mepTagTemplate.ts (621); roomTagRows and roomTagPoint in src/core/elements/roomTag.ts, and tagItemSchema in src/core/sheets/tagItemSchema.ts (622); tagLineUpCommandSchema and linedUpView (623); symbolItemSchema and lineConnectSchema in src/core/sheets/symbolItemSchema.ts, the refinement of a detail item in src/core/sheets/documentation.ts (rules 5 and 6), symbolGeometry in src/core/annotations/symbolLibrary.ts, and connectedDetails and withConnectedSymbols in src/core/annotations/symbolConnections.ts (634).

14. Members of views, detail items and detail families

Part of 0.2. OpenEik 0.1 had it as the proposal adopted: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), the members of this section are members a schema of 0.1 does not know, which a reader keeps (Part I, 10). The section adds them, the cut pattern clt (section 4, rule 4), the reference a bolt group's bolts.through holds (section 7), a slab edge element's notches (Part IV, 4.6, rules 12 to 14), and no file. Their schemas, in schemas/0.2/adopted/, are generated from the reference implementation's, as the schemas of 0.1 are.

By the steward's decision of 30 October 2026 (open questions, 115). The reference implementation writes these members, and another tool needs them to read, draw, check or print the building, so they join 0.2. What only the reference implementation's own interface reads, it writes under its prefix from 0.2 (Part I, 11.5).

14.1 Elements cast in one pour

  1. An element MAY carry castWith, the id of another element that it is cast with in one pour, as a foundation poured with the slab on it. It SHOULD name an element of the model (section 7), and MUST NOT name the element itself: a validator warns of one that does (cast-with).
  2. Where a section cuts both elements, their cut regions of one material, at one depth of the cut, are drawn as one region: no line is drawn where they meet, and their regions of other materials keep their lines. It changes no body and no quantity, and the two stay two elements.

14.2 A layer drawn with a detail family

A drawing of a type's build-up (a view's assembly, section 14.3, rule 4) draws each layer where it is cut, by its material's pattern (section 4).

  1. A layer (section 5.1) of a type of the category slab, wall, foundation, roof or ceiling MAY carry drawing, {familyId?, typeId?, spacing?, offset?, rotation?, align?}: the detail family its cut is drawn with in such a drawing, in place of its material's pattern. familyId SHOULD name a detail family of the model (project/detail-families), and typeId names one of that family's types; a validator warns of a typeId the family does not have, or one without a familyId (layer-drawing). Without familyId the layer draws nothing of its own where it is cut, so that what the layer beside it draws shows there, as the battens a roof's tiles hang on.
  2. The copies. The family is drawn along the layer again and again, each copy centred on its place: at offset mm from the layer's start (0 where it is absent) and every spacing mm from there, both ways, as far as a copy reaches into the layer; where spacing is absent, the copies lie side by side, each its own extent along the layer from the next. Each copy is turned by rotation degrees, as a detail item's rotation turns it. Across the layer it lies on the layer's middle, or, with align inside or outside, its box against the layer's inside or outside face; a family with an insertion point (section 14.5) hangs by that point instead, on the layer's middle, inside or outside face.
  3. Its axes. The family's x runs along the layer, a roof's from its eave up and a wall's from its foot up, and its y out of the building.
  4. A layer's drawing changes no body and no quantity.

14.3 A view

  1. An electrical plan's devices. A plan's display.electrical says which electrical devices (Part XIII, 2) it draws: power, every device but a data outlet, a TV outlet, a speaker and an access point (the kinds dataOutlet, tvOutlet, speaker and accessPoint); data, those four kinds alone; absent, all of them. On a view of another kind it means nothing, and a validator warns of it there (adopted-kind).
  2. Level heads. display.levelLabels, false, draws a section's, an elevation's or a callout's level lines without their heads, the mark that gives the level's name and its elevation; absent or true, with them.
  3. The sides of a build-up. display.sideMarks, false, leaves out the marks that a drawing of a type's build-up (rule 4) sets beside its sides, outside and inside for a wall, top and underside for a horizontal type; absent or true, they are drawn. Their words are the drawing application's.
  4. A build-up's pitch. A view's assembly, {typeId, mode} in 0.1, draws the build-up of the type typeId names: cut vertically (vertical), horizontally (horizontal), or as an isometric (isometric). Its pitch, from 0 to 75 degrees, is the pitch a roof type's vertical cut is drawn at, turned about its middle and rising to the right. Absent, it is the pitch every roof of the type has, the slope of its first slope-defining edge to 0.1 degrees (0 for a roof without one), where they all have the same, else 0, level.
  5. Line weights. A view's lineWeights, true, says that the view is drawn on screen with its line weights; absent, it is drawn in thin lines, every line one device pixel wide. It is the view's own, so that it is the same for everyone who opens the view. A print draws a view's line weights whatever it says.
  6. A 3D view's perspective camera. A 3D view MAY carry camera, {position, target, fov}: where its perspective camera stands and the point it looks at, each [x, y, z] in mm in the model's axes, and its vertical field of view in degrees, from 10 to 120. position and target MUST be at least 1 mm apart; a validator reports a camera whose two points are not (camera-points). A tool that shows the view in perspective, or renders it, looks so. A sheet draws a 3D view without perspective, by the camera of its origin, direction and axonometricElevation, which camera leaves as they are, so a 3D view that a sheet places SHOULD NOT carry camera. On a view of another kind it means nothing (adopted-kind).

14.4 Detail items

  1. A revision cloud. A detail item of the kind line MAY carry cloud, {arc, inward?, revision?, tag?, tagHidden?}, which draws it as a revision cloud. Its points (its points, else a and b) are the cloud's corners, in order round it, and it is closed. Between them it is drawn as scallops of arc mm on the paper, from 1 to 50, so that they keep their size at any scale, bulging out of the cloud, or into it where inward is true. revision, at most 20 characters, is the mark of the revision the cloud belongs to, which its tag shows: a triangle with the mark in it, centred on tag, a point of the view, else on the cloud's first corner. The tag is not drawn where tagHidden is true or the mark is empty. A reader that does not draw clouds draws the corners as a closed line. On an item of another kind cloud means nothing (adopted-kind).
  2. A schedule's headings. A schedule's schedule.headings gives its columns' headings by the field each column shows (number, name, area and the others its schema names), each of 1 to 80 characters, in place of the headings the drawing application gives them; a column whose field it does not name keeps the application's.
  3. A legend's heading. A legend's legend.title, of 1 to 120 characters, is its own heading, in place of the one the drawing application gives its scheme.
  4. A build-up text. A detail item of the kind leader MAY carry buildUp, {typeId, uValue?}, which makes its text the build-up of the type typeId names: a line for each of its layers, in the order of section 5.1, rule 6, a wall's exterior first and a horizontal type's top first, each with its thickness and its name; and above them the type's U value (section 5.5), unless uValue is false. The text is worked out from the type each time it is drawn, so that it follows the type; its words and its layout are the drawing application's. Where the item gives its own text, that text is drawn instead. typeId SHOULD name a type of the model that has layers: a validator warns of a type without (build-up-type). On an item of another kind buildUp means nothing (adopted-kind).
  5. An item that follows a group's instance. A detail item of a plan MAY carry follows, the mark of the instance of a group (section 10) it moves and turns with: the id of a copy, or of an original whose kept elements are its own instance (its frame absent or its own id). Only a free item follows an instance: neither a tag, a view reference, a schedule or a legend, nor an item with tag, dimensionReferences, hatchHost, host or modelPoint, each of which follows what it is tied to. On such an item, or on an item of a view that is no plan, follows is not read, and a validator warns of it (follows-free). It SHOULD name an instance of the model.
  6. Moving with it. A command that moves or turns an instance (copy.move, Part V, 2.1) moves and turns the items that follow it, as its elements move: each is turned by the change of the instance's turn about the point its at named before, then moved by the change of its at. Where the instance goes (copy.remove, copy.unique, original.dissolve), an item that follows it stays where it is, and the command takes its follows away.
  7. A region cut by groups. A detail item of the kind region in a plan MAY carry cutBy, 1 to 200 marks of instances of groups (rule 5), whose floors are taken out of it where it is drawn. An instance's floors are, in plan, the outlines of its slabs and foundations on the plan's level; where it has none there, those on the lowest level it has any on; where it has none at all, the convex hull of what it covers in plan. They are worked out where the instance stands each time the region is drawn, so that a hole moves with its instance. The region keeps its own outline, and is drawn as that outline less theirs. Each mark SHOULD name an instance of the model; where an instance goes, its mark is taken out of cutBy, and its hole closes. On an item of another kind cutBy means nothing (adopted-kind).
  8. A text that measures nothing. A detail item of the kind text MAY carry notDimension, true: its words read as a number, and measure nothing, so that a check that holds a drawing's written dimensions to the model leaves it out. An item of another kind MUST NOT carry it (not-dimension).

14.5 A detail family's insertion point

  1. A detail family MAY carry insertion, [x, y], the point of the family that a placed copy hangs on: a single item of the family at its place, each copy of a repeating detail along its path, each copy of a layer's drawing (section 14.2). Each of the two is in the family's own mm, a number or bound to its parameters as a sketch's points are ({parameter, factor?, offset?} or {terms, offset?}); a parameter it names MUST be one of the family's (insertion-parameter). Without insertion, a single item hangs on the family's origin, and a repeated copy on the middle of its box, so that an edit that changes the box moves the repeated copies; with it, an edit never does.

14.6 A terminal's tag without its size

  1. A terminal type's terminal.tagSize, false, says that its elements' tags give no size: the plan's automatic tags and a template's size field (section 13.3). Absent or true, they give the size of the type's connection of the terminal's own system, else of its first connection. A component in a pipe or a duct (section 6.9) is tagged with its run's size whatever its type says.

Reference: castGroups in src/core/geometry/sectionOutline.ts (Stomra work 508); layerDrawingSchema and layerCopies in src/core/assemblies/layerDrawing.ts, roofTypePitch in assemblyViews.ts, and buildUpSchema and buildUpText in src/core/sheets/buildUpSchema.ts and src/core/assemblies/buildUpText.ts (its work 556); viewElectrical in src/core/views/viewVisibility.ts and electricalPart in src/core/elements/electrical.ts (537); levelLabels and sideMarks in a view's display (651); lineWeights in viewSchema of src/core/model/model.ts (706); perspectiveCameraSchema in src/core/views/perspectiveSchema.ts and perspectiveOf in perspectiveCamera.ts (747); revisionCloudSchema and cloudTag in src/core/annotations/revisionCloudSchema.ts and revisionCloud.ts (523); canFollow and moveFollowers in src/core/model/groupDetails.ts (603); instanceFloors in src/core/annotations/regionCuts.ts (678); the refinement of a detail item in src/core/sheets/documentation.ts (notDimension, 695); familyAnchor in src/core/annotations/detailFamilies.ts (556); tagSize in terminalTypeSchema and terminalTag in src/core/elements/mep.ts (543).