HBC-2 Building Configurator — Architecture & Test Guide

benchmark-HBC-2 is a real-world FCS project: a steel agricultural building with parameterised openings, accessories, and cladding. Its purpose in the knowledge base is twofold:

  1. It is the hardest regression benchmark — if fli.exe can reproduce the pre-computed reference outputs, the engine is correct.
  2. It is the reference training target for Golem — by studying its structure Golem learns how real multi-file FCS projects are organised.

1 Physical Layout on Disk

TestData/
  benchmark-HBC-2/
    fcs-HBC-2-forUT/        ← FCS project source (run fli.exe from here)
      .fcsconfig.json        ← {"EngineVersion":"e4.trans"}
      _resources.fcs         ← project-wide library registry
      _Submodules/           ← standard libraries (fcs-common, fcs-construct)
      HiStruct_BuildingConfigurator2/   ← the engine
      AgriculturalBuilding.fcc/         ← concrete building instance
      BlankBuildingDuopitch.fcc/
      Buildings.fcc/                    ← base template with gblock definitions
      OfficeWithStorage.fcc/
      SportHall.fcc/
    Results/                 ← 12 pre-computed reference .hiscene.json files

CRITICAL: Always run fli.exe with fcs-HBC-2-forUT/ as the working directory. Every _resources.fcs uses relative paths like "_Submodules/fcs-common/...". If you run from any other directory the imports will fail.


2 _resources.fcs — Library Registry

Every subdirectory inside the project has its own _resources.fcs that simply re-exports from its parent:

# HiStruct_BuildingConfigurator2/Accessory/DownSpout/_resources.fcs
import "../_resources.fcs"

# HiStruct_BuildingConfigurator2/Accessory/_resources.fcs
import "../_resources.fcs"

# HiStruct_BuildingConfigurator2/_resources.fcs  (or any sub-engine folder)
import "../_resources.fcs"

The root _resources.fcs (at fcs-HBC-2-forUT/) is the one that actually binds short names to library files:

gclass {f}        filename ( "_Submodules/fcs-common/FcsFunction/FcsFunctions.fcs")
gclass {fit}      filename ( "_Submodules/fcs-common/FcsEntity/_FcsEntityResources.fcs")
gclass {comp}     filename ( "_Submodules/fcs-common/FcsComponent/_FcsComponentResources.fcs")
gclass {doc}      filename ( "_Submodules/fcs-common/FcsDocument/FcsDocTools.fcs")
gclass {test}     filename ( "_Submodules/fcs-common/FcsTest/_resources.fcs")
gclass {en}       filename ( "_Submodules/fcs-common/Eurocode/_resources.fcs")
gclass {asce}     filename ( "_Submodules/fcs-common/Asce/_resources.fcs" )
gclass {ubc}      filename ( "_Submodules/fcs-common/Ubc/_resources.fcs" )
gclass {analysis} filename ( "_Submodules/fcs-common/AnalysisSocket/_resources.fcs" )
gclass {fcsCss}   filename ( "_Submodules/fcs-common/FcsSections/_resources.fcs")
gclass {fcsMat}   filename ( "_Submodules/fcs-common/FcsMaterials/_resources.fcs")
gclass {dtools}   filename ( "_Submodules/fcs-common/DrawingTools/_resources.fcs")
gclass {btools}   filename ( "_Submodules/fcs-common/BuildingTools/_resources.fcs")
gclass {symbol}   filename ( "_Submodules/fcs-common/FcsSymbol/_resources.fcs" )

Every FCS file in the project can therefore write f.someFunction(...), fit.entity(...), comp.someComponent(...), etc, and the engine resolves them through this chain.


3 .fcc Folders — Component Projects

A folder with a .fcc extension is a component project — it is always a pair of files:

AgriculturalBuilding.fcc/
  BuildingsComponentMain_updated.fcs    ← parameter overrides + imports engine
  BuildingsComponentMain_viewAll.fcs    ← ENTRY POINT  (imports the two files above)

The _viewAll.fcs (or _view*.fcs) is the file you pass to fli.exe. It does two imports:

// BuildingsComponentMain_viewAll.fcs
import "BuildingsComponentMain_updated.fcs"
import "../../Buildings.fcc/BuildingsComponentMain_viewAll_toImport.fcs"

The _updated.fcs overrides individual parameters of the engine default settings:

// BuildingsComponentMain_updated.fcs
import "../HiStruct_BuildingConfigurator2/Geometry/BuildingsComponentMain.fcs"

site.definitionBuildingsBase[0].inWidth_m       = 24.716
site.definitionBuildingsBase[0].inEaveHeight_m  = 4.6
site.definitionBuildingsBase[0].inLength_m      = 35.556
site.definitionBuildingsBase[0].inRoofSlopePct  = 15.0
site.definitionBuildingsBase[0].inNrBays        = 7
// ... more parameter overrides

AgriculturalBuilding key dimensions: width = 24.716 m, length = 35.556 m, eave height = 4.6 m, roof slope = 15 %, 7 bays.


4 Engine Structure — HiStruct_BuildingConfigurator2/

HiStruct_BuildingConfigurator2/
  _inputs.fcs                 ← assembles all settings classes
  Geometry/                   ← 200+ files, BuildingsComponentMain.fcs is master
  General/GeneralSettings.fcs ← input complexity, rope slope methods
  Accessory/                  ← DownSpout, CageLadder, Reflector, VentGrill, Canopy
  Openings/                   ← SectionalDoor, SlidingDoor, SmallSkylight, Window, RidgeSkylight
  Sheeting/Sheetings/         ← 80+ panel cross-section geometry files
  Structure/                  ← FEM primary/secondary framing
  Outputs/                    ← BOM, reports, DXF, drawings

Each sub-folder follows the same _resources.fcs_inputs.fcs → component files pattern.


5 Accessory Architecture

Accessories are registered in a central file and then used via the placeableItem2 pattern.

AccessoryAssies.fcs — registry

gclass {downSpoutAssyBase}   filename "DownSpout/DownSpout.fcs"
gclass {cageLadderAssyBase}  filename "CageLadder/CageLadder.fcs"
gclass {reflectorAssyBase}   filename "Reflector/ReflectorAssy.fcs"
gclass {ventGrillAssyBase}   filename "VentGrill/Vent.fcs"

downSpoutUpdateAssyFn  := ...    // update function (parameterises the assembly)
downSpout_pi := placeableItem2{
    assy := downSpoutAssyBase,
    updateSingleAssyFn := downSpoutUpdateAssyFn,
}

AccessorySettings.fcs — instantiation

accessoryAssies := AccessoryAssies{}

Per-component *Assy.fcs — the geometry definition

Each component file is standalone — it can be evaluated directly by fli.exe. It follows the pattern:

// Example: DownSpout.fcs (simplified)
import "../_resources.fcs"
import "_inputs.fcs"

// ... input parameters ...
trParts := [part1, part2, ...]       // array of geometry parts (LinearTractionPart etc.)
isDownSpoutOn := trParts.Count > 0

gblock {gbTrParts} gclass (res.comp.dac(trParts)) lcs (GCS) if (isDownSpoutOn)

simpleRepresentation := {
    inputPoints := [
        Fcs.Geometry.Point3D( 0, 0.25, 0 ),
        Fcs.Geometry.Point3D( 0, 2.00, 0 ),
    ]
}

Key FCS idioms:


6 Reference Results — Results/ Folder

The Results/ folder is outside fcs-HBC-2-forUT/. It contains 12 pre-computed .hiscene.json files that Richard generated manually by running fli.exe on individual component files and the full building view.

Complete Mapping Table

Reference File (in Results/) Source FCS (relative to fcs-HBC-2-forUT/) Expression
DrawHbc_AgriculturalBuilding_viewAll.hiscene.json AgriculturalBuilding.fcc/BuildingsComponentMain_viewAll.fcs main
DrawHbc_CageLadder.hiscene.json HiStruct_BuildingConfigurator2/Accessory/CageLadder/CageLadder.fcs main
DrawHbc_DownSpout.hiscene.json HiStruct_BuildingConfigurator2/Accessory/DownSpout/DownSpout.fcs main
DrawHbc_ReflectorAssy.hiscene.json HiStruct_BuildingConfigurator2/Accessory/Reflector/ReflectorAssy.fcs main
DrawHbc_RidgeSkyLightAssy.hiscene.json HiStruct_BuildingConfigurator2/Openings/RidgeSkylight/RidgeSkyLightAssy.fcs main
DrawHbc_SectionalDoorAssy.hiscene.json HiStruct_BuildingConfigurator2/Openings/SectionalDoor/SectionalDoorAssy.fcs main
DrawHbc_Sheeting_TR150_280_Geom.hiscene.json HiStruct_BuildingConfigurator2/Sheeting/Sheetings/Sheeting_TR150_280_Geom.fcs main
DrawHbc_Sheeting_TR_Spot_Geom.hiscene.json HiStruct_BuildingConfigurator2/Sheeting/Sheetings/Sheeting_TR_Spot_Geom.fcs main
DrawHbc_SlidingDoorAssy.hiscene.json HiStruct_BuildingConfigurator2/Openings/SlidingDoor/SlidingDoorAssy.fcs main
DrawHbc_SmallSkylightAssy.hiscene.json HiStruct_BuildingConfigurator2/Openings/SmallSkylight/SmallSkylightAssy.fcs main
DrawHbc_VentGrill.hiscene.json HiStruct_BuildingConfigurator2/Accessory/VentGrill/Vent.fcs main
DrawHbc_Window01.hiscene.json HiStruct_BuildingConfigurator2/Openings/Window/WindowAssy.fcs main

Naming convention: DrawHbc_ is purely Richard's manual naming scheme. There is no FCS variable, expression, or class anywhere in the source code named DrawHbc_*. The prefix simply stands for "Draw HBC-2". Do not look for it in source files.


7 Running fli.exe Against HBC-2 Components

Single Component (standalone)

# From workspace root: TestData/benchmark-HBC-2/fcs-HBC-2-forUT/ is the CWD
cd TestData/benchmark-HBC-2/fcs-HBC-2-forUT

# Export a component to HiScene JSON
fli.exe "HiStruct_BuildingConfigurator2/Accessory/DownSpout/DownSpout.fcs" "main" --t 3JS --o "../Results/DrawHbc_DownSpout.hiscene.json"

# Reflector
fli.exe "HiStruct_BuildingConfigurator2/Accessory/Reflector/ReflectorAssy.fcs" "main" --t 3JS --o "../Results/DrawHbc_ReflectorAssy.hiscene.json"

# Sectional door
fli.exe "HiStruct_BuildingConfigurator2/Openings/SectionalDoor/SectionalDoorAssy.fcs" "main" --t 3JS --o "../Results/DrawHbc_SectionalDoorAssy.hiscene.json"

# Sheeting cross-section
fli.exe "HiStruct_BuildingConfigurator2/Sheeting/Sheetings/Sheeting_TR150_280_Geom.fcs" "main" --t 3JS --o "../Results/DrawHbc_Sheeting_TR150_280_Geom.hiscene.json"

Full Building (.fcc entry point)

fli.exe "AgriculturalBuilding.fcc/BuildingsComponentMain_viewAll.fcs" "main" --t 3JS --o "../Results/DrawHbc_AgriculturalBuilding_viewAll.hiscene.json"

Evaluate a single expression to check a value

fli.exe "HiStruct_BuildingConfigurator2/Accessory/DownSpout/DownSpout.fcs" "trParts.Count"
# Output: 3       (number of traction parts in a default downspout)

8 HiScene JSON Output Format

When you run with --t 3JS the output is a Three.js scene file. Key structure:

{
  "metadata": {"formatVersion": 3.1},
  "faceMaterials": [{"colorDiffuse":[1,1,1], "doubleSided":true}],
  "lineMaterials": [{"colorDiffuse":[0,0,0], "dashPattern":[]}, ...],
  "labelMaterials": [],
  "classes": [
    {
      "className": "LinearTractionPart:3",
      "classHash": "SIxYqBJ0/TY",
      "threeJsModel": {
        "vertices": [0,-0.099,0.045, ...],
        "faces":    [2,0,1,2, 0, 2,0,3,1, ...],
        "fcsLines": {"linesMaterialRle":[192,0], "lines":[...]}
      }
    },
    {
      "className": "class:dParts[0]:2",
      "classHash": "RrazU6MSnLk",
      "threeJsModel": {...},
      "objects": [{"n":"gbDetailedModel","lcs":{...},"h":"SIxYqBJ0/TY"}]
    },
    {
      "className": "ManDistributionOfArrayClass:1",
      "classHash": "H+jAzM30FiA",
      "objects": [{"n":"dParts[0]","h":"RrazU6MSnLk"}]
    },
    {
      "className": "main",
      "classHash": "CienFA2MvqY",
      "objects": [{"n":"gbTrParts","h":"H+jAzM30FiA"}]
    }
  ]
}

The class hierarchy shows the instantiation chain: main → (gblock gbTrParts) ManDistributionOfArrayClass → (each part) class:dParts[i]LinearTractionPart (leaf geometry).

vertices is a flat float array; every 3 values are one (X, Y, Z) point in metres. faces encodes Three.js face types: a leading byte 2 means a quad (next 4 indices), 0 means a triangle. classHash is stable across runs — use it to verify that a component was not changed.


9 FCS Unit Tests Inside HBC-2

The DownSpout component has built-in FCS unit tests demonstrating the standard pattern:

// DownSpoutUnitTests.fcs
testClassName := "DownSpout edge definitions"

edgeDefsTestFn := testModel, testResult => res.test.case{
    NamedModel := res.test.tools.baseModel{
        Name      := "Class: " + testClassName,
        TestModel := testModel
    },
    Variable := model => res.test.tools.bvArrDbl{
        Name     := "edgeDefs",
        Variable := model.edgeDefs.Select(it => it.length)
    },
    Result   := testResult
}

tests := [
    edgeDefsTestFn( DownSpout{ inLength := 1.86 }, [1.86] ),
    // ... more cases
]

testSuite     := res.test.tools.runTestSuiteRawFn( testClassName, tests, False )
testSuiteDeep := testSuite.GetDeepResult()

To run the tests from the CLI:

fli.exe "HiStruct_BuildingConfigurator2/Accessory/DownSpout/DownSpoutUnitTests.fcs" "testSuiteDeep"

testSuiteDeep is the standard expression that returns a pass/fail summary of all cases.


10 .fcsdrs Files

Files with extension .fcsdrs are draw/rendering settings JSON for the FemCAD GUI. They control camera position, lighting, and material display preferences for the viewer. They are NOT FCS source code. Examples: CageLadder.fcsdrs, Vent.fcsdrs, SlidingDoor.fcsdrs.

These files are irrelevant for fli.exe runs; they only affect how the FemCAD desktop IDE renders a component interactively.


11 Using HBC-2 as a Golem Test Target

The general strategy for testing Golem against HBC-2:

Recommended Test Progression (easy → hard)

  1. Leaf geometry — a single cross-section profile (e.g. Sheeting_TR150_280_Geom.fcs)
  2. Simple assembly — a standalone accessory with fixed defaults (e.g. ReflectorAssy.fcs)
  3. Parameterised component — a component with an input struct (e.g. DownSpout.fcs)
  4. Multi-part assembly with DAC — e.g. DownSpout.fcs with res.comp.dac
  5. Full building viewAgriculturalBuilding.fcc/BuildingsComponentMain_viewAll.fcs

Golem Test Case — hiscene Validation Type

{
  "id": "hbc2-001-reflector-geometry",
  "description": "Reproduce ReflectorAssy standalone geometry",
  "difficulty": "easy",
  "concepts": ["gblock", "gclass", "lcs", "Tz", "Rx", "standalone-assembly"],
  "prompt": "Write a minimal FCS file that defines a Reflector assembly using the ReflectorAssy pattern. The reflector geometry is placed at LCS offset Tz(0.035) with rotation Rx(-PI/2).",
  "validation": {
    "type": "hiscene",
    "scene_expression": "main",
    "reference": "../../benchmark-HBC-2/Results/DrawHbc_ReflectorAssy.hiscene.json"
  }
}

Command to validate Golem's output

# Golem saves its script to output/hbc2-001.fcs
# Run from fcs-HBC-2-forUT/ as CWD:
fli.exe "output/hbc2-001.fcs" "main" --t 3JS --o "output/hbc2-001.hiscene.json"

# Then Run-GolemTests.ps1 compares output/hbc2-001.hiscene.json
# against the reference in Results/ using classHash comparison

Key Things Golem Must Reproduce Correctly

For a hiscene test to pass, the classHash of the root main class must match. This means:

Any parameter change that affects geometry will change the hash.


12 Summary of FCS Patterns in HBC-2

// Pattern 1: Conditional geometry block
gblock {gbReflector} gclass (reflector) lcs (GCS.Tz(0.035).Rx(-PI/2))

// Pattern 2: Conditional block with guard
gblock {gbTrParts} gclass (res.comp.dac(trParts)) lcs (GCS) if (isDownSpoutOn)

// Pattern 3: DAC — distribute an array of parts into geometry
trParts := [part1, part2, part3]
gblock {gbParts} gclass (res.comp.dac(trParts)) lcs (GCS)

// Pattern 4: Simple representation for coarse views
simpleRepresentation := {
    inputPoints := [
        Fcs.Geometry.Point3D( 0, 0.25, 0 ),
        Fcs.Geometry.Point3D( 0, 2.00, 0 ),
    ]
}

// Pattern 5: gclass registration (in *Assies.fcs registry files)
gclass {myComponentBase} filename "SubFolder/MyComponent.fcs"

// Pattern 6: placeableItem2 (an accessory entry with update function)
component_pi := placeableItem2{
    assy               := myComponentBase,
    updateSingleAssyFn := myUpdateFn,
}

// Pattern 7: FCS unit test case
testFn := testModel, expectedResult => res.test.case{
    NamedModel := res.test.tools.baseModel{ Name := "Test", TestModel := testModel },
    Variable   := model => res.test.tools.bvArrDbl{
        Name     := "values",
        Variable := model.someValues
    },
    Result := expectedResult
}
testSuiteDeep := res.test.tools.runTestSuiteRawFn( "SuiteName", tests, False ).GetDeepResult()

// Pattern 8: .fcc entry point (import pair)
// _viewAll.fcs:
import "_updated.fcs"
import "../../BaseTemplate.fcc/BaseTemplate_viewAll_toImport.fcs"
// _updated.fcs:
import "../Engine/Master.fcs"
site.someParameter = overrideValue