Skip to content

Instantly share code, notes, and snippets.

@cdiggins
Last active February 17, 2026 17:54
Show Gist options
  • Select an option

  • Save cdiggins/7deeefe0779a7149b78cb8c01f1293e3 to your computer and use it in GitHub Desktop.

Select an option

Save cdiggins/7deeefe0779a7149b78cb8c01f1293e3 to your computer and use it in GitHub Desktop.
BIM Open Schema
using System.Collections.Generic;
namespace Ara3D.BimOpenSchema;
/// <summary>
/// Contains all the BIM Data for a discipline or federated model.
///
/// Optimized for efficient loading into analytical tools as a set of Parquet files, or a DuckDB database.
/// Provides a simple and efficient standardized way to interact with BIM data from different tools,
/// without having to go through APIs, or ad-hoc representations.
///
/// It is optimized for space and load-times, not ease of queries.
/// A typical workflow would be to ingest this into a DuckDB database then to use SQL to
/// create denormalized (wide) tables depending on an end-user's specific use-case
/// and what data they are interested in.
///
/// This expresses the schema as an object model that is
/// independent of any specific serialization format,
/// whether it is JSON, Parquet, CSV, SQLite, or something else.
///
/// When exporting to a database, each list corresponds to a table.
/// When exporting to parquet, each list corresponds to a parquet file.
///
/// This data structure can also be used directly in C# code as am efficient
/// in-memory data structure for code-based workflows.
/// </summary>
public interface IBimData
{
Manifest Manifest { get; }
IReadOnlyList<ParameterDescriptor> Descriptors { get; }
IReadOnlyList<Parameter> Parameters { get; }
IReadOnlyList<Document> Documents { get; }
IReadOnlyList<Entity> Entities { get; }
IReadOnlyList<string> Strings { get; }
IReadOnlyList<Point> Points { get; }
IReadOnlyList<EntityRelation> Relations { get; }
IReadOnlyList<Diagnostic> Diagnostics { get; }
BimGeometry Geometry { get; }
}
// Usually stored as a .JSON file in the BOS package.
public class Manifest
{
public const string CurrentVersion = "0.3";
public string BimOpenSchemaVersion { get; set; } = CurrentVersion;
public string GeneratorApplication { get; set; }
public string GeneratorVersion { get; set; }
public object ExportOptions { get; set; }
}
//==
// Enumerations used for indexing tables. Provides type-safety and convenience in code
//
public enum EntityIndex : int { }
public enum PointIndex : int { }
public enum DocumentIndex : int { }
public enum DescriptorIndex : int { }
public enum StringIndex : int { }
public enum RelationIndex : int { }
//==
// Main data type
/// <summary>
/// Corresponds roughly to an element in the Revit file.
/// Some items are associated with entities that are not expressly derived from Element (e.g., Document,
/// </summary>
public record struct Entity
(
// ElementID in Revit, and Step Line # in IFC
// Will be unique when combined with a DocumentIndex (e.g., "${LocalId}-{Document}" would be a unique string identifier within the database).
// But multiple documents can share the same entity
long LocalId,
// UniqueID in Revit, and GlobalID in IFC (not stored in string table, because it is never duplicated)
StringIndex GlobalId,
// The index of the document this entity is part of
DocumentIndex Document,
// The name of the entity
StringIndex Name,
// The category of the entity
EntityIndex Category,
// The "type" of the entity if it is an instance
EntityIndex Type
);
/// <summary>
/// Corresponds with a specific Revit or IFC file
/// </summary>
public record struct Document
(
StringIndex Title,
StringIndex Path
);
/// <summary>
/// Represents 3D location data.
/// </summary>
public record struct Point
(
float X,
float Y,
float Z
);
/// <summary>
/// Important for grouping the different kinds of parameter data ...
/// otherwise we can have two parameter with the same name, but different underlying parameter types.
/// </summary>
public enum ParameterType
{
Int = 0,
Bool = Int,
Number = 1,
Entity = 2,
String = 3,
Point = 4,
}
/// <summary>
/// Meta-information for understanding a parameter
/// </summary>
public record struct ParameterDescriptor
(
StringIndex Name,
StringIndex Units,
StringIndex Group,
ParameterType Type
);
//==
// Parameter data
/// <summary>
/// If the Descriptor Type is a Number
/// divide Value by 10,000 to get the precise value.
/// </summary>
public record struct Parameter
(
EntityIndex Entity,
DescriptorIndex Descriptor,
int Value
);
//==
// Relations data
/// <summary>
/// Expresses different kinds of relationships between entities
/// </summary>
public record struct EntityRelation
(
EntityIndex EntityA,
EntityIndex EntityB,
RelationType RelationType
);
/// <summary>
/// The various kinds of relations, aimed at covering both the Revit API and IFC
/// </summary>
public enum RelationType
{
// For parts of a whole. Represents composition.
PartOf = 0,
// For elements of a group or set or layer. Represents aggregations.
MemberOf = 1,
// Represents spatial relationships. Like part of a level, or a room.
ContainedIn = 2,
// For parts or openings that occur within a host (such as windows or doorways).
HostedBy = 3,
// For parent-child relationships in a graph (e.g. sub-categories)
ChildOf = 4,
// Represents relationship of compound structures and their constituents
HasLayer = 5,
// Represents different kinds of material relationships
HasMaterial = 6,
// Two-way connectivity relationship. Can assume that only one direction is stored in DB
ConnectsTo = 7,
// MEP networks and connection manager
HasConnector = 8,
// For space <-> boundary relationships.
BoundedBy = 9,
// Can traverse from one space to another (e.g., portal)
TraverseTo = 10,
// Relationship between openings (e.g., doorways, window frame) and hosts
Voids = 11,
// When an object like a door or window fills a void
Fills = 12,
// For finishes on walls/floors/ceilings
Covers = 13,
// For MEP systems (e.g., HVAC) providing service to a zone
Serves = 14,
}
public enum DiagnosticType
{
RevitWarning,
RevitError,
ExporterWarning,
ExporterError,
ExporterInfo
}
public record struct Diagnostic
(
DiagnosticType Type,
DocumentIndex Document,
EntityIndex Entity,
StringIndex Message
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment