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:
- It is the hardest regression benchmark — if fli.exe can reproduce the pre-computed reference outputs, the engine is correct.
- 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.exewithfcs-HBC-2-forUT/as the working directory. Every_resources.fcsuses 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:
gblock {name} gclass (expr) lcs (lcs) if (condition)— conditional named geometry blockres.comp.dac(array)— DAC (Distributed Array Component) distributes an array of parts into a renderable scenesimpleRepresentation— simplified display for zoomed-out views (just representative points)
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 namedDrawHbc_*. 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)
- Leaf geometry — a single cross-section profile (e.g.
Sheeting_TR150_280_Geom.fcs) - Simple assembly — a standalone accessory with fixed defaults (e.g.
ReflectorAssy.fcs) - Parameterised component — a component with an input struct (e.g.
DownSpout.fcs) - Multi-part assembly with DAC — e.g.
DownSpout.fcswithres.comp.dac - Full building view —
AgriculturalBuilding.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:
- Identical geometry (vertex positions, faces)
- Identical class hierarchy (same gblock names, same distribution type)
- Identical LCS transforms
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