GeometryHelper.IfcConvert 11.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package GeometryHelper.IfcConvert --version 11.0.1
                    
NuGet\Install-Package GeometryHelper.IfcConvert -Version 11.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GeometryHelper.IfcConvert" Version="11.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GeometryHelper.IfcConvert" Version="11.0.1" />
                    
Directory.Packages.props
<PackageReference Include="GeometryHelper.IfcConvert" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GeometryHelper.IfcConvert --version 11.0.1
                    
#r "nuget: GeometryHelper.IfcConvert, 11.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GeometryHelper.IfcConvert@11.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GeometryHelper.IfcConvert&version=11.0.1
                    
Install as a Cake Addin
#tool nuget:?package=GeometryHelper.IfcConvert&version=11.0.1
                    
Install as a Cake Tool

GeometryHelper.IfcConvert

NuGet Version License: MIT

Converts geometry between IFC models (via xBIM) and GeometryHelper (GeometryHelper): points, vectors, transformation matrices, planar faces, curved surfaces, and 3D solids.

What It Does

GeometryHelper.IfcConvert bridges building information models in IFC (IFC2x3, IFC4, IFC4x3) to pure computational geometry structures in GeometryHelper:

  • Converts 3D points (XbimPoint3D), vectors (XbimVector3D), and homogeneous transformation matrices (XbimMatrix3D / GeoTransform3).
  • Converts IFC boundary representations and swept solids (IXbimSolid, IIfcGeometricRepresentationItem, IIfcProduct, IIfcMappedItem) into GeoSolid3 bodies.
  • Handles planar faces with both outer boundaries and inner cutout loops/holes (InnerBounds).
  • Automatically tessellates curved and non-planar faces into planar triangles, maintaining strict coplanarity compliance for GeometryHelper.
  • Preserves hierarchical coordinate placements across nested mapped items and product placements.
  • Provides thread-safe IFC store caching (IfcStoreCache) with GlobalId indexing.

Installation

dotnet add package GeometryHelper.IfcConvert

The package is self-contained: it bundles all required customized xBIM managed assemblies and the 64-bit native geometry engine (Xbim.Geometry.Engine64.dll).

The library targets netstandard2.0, but it runs only in a .NET Framework 4.8, Windows x64 process: the bundled xBIM geometry engine is C++/CLI built for .NET Framework 4.7.2. A .NET Core / .NET 5+ project can install the package, build against it and open a model, but every geometry conversion fails when the engine loads. Nothing is thrown: the product comes back without solids, and its Warnings read Conversion failed: FileLoadException: Failed to load Xbim.Geometry.Engine64.dll.

Platform Target Requirement (x64): Because the underlying geometry engine is a 64-bit native C++/CLI binary, any consuming application must run as an x64 process.

Running under AnyCPU with the default "Prefer 32-bit" setting enabled will result in a BadImageFormatException:

System.BadImageFormatException: Could not load file or assembly 'GeometryHelper.IfcConvert' or one of its dependencies.
An attempt was made to load a program with an incorrect format.

Ensure your consuming project's .csproj specifies <PlatformTarget>x64</PlatformTarget>:

<PropertyGroup>
  <PlatformTarget>x64</PlatformTarget>
</PropertyGroup>

(Or if you must use AnyCPU, set <Prefer32Bit>false</Prefer32Bit> to ensure Windows executes it as a 64-bit process).

How Winding and Orientations are Handled

GeometryHelper measures volumes and point containment assuming outward-pointing face normals. Orientation is propagated across shared edges (neighbouring faces must traverse a shared edge in opposite directions), so bodies with mixed orientations, such as boolean results, are repaired face by face. Each connected shell is then turned outwards by the sign of its volume. Hole loops are stored wound like their boundary, as GeoSolid3 expects.

Planar vs Curved Geometry

A solid whose faces and edges are all planar and straight is read face by face: outer loops and hole loops become GeoPolygon3 rings. A face that strays out of flat, or a hole off its face's plane, as rounding in the file leaves some, comes as triangles on its own corners (GeoFace3.FromLoops), which keep every edge it shares with its neighbours, so the body stays closed and its holes stay open. A solid with any curved face or curved edge (round columns, tubes, a plate with a bolt hole) is triangulated as a whole by the geometry engine, so shared edges are subdivided once and the mesh is watertight. Reading it face by face would collapse circular loops to their vertices and drop the hole.

IfcConvertOptions.DeflectionTolerance is the chord tolerance in output units. The default (0) uses the deflection xBIM derives from the model's length unit, which keeps round sections within about 0.5 % of their true volume.

Conversion Rules

  • Body only: the Body representation is converted (then Body-*, then Facetation). Box, Axis, Clearance and similar representations are ignored so they cannot add extra solids.
  • Mapped items: world = MappingTarget × MappingOrigin × local, the order IfcOpenShell uses. (xBIM's own scene builder applies them the other way round, and ISO 10303-43 reads the origin as inverted. All three agree when MappingOrigin is the identity, which covers most exports.)
  • Openings (ApplyVoids, off by default for speed): with ApplyVoids = true, openings are cut from the exact B-rep by the geometry engine, in the host's frame, so they are also correct with CoordinateSpace.Local and for round openings. If an opening cannot be cut that way, conversion falls back to the GeoSolid3 boolean. Cutting costs time: on a Tekla steel model, the first conversion of 500 beams (183 of them with bolt holes or cuts) took 1.5 s uncut and 29 s with openings cut. Most of those cut beams also came back not closed (a warning says so): their volumes remain plausible, but point containment may be off.
  • Model-wide queries (GetAllGeometries, GetAllSolids, EnumerateSolids, EnumerateGeometries) skip openings, spatial elements, annotations, grids, virtual elements, ports and structural analysis items unless IncludeNonPhysicalProducts is set. Queries by GlobalId or type name are not filtered.
  • Every product at once: GetAllGeometries returns the products EnumerateGeometries gives, in its order, all converted before it returns, and a file xBIM holds in memory (IsInMemory) is converted on every core at once, each product under the tolerance in force where you call. GetAllSolids converts the same way. The answers are the ones converting one product at a time gives. Three Tekla models of about 21,000 products each, read whole outside Tekla on 24 logical processors, converted in 28 s instead of 85 s, and in 14 s instead of 80 s with the server garbage collector. EnumerateGeometries converts as it is read instead, and holds fewer products in memory at once.
  • Bodies in one order: where the geometry engine cuts one brep into several shells, the bodies come back in order of where they lie, low corner first, and their warnings in the order of their text. The engine hands the shells back in an order that is not the same from one reading of a file to the next.
  • Assemblies: with IncludeAggregatedParts, a product also returns the parts it aggregates, for example a Tekla IfcElementAssembly, which has no body of its own.
  • Units: property and quantity units come from the value when present, otherwise from the project unit for that measure. Tekla, for example, exports lengths in mm but areas in m2 and volumes in m3. Properties attached to the product's type are included, and occurrence values override them.
  • Duplicate GlobalIds are listed in IfcStoreCache.DuplicateGlobalIds; only the first product with such an id is reachable by GUID.

Choosing Products by Name

SkipNames leaves out the products whose names match, and OnlyNames keeps only those. A product left out comes back empty, and its name is checked before any geometry is built, so it costs next to nothing. Both lists ignore case and take * as a wildcard. A name matching both is skipped, and a product with no name matches only "*" in OnlyNames.

// Everything but the bolts.
var noBolts = new IfcConvertOptions { SkipNames = { "Bolt assembly" } };

// Only the main members, or only the plates: the names are those the model gives its parts.
var members = new IfcConvertOptions { OnlyNames = { "BEAM", "GIRDER", "PRD_COLUMN", "COLUMN" } };
var plates = new IfcConvertOptions { OnlyNames = { "*PLATE" } };

// AddOnlyNames and AddSkipNames take names one by one or as a list, trim them, ignore blank ones and chain,
// which suits names read from a settings file or a text box.
var fromSettings = new IfcConvertOptions().AddOnlyNames(" BEAM", "GIRDER ", "").AddSkipNames("Bolt assembly");

Reading every product of a 115 MB Tekla steel model (21,188 products) took about 17 minutes, 96 % of it on its 5,512 bolts. Leaving the bolts out took 35 s, only the main members 3.5 s, and only the plates 3.8 s.

Both lists also apply to the parts an assembly aggregates (IncludeAggregatedParts), so with OnlyNames, list the parts wanted as well as the assembly. Neither applies to the openings that cut a product.

Conversion Warnings

A representation item that fails is skipped rather than failing the whole product, and every such problem is recorded in IfcProductGeometry.Warnings (HasWarnings for a quick check). Examples: an item that produced no solid, a shape the engine reports as invalid (skipped, because reading it can fault inside the native engine), faces left out, a solid that is not closed, openings that fell back to the GeoSolid3 boolean. Messages about an item start with its STEP entity label, e.g. #123 IfcExtrudedAreaSolid: It produced no solid., so the item can be found in the IFC file.

foreach (IfcProductGeometry g in model.EnumerateGeometries())
{
    foreach (string warning in g.Warnings)
    {
        Console.WriteLine($"{g.GlobalId} {g.IfcType}: {warning}");
    }
}

Moving Converted Geometry

To place a product somewhere else, for example where a Tekla reference model was inserted, use IfcProductGeometry.TransformBy rather than GeoSolid3.TransformBy on each body:

IfcProductGeometry placed = geometry.TransformBy(transform, options.Tolerance);

The conversion builds faces with a finer area threshold than Tolerance.Global (EqualPoint squared rather than EqualVector), so a triangulated or cut body can carry thin sliver faces that are valid there. GeoSolid3.TransformBy, given a transformation that stretches or shears, checks every face against Tolerance.Global again and throws on the whole body (a move that keeps every length it carries over as it is): on a 115 MB Tekla IFC, 3 of 500 beams read with ApplyVoids = true were once lost that way. TransformBy rebuilds the faces with the tolerance of the conversion, and keeps a face that lands a rounding off flat as triangles on its own corners, so pass the Tolerance of the options the product was read with (the default is Tolerance.Global, as in the options). It returns a copy: bodies, open surfaces and Placement are carried, the GlobalId, name, type, tag and warnings are kept, and the cached geometry is left unchanged. What the transformation itself collapses, such as a body scaled down to nothing, is left out and added to Warnings.

Quick Start

using System;
using System.Collections.Generic;
using System.IO;
using GeometryHelper;
using GeometryHelper.IfcConvert.Core;
using GeometryHelper.IfcConvert.Models;
using GeometryHelper.Geometry;

string[] ifcFiles = { @"C:\Models\Building-A.ifc", @"C:\Models\Building-B.ifc" };
// Tolerances are in the output unit, and the default suits millimetres: in metres, a thousand times smaller.
var metres = new Tolerance(1E-6, 1E-8, Tolerance.DefaultEqualAngleRad, 1E-6);
var options = new IfcConvertOptions { TargetUnit = LengthUnit.Meters, Tolerance = metres };

foreach (string ifcFile in ifcFiles)
{
    // 1. Get the model from the process-wide cache (pure GeometryHelper, zero xBIM compile-time dependencies).
    //    The first call for a file parses it; later calls with the same path return the same instance at once,
    //    together with every geometry and property it has already converted.
    //    Pass the full path of the file; a relative path throws ArgumentException.
    IfcStoreCache model = IfcStoreCache.GetOrCreate(ifcFile);

    // 2. Query product metadata catalog (super lightweight, zero 3D conversion cost)
    IReadOnlyList<IfcProductMetadata> catalog = model.GetProductCatalog();
    Console.WriteLine($"{Path.GetFileName(ifcFile)}: {catalog.Count} products, unit {model.OriginalLengthUnit}");

    // 3. Loop over every product: complete geometry information (solids, surfaces, AABB, warnings)
    foreach (IfcProductMetadata product in catalog)
    {
        IfcProductGeometry geom = model.GetGeometry(product.GlobalId, options);
        if (geom == null || geom.IsEmpty)
        {
            continue;
        }

        Console.WriteLine($"Name: {geom.Name}, GUID: {geom.GlobalId}, Type: {geom.IfcType}");
        Console.WriteLine($"  Total Volume: {geom.TotalVolume:F4} m3");
        Console.WriteLine($"  Bounding Box: Min={geom.BoundingBox.Min}, Max={geom.BoundingBox.Max}");

        foreach (GeoSolid3 s in geom.Solids)
        {
            Console.WriteLine($"  Solid Faces: {s.Faces.Count}, Volume: {s.GetVolume():F4} m3");
        }

        foreach (GeoFace3 surface in geom.OpenSurfaces)
        {
            Console.WriteLine($"  Surface Area: {surface.Area:F4} m2");
        }

        foreach (string warning in geom.Warnings)
        {
            Console.WriteLine($"  Warning: {warning}");
        }
    }

    // 4. Retrieve all solids of a specific IFC type (e.g. IfcWall, IfcBeam, IfcColumn)
    var optionsWithVoids = new IfcConvertOptions { TargetUnit = LengthUnit.Meters, Tolerance = metres, ApplyVoids = true };
    IReadOnlyList<GeoSolid3> wallSolids = model.GetSolidsByType("IfcWall", optionsWithVoids);
    Console.WriteLine($"Loaded {wallSolids.Count} wall solids with voids subtracted.");
}

// 5. Cached models stay in memory until released. Release a model when no code uses it any more.
IfcStoreCache.ClearGlobalCache(ifcFiles[0]);                     // one model
IfcStoreCache.ClearGlobalCache();                                 // every cached model

GetOrCreate or Open

Measured on a 115 MB Tekla IFC (25 000 products):

Time Memory
Open (every call parses the file again) about 6 s released on Dispose
GetOrCreate, first call for a path about 6 s about 200 MB kept
GetOrCreate, later calls for the same path 0 ms same instance
Geometry of 500 beams, first request / again (the default, openings not cut) 1.5 s / 1 ms cached with the model
The same with ApplyVoids = true (openings cut) 29 s / 1 ms cached with the model
  • Use GetOrCreate when the same files are read again during the process's life, for example a plugin command run several times, or several features querying one model. It does not make the first opening faster, so for files each read only once, Open inside using frees memory as soon as possible.
  • Dispose does nothing on a cached instance, so wrapping it in using does not release it. Call ClearGlobalCache(path) or ClearGlobalCache() once no code uses the model any more; clearing a model that another caller is still using leaves that caller with a disposed model.
  • GetOrCreate and ClearGlobalCache(path) take the full path of the file (C:\Models\A.ifc or \\server\share\A.ifc) and throw ArgumentException for anything relative, including \Models\A.ifc and C:A.ifc: a relative path depends on the current folder, so one file could be cached twice. Different spellings of the same full path (/ or \, . and .. segments, letter case) share one cached model.
  • The cache does not watch the file. After the file changes on disk, call ClearGlobalCache(path) to reload it.
  • Opening happens under the cache lock, so threads opening different files wait for each other.
  • Geometry that several products share -- a representation map every bolt of a size maps, a brep several beams point at -- is built once per file and settings and placed for each product, unless openings are cut from it (ApplyVoids). 2,711 products of a 116 MB Tekla model took 7.4 s instead of 3.6 minutes.
  • IsInMemory says whether xBIM read the file whole into memory or keeps it in a database on disk, as it does a 114 MB one. The products of a file in memory can be converted from several threads at once; convert those of a file on disk one at a time.
  • IfcStoreCache.GetGlobalCacheInfo() lists the cached paths and their product counts.

Using with Tekla Structures

GeometryHelper.TeklaConvert reads the objects of IFC reference models through this package: it finds the IFC file of each reference model, reads it in millimetres at the reference model's scale, and places the result where Tekla shows it, in the current work plane. Install the GeometryHelper.TeklaConvert.{year} package of your Tekla version, which brings this one, and see its IFC reference models section for the details.

The example draws every face of the selected reference objects as control polycurves, outer boundaries in red and holes in white:

using System.Collections.Generic;
using GeometryHelper.Geometry;
using GeometryHelper.TeklaConvert;
using Tekla.Structures.Model;
using Tekla.Structures.Model.Operations;

public static class IfcReferenceSample
{
    public static void DrawSelectedReferenceObjects()
    {
        Model model = new Model();

        // The IFC objects selected in the model view (Tekla.Structures.Model has a ModelObjectSelector too, hence the full name).
        var selected = new List<ReferenceModelObject>();
        ModelObjectEnumerator selection = new Tekla.Structures.Model.UI.ModelObjectSelector().GetSelectedObjects();
        while (selection.MoveNext())
        {
            if (selection.Current is ReferenceModelObject referenceObject)
            {
                selected.Add(referenceObject);
            }
        }

        // In the current work plane, which is where control polycurves are drawn too: no work plane to switch.
        GeoSolid3[] solids = selected.ToGeoSolids();

        // Every face as control polycurves (GeometryDraw): outer boundaries red, holes white.
        ControlPolycurve[] drawn = solids.DrawToTekla();

        model.CommitChanges();
        Operation.DisplayPrompt($"{solids.Length} solid(s), {drawn.Length} polycurve(s), from {selected.Count} reference object(s).");
    }
}

Notes:

  • GUIDs: the EXTERNAL.GUID report property of a reference object is its IFC GlobalId, the key of every IfcStoreCache query. ReferenceModelObject.GetIfcGuid() reads it, and ReferenceModel.GetReferenceModelObjectByExternalGuid(guid) goes the other way.
  • Cache lifetime: a plugin or extension runs inside the Tekla process, so each IFC file is parsed once and every later run is served from the cache. A standalone .exe is a new process each time, so it parses the files on every run.
  • Curved parts are triangulated, so a round tube draws hundreds of polycurves. Fine for a visual check; for anything else, work with the GeoSolid3 itself.

Matrix Transformations

xBIM uses row-vector convention ($v \cdot M$) with translations located in the fourth row (OffsetX, OffsetY, OffsetZ). GeoTransform3 uses column-vector convention ($M \cdot v$) with translations located in the fourth column. MatrixConvert.ToGeoTransform3 and MatrixConvert.ToXbimMatrix3D transpose the linear 3x3 block and swap offset rows/columns accordingly, ensuring mathematically identical coordinate transformations between the two systems.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on GeometryHelper.IfcConvert:

Package Downloads
GeometryHelper.TeklaConvert.2026

Converts geometry between Tekla Structures 2026 and GeometryHelper: points, vectors, segments, planes, coordinate systems, bounding boxes, transformation matrices, and the faces and loops of a Tekla solid. Also reads the objects of IFC reference models into solids placed where Tekla shows them, through GeometryHelper.IfcConvert. Built for Tekla Structures 2026; install the package whose year matches your Tekla.

GeometryHelper.TeklaConvert.2025

Converts geometry between Tekla Structures 2025 and GeometryHelper: points, vectors, segments, planes, coordinate systems, bounding boxes, transformation matrices, and the faces and loops of a Tekla solid. Also reads the objects of IFC reference models into solids placed where Tekla shows them, through GeometryHelper.IfcConvert. Built for Tekla Structures 2025; install the package whose year matches your Tekla.

GeometryHelper.TeklaConvert.2020

Converts geometry between Tekla Structures 2020 and GeometryHelper: points, vectors, segments, planes, coordinate systems, bounding boxes, transformation matrices, and the faces and loops of a Tekla solid. Also reads the objects of IFC reference models into solids placed where Tekla shows them, through GeometryHelper.IfcConvert. Built for Tekla Structures 2020; install the package whose year matches your Tekla.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
11.2.0 33 10/6/2026
11.1.0 34 10/6/2026
11.0.1 109 10/5/2026
11.0.0 114 10/5/2026
10.0.0 126 10/3/2026
9.0.2 130 10/1/2026
9.0.1 122 10/1/2026
9.0.0 123 10/1/2026
8.0.0 124 9/30/2026
7.0.0 153 9/29/2026
6.3.0 178 9/29/2026
6.2.0 182 9/29/2026
6.1.0 201 9/28/2026
6.0.0 197 9/28/2026
5.1.0 199 9/21/2026
5.0.0 199 9/21/2026
4.0.0 99 9/18/2026
3.2.0 100 9/17/2026

NEW IN 11.0.1
     - GeometryHelper.IfcConvert is released at 11.0.1 with GeometryHelper, whose booleans of solids, which cut the
       openings the geometry engine cannot, no longer hand back a closed result of the wrong volume where a plane could
       not cut a cell, nor leave thin cells open, and work out faces with two corners one over the other.

     NEW IN 11.0.0
     - GeometryHelper.IfcConvert is released at 11.0.0 with GeometryHelper, whose default tolerance is a thousandth of
       a millimetre now, where it was a hundredth: this BREAKS code that relied on points 0.001 to 0.01 apart being one.
       Read within the default, both IFC models of steelwork it was measured on came out closed, 141 464 and 129 403
       bodies, the second with 7 of its products warned of with their openings cut, where within a hundredth 12 were.
     - The openings the geometry engine cannot cut are cut by GeometryHelper's booleans, which close more of their
       results now.

     NEW IN 10.0.0
     - GeometryHelper.IfcConvert reads as before: it is released at 10.0.0 with GeometryHelper, which BREAKS code that
       measures a body: GeoSolid3.Volume, SurfaceArea and Centroid are GetVolume(), GetSurfaceArea() and GetCentroid()
       now, the material of a body with its openings cut out. The bodies converted here carry no openings, cut in or
       left out as ApplyVoids says, so they measure as before, and IfcProductGeometry.TotalVolume with them.
     - The openings the geometry engine cannot cut are cut by GeometryHelper's booleans, which no longer take two faces
       a fraction of a milliradian apart for one plane, nor misread a body wound inwards.

     NEW IN 9.0.2
     - Nothing in GeometryHelper.IfcConvert itself changed: it is released at 9.0.2 with GeometryHelper, whose surface
       meshes of faces with holes no longer lay triangles across the holes.

     NEW IN 9.0.1
     - Nothing in GeometryHelper.IfcConvert itself changed: it is released at 9.0.1 with GeometryHelper, whose solid
       booleans it cuts openings with when the geometry engine cannot. They no longer take material from a body that
       an opening only touches, nor lose slivers thinner than the tolerance.

     NEW IN 9.0.0
     - Nothing in GeometryHelper.IfcConvert changed: it is released at 9.0.0 with GeometryHelper.TeklaConvert, which
       removes FaceConvert.TryReadFace and LoopConvert.TryReadLoop.

     NEW IN 8.0.0
     - BREAKING, from GeometryHelper 8.0.0: the default tolerance is 1E-2 in the output unit, set for millimetres.
       Output in metres needs IfcConvertOptions.Tolerance = new Tolerance(1E-5, 1E-5, Tolerance.DefaultEqualAngleRad,
       1E-5).
     - A planar face is read from its loops with GeoFace3.FromLoops: a face out of flat, or a hole off its face's
       plane, comes as triangles on the corners the file gives, where a hole was dropped, without a word when out
       of flat, and left the body open. Placing a body keeps a face that lands off flat the same way.
     - The GeoSolid3 boolean openings fall back to no longer throws out of a product, which took a whole model
       read at once with it; an opening that cannot be cut is left uncut with a warning.

     NEW IN 7.0.0
     - Nothing in GeometryHelper.IfcConvert changed: it is released at 7.0.0 with GeometryHelper, whose label
       placement changed in ways that break code reading ArrangeItem.Offset.

     NEW IN 6.3.0
     - Nothing in GeometryHelper.IfcConvert changed: it is released at 6.3.0 with GeometryHelper, whose label
       placement takes a gap of its own for each side of a leader.

     NEW IN 6.2.0
     - Added IfcStoreCache.GetAllGeometries: every product across the model at once, the ones EnumerateGeometries gives and in its order, converted on every core at once when the file is held in memory, each under the tolerance in force where it is called. GetAllSolids converts the same way. The answers are the ones converting one product at a time gives: three Tekla models of about 21,000 products each, read whole outside Tekla on 24 logical processors, converted in 28 s instead of 85 s, and in 14 s instead of 80 s with the server garbage collector.
     - The bodies of one brep the geometry engine cuts into several shells come back in one order, of where they lie, and their warnings in the order of their text. The engine hands the shells back in an order that changed from one reading of a file to the next, and the bodies of a product, the clashes found with them and the warnings naming them by their place changed with it: House.ifc's railings, for one, and 5,529 of the 64,159 products of the Tekla models above.

     EARLIER
     - Modular architecture: separate models (IfcPropertySet, IfcPropertyValue, IfcProductMetadata), options, and internal converters.
     - Enhanced IfcStoreCache: thread-safe global caching, path canonicalization, and duplicate GlobalId detection.
     - Added lightweight metadata and property/quantity extraction without full 3D conversion cost.
     - Added IfcConvertOptions filters for non-physical products and aggregated assembly parts.
     - Added IfcConvertOptions.OnlyNames, the opposite of SkipNames: only products whose names match are converted, and the rest come back empty before any geometry is built. A 115 MB Tekla steel model read in 3.5 s for its main members, against about 17 minutes for every product. AddSkipNames and AddOnlyNames add names, trimmed and without blanks, and can be chained.
     - Breaking: IfcConvertOptions has a single, parameterless constructor. The constructor taking skip names and a tolerance was removed; use new IfcConvertOptions { Tolerance = ... }.AddSkipNames(...) instead.
     - Added IfcProductGeometry.TransformBy, which moves converted geometry without losing bodies: it rebuilds faces with the tolerance of the conversion, where GeoSolid3.TransformBy would throw on thin sliver faces.
     - Geometry that products share is built once. An item with nothing to cut from it (a brep several beams point at, a representation map every bolt of a size maps) is converted once per file and settings and placed for every product, where each product used to have it built again in the geometry engine. Tekla writes whole models that way: 2,549 objects selected from one convert in 3.7 s instead of 80 s, and 2,711 products of a 116 MB model in 7.4 s instead of 3.6 minutes, the bodies byte for byte the same.
     - Added IfcStoreCache.IsInMemory: whether xBIM read the file whole into memory or keeps it in a database on disk, as it does a 114 MB one. The products of a file in memory can be converted from several threads at once; those of a file on disk are best converted one at a time.
     - A face the file calls flat that strays from one plane by more than a polygon allows is read as triangles instead of being left out, which left its body open. Tekla writes coordinates to a tenth of a micron, and a corner rounded the other way leaves a face off its plane by a few ten-thousandths of a millimetre: 178 bodies of one Tekla steel model lost a face that way, and two plates came out with no body at all.
     - A closed shell written with a flat gap in it has the gap capped with a flat face, when that closes it. Tekla writes the nuts of bolts with one face running straight across a corner the faces beside it keep, leaving a sliver of a triangle open: 1,320 bolt bodies of one steel model and 9,321 of another came out open, and now come out closed. A gap that does not lie in one plane is left as it is.