Vulcan Geometry CSV format guide

Documentation

Geometry CSV

Describe building geometry and services in CSV so they can be combined with thermal data into Home Energy Model (HEM:FHS) inputs. The format helps you author and check models faster and lets other tools produce compatible files.

Contract version
0.2.1
HEM:FHS version
v1.0.0a7 (alpha_prerelease)

Quickstart

Merge combines your geometry CSV with a defaults JSON file to produce HEM:FHS input JSON: values you set in the CSV are merged into that baseline, then the result is validated (schema and FHS preflight)—the same pipeline as the browser merge tool on this page. Omitting a CSV section usually means that part of the model is not supplied from the file; do not assume missing sections are silently reconstructed from defaults.

  1. Start from an example file if one is close to your dwelling.
  2. Add or remove sections until the CSV describes the geometry and systems you need.
  3. Check the matching Section reference card for column names, required fields, and allowed values.

Defaults template: defaults_template.json is the baseline JSON used for merge on this site and in the bundled examples.

Authoring the CSV

Use these rules when editing an example or writing a CSV from scratch.

Syntax

  • coords: comma-separated 3D points; quote cells when needed; use doubled quotes ("") for a literal quote inside a cell.
  • Booleans use TRUE / FALSE unless a section documents otherwise.
  • Match listed values exactly, including capital letters.

Which value wins

Usually: CSV cell → extra_json → defaults template. Empty cells are skipped, so the next source can still provide the value. Section notes call out exceptions.

Rows and defaults

  • Put Zone rows before rows that reference zones.
  • One extra_json object per row; allowed keys depend on section and Type (extra_json in the appendix).
  • Put defaults path, compliance fields, or orientation in Metadata when the file needs to stand on its own.

Examples

Working snippet

Full enough metadata, zone, fabric, and systems for merge + validation in the browser tool.

Metadata,,,,,,,,,,,,,
GlobalOrientationOffset,0,,,,,,,,,,,,,
SchemaProfile,input_fhs,,,,,,,,,,,,,
DefaultsPath,input/defaults/defaults_template.json,,,,,,,,,,,,,
DefaultAssemblyWall,,,,,,,,,,,,,,
DefaultAssemblyRoof,,,,,,,,,,,,,,
DefaultAssemblyGroundFloor,,,,,,,,,,,,,,
DefaultThermalBridging,0.2,,,,,,,,,,,,,
NumberOfBedrooms,1,,,,,,,,,,,,,
NumberOfWetRooms,1,,,,,,,,,,,,,
GroundFloorArea,78.89,,,,,,,,,,,,,
HeatingControlType,SeparateTempControl,,,,,,,,,,,,,
PartGcompliance,TRUE,,,,,,,,,,,,,
ComplianceValidationEnabled,TRUE,,,,,,,,,,,,,
ScenariosBaseModelEnabled,FALSE,,,,,,,,,,,,,
AirPermeability_test_pressure,Standard,,,,,,,,,,,,,
AirPermeability_test_result,4,,,,,,,,,,,,,
BuildingLength,11.08,,,,,,,,,,,,,
BuildingWidth,7.12,,,,,,,,,,,,,
NumberOfHabitableRooms,1,,,,,,,,,,,,,
NumberOfHotTappedRooms,1,,,,,,,,,,,,,
NumberOfUtilityRooms,0,,,,,,,,,,,,,
NumberOfBathrooms,1,,,,,,,,,,,,,
NumberOfSanitaryAccommodations,0,,,,,,,,,,,,,
,,,,,,,,,,,,
Zone,,,,,,,,,,,,,
Name,Type,volume,floor_area,height,simplified thermal bridging,livingroom_area,restofdwelling_area
Zone 1,Zone,189.34,78.89,2.4,TRUE,50,28.89

Exposed Elements,,,,,,,,,,,,,,
Name,Zone,Type,area,pitch,width,height,orientation360,base_height,is_unheated_pitched_roof,is_external_door,parent_element,coords,extra_json
Wall (S),Zone 1,BuildingElementOpaque,22.97,,11.08,2.4,180,0,FALSE,FALSE,,"-6.340,-4.660,0.000|4.740,-4.660,0.000","{}"
Wall (E),Zone 1,BuildingElementOpaque,17.09,,7.12,2.4,90,0,FALSE,FALSE,,"4.740,-4.660,0.000|4.740,2.460,0.000",
Wall (N),Zone 1,BuildingElementOpaque,22.97,,11.08,2.4,0,0,FALSE,FALSE,,"4.740,2.460,0.000|-6.340,2.460,0.000",
Wall (W),Zone 1,BuildingElementOpaque,13.47,,7.12,2.4,270,0,FALSE,FALSE,,"-6.340,2.460,0.000|-6.340,-4.660,0.000",
Wall,Zone 1,BuildingElementOpaque,78.89,0,8.88,8.88,0,2.4,FALSE,FALSE,,"-6.340,2.460,1.000|4.740,2.460,1.000|4.740,-4.660,1.000|-6.340,-4.660,1.000","{}"

Window Elements,,,,,,,,,,,,,,
Name,Zone,Type,area,pitch,width,height,orientation360,base_height,linked_wall,frame_area_fraction,free_area_height,mid_height,max_window_open_area,coords,extra_json
Window,Zone 1,BuildingElementTransparent,3.62,,3.02,1.2,0,0.2,Wall (N),0.1,1,0.8,1.2,"0.820,2.460,0.000|-2.200,2.460,0.000","{}"
Window 1,Zone 1,BuildingElementTransparent,3.62,,3.02,1.2,270,0.2,Wall (W),0.1,1,0.8,1.2,"-6.340,0.610,0.000|-6.340,-2.410,0.000","{}"
Window 1 1,Zone 1,BuildingElementTransparent,3.62,,3.02,1.2,180,0.2,Wall (S),0.1,1,0.8,1.2,"-1.910,-4.660,0.000|1.110,-4.660,0.000","{}"

Lighting,,,,,,,,,,,,,
Name,Zone,efficacy,count,power,coords,extra_json
Light,Zone 1,80,1,10,"-0.960,0.500,0.000","{}"

Ground Elements,,,,,,,,,,,,,
Name,Zone,Type,area,width,height,perimeter,floor_type,depth_basement_floor,thickness_walls,parent_element,coords,extra_json
Floor,Zone 1,BuildingElementGround,78.89,0,0,36.4,Slab_no_edge_insulation,,,,"-6.340,-4.660,0.000|4.740,-4.660,0.000|4.740,2.460,0.000|-6.340,2.460,0.000","{}"

Ventilation Systems,,,,,,,,,,,,
Name,Type,length,duct_type,parent_element,mid_height_air_flow_path,area_cm2,orientation360,pitch,vent_type,coords,extra_json
Background Vent 1,Vents,,,,1.5,120,180,90,,"0.820,2.460,0.000","{}"
Background Vent 2,Vents,,,,1.5,120,270,90,,"-6.340,0.610,0.000","{}"
Background Vent 3,Vents,,,,1.5,120,0,90,,"-1.910,-4.660,0.000","{}"
Background Vent 4,Vents,,,,1.5,120,90,90,,"4.740,-2.000,0.000","{}"
Intermittent MEV,MechanicalVentilation,,,,,,,,Intermittent MEV,"2.800,1.560,0.000","{""design_outdoor_air_flow_rate"":270}"

Appliances,,,,
Name,Type,appliancekey,coords
Fridge,Appliance,Fridge,"3.280,-0.540,0.000"

Hot Water Outlets,,,,,,,,
Name,Type,subcategory,flowrate,size,rated_power,allow_low_flowrate,coords
Tap,HotWaterDemand,OtherWaterUseDetails,8,,,,"1.120,-0.120,0.000"

Systems,,,,,,,
Name,Zone,Type,subcategory,system_preset,coords,extra_json
Storage Tank,Zone 1,System,HotWaterSource,immersion_cylinder,"-2.360,-1.600,0.000","{""HotWaterSource"":{""hw cylinder"":{""type"":""StorageTank"",""volume"":80,""daily_losses"":1.68,""init_temp"":55,""ColdWaterSource"":""mains water"",""HeatSource"":{""immersion"":{""type"":""ImmersionHeater"",""power"":3,""EnergySupply"":""mains elec"",""heater_position"":0.1,""thermostat_position"":0.33}}}},""_system_source"":""presets""}"

Section reference

Each block below is one CSV section with a field table. Required? says how much you must supply; a tag next to a field means merge behaviour differs from the usual path (same column name can mean different things by section—e.g. coords).

Related columns (extra cross-field rules) only appear when the contract lists them for that section. How rows become part of the model appears when output_mapping.merge_mode and/or merge_glossary_backrefs are set (see geometry_csv_contract.json). Many sections document merge behaviour only in the field table or under Types and row variants—that is normal, not an omission.

Column tags

  • Outside merged modelNot used in the usual dwelling-model merge for this field or section.

Required?

  • RequiredMust be correct for this dwelling.
  • Required whenOnly when the row matches the rule (grey “when …” text).
  • OptionalCan be blank if defaults or other cells supply the value.
  • Layout onlyFor plans and layout—not compliance numbers on its own.

Grey when … lines mark conditional columns. Merge semantics: merge_effect_definitions in geometry_csv_contract.json.

Metadata

Project-wide settings rows (Key, Value): schema profile, defaults file paths, compliance inputs, and editor/workflow flags that the conversion step reads before merging geometry into the dwelling JSON.

Metadata keys

Each row is Key + Value (two columns). Tags under each key name follow the merge legend in Section reference (Outside merged model).

KeyFormatRequired?
AirPermeability_test_pressureEnum
Required
AirPermeability_test_resultNumber
Required
AirPermeability_ventilation_zone_heightNumber
Required
BuildingLengthNumber
Required
BuildingWidthNumber
Required
General_build_typeEnum
Required
General_storeys_in_dwellingInteger
Required
GroundFloorAreaNumber
Required
HeatingControlTypeEnum
Required
KitchenExtractorHoodExternalBoolean
Required
LocationString
Required
NumberOfBathroomsInteger
Required
NumberOfBedroomsInteger
Required
NumberOfHabitableRoomsInteger
Required
NumberOfHotTappedRoomsInteger
Required
NumberOfSanitaryAccommodationsInteger
Required
NumberOfUtilityRoomsInteger
Required
NumberOfWetRoomsInteger
Required
PartGcomplianceBoolean
Required
PartO_active_cooling_requiredBoolean
Required
SchemaProfileOutside merged modelString
Required
Ventilation_altitudeNumber
Required
Ventilation_noise_nuisanceBoolean
Required
Ventilation_shield_classEnum
Required
Ventilation_terrain_classEnum
Required
AirPermeability_env_areaOutside merged modelString
Optional
ComplianceValidationEnabledOutside merged modelString
Optional
DefaultAssemblyGroundFloorOutside merged modelString
Optional
DefaultAssemblyRoofOutside merged modelString
Optional
DefaultAssemblyWallOutside merged modelString
Optional
DefaultsPathOutside merged modelPath
Optional
DefaultThermalBridgingNumber
Optional
General_storeys_in_buildingInteger
Optional
GuideOverlayOutside merged modelString
Optional
JunctionPsiDefaultsPathOutside merged modelString
Optional
SapDirOutside merged modelString
Optional
SapPdfPathOutside merged modelString
Optional
SapXmlPathOutside merged modelString
Optional
ScenariosBaseModelEnabledOutside merged modelString
Optional
GlobalOrientationOffsetOutside merged modelString
Layout only

Notes

  • The Metadata keys table lists every key. In generated docs, Outside merged model matches merge_effect values other than affects_calculation_input (including workflow_only for editor/tooling keys and not_in_calculation_input for paths and canvas-only values); those keys are not merged into dwelling_calculation_input_json as ordinary dwelling fabric/system inputs. Keys with affects_calculation_input participate where each authoring_note describes. Per-key merge_effect and populate_requirement in this contract are authoritative.

Zone

Thermal zones and zone-level attributes.

Fields

FieldFormatRequired?
NameString
Required
TypeOutside merged modelString
Required
floor_areaNumber
Required
livingroom_areaNumber
Required
restofdwelling_areaNumber
Required
heightNumber
Required when
when Used for volume derivation; whether height remains on the merged zone object depends on output mode (FHS may strip).
include this column header
simplified thermal bridgingString
Optional
volumeNumber
Optional
include this column header

How rows become part of the model

Zone rows create or update each `Zone.<zone name>` object at the dwelling JSON root before fabric and system merges. (merge_zone_rows_into_root_zone_map)

Exposed Elements

External opaque/roof/door building elements by zone.

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
coordsCoords (x,y,z)
Required
heightNumber
Required
orientation360Number
Required
pitchNumber
Required
widthNumber
Required
ZoneString
Required
is_external_doorBoolean
Required when
when door or opaque branch semantics apply (see fields_by_type.BuildingElementOpaque).
is_unheated_pitched_roofBoolean
Required when
when opaque roof semantics apply (see fields_by_type.BuildingElementOpaque).
parent_elementOutside merged modelString
Required when
areaNumber
Optional
include this column header
base_heightNumber
Optional
extra_jsonJSON object
Optional

extra_json keys by Type

Extra fields allowed in the row's extra_json cell, besides the normal columns.

  • BuildingElementOpaque areal_heat_capacity, colour, mass_distribution_class, thermal_resistance_construction, u_value

How rows become part of the model

Rows merge under `Zone.<Zone>.BuildingElement.<Name>` on the schema branch for each row Type (opaque surfaces, glazing, ground-contact slabs). The Zone column selects the parent zone; Name keys the element. (merge_building_element_rows_under_zone)

Window Elements

Transparent elements by zone. Glazing rows merge along the transparent path (e.g. U-value and related glazing fields). Mass-category fields such as `areal_heat_capacity` do not apply as they do for opaque or ground fabric—do not expect them to populate regulated glazing inputs.

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
base_heightNumber
Required
coordsCoords (x,y,z)
Required
frame_area_fractionNumber
Required
free_area_heightNumber
Required
heightNumber
Required
max_window_open_areaNumber
Required
orientation360Number
Required
pitchNumber
Required
widthNumber
Required
ZoneString
Required
linked_wallOutside merged modelString
Required when
extra_jsonJSON object
Optional
mid_heightOutside merged modelNumber
Optional
areaOutside merged modelNumber
Layout only
include this column header

extra_json keys by Type

Extra fields allowed in the row's extra_json cell, besides the normal columns.

  • BuildingElementTransparent controls, delta_r, depth, distance, g_value, mid_height_air_flow_path, security_risk, shading, trans_red, treatment, window_part_list

How rows become part of the model

Rows merge under `Zone.<Zone>.BuildingElement.<Name>` on the schema branch for each row Type (opaque surfaces, glazing, ground-contact slabs). The Zone column selects the parent zone; Name keys the element. (merge_building_element_rows_under_zone)

Ground Elements

Ground-contact elements by zone.

Fields

FieldFormatRequired?
NameString
Required
TypeString
Required
areaNumber
Required
coordsCoords (x,y,z)
Required
floor_typeString
Required
perimeterNumber
Required
ZoneString
Required
depth_basement_floorNumber
Required when
when Basement or below-grade ground variants where schema exposes depth_basement_floor.
thickness_wallsString
Required when
when Basement or enclosing-wall variants where schema exposes thickness_walls.
extra_jsonJSON object
Optional
heightNumber
Optional
widthNumber
Optional

extra_json keys by Type

Extra fields allowed in the row's extra_json cell, besides the normal columns.

  • BuildingElementGround area_per_perimeter_vent, areal_heat_capacity, edge_insulation, edge_thermal_resistance, height_basement_walls, height_upper_surface, mass_distribution_class, psi_wall_floor_junc, shield_fact_location, thermal_resist_insul, thermal_resist_walls_base, thermal_resistance_floor_construction, thermal_transm_envi_base, thermal_transm_walls, u_value

How rows become part of the model

Rows merge under `Zone.<Zone>.BuildingElement.<Name>` on the schema branch for each row Type (opaque surfaces, glazing, ground-contact slabs). The Zone column selects the parent zone; Name keys the element. (merge_building_element_rows_under_zone)

Non-Exposed Elements

Adjacent/non-exposed elements by zone.

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
coordsCoords (x,y,z)
Required
heightNumber
Required
pitchNumber
Required
widthNumber
Required
ZoneString
Required
areaNumber
Optional
include this column header
extra_jsonJSON object
Optional

extra_json keys by Type

Extra fields allowed in the row's extra_json cell, besides the normal columns.

  • BuildingElementAdjacentConditionedSpace areal_heat_capacity, mass_distribution_class, thermal_resistance_construction, u_value
  • BuildingElementAdjacentUnconditionedSpace_Simple areal_heat_capacity, mass_distribution_class, thermal_resistance_construction, thermal_resistance_unconditioned_space, u_value
  • BuildingElementPartyWall areal_heat_capacity, mass_distribution_class, thermal_resistance_construction

How rows become part of the model

Rows merge under `Zone.<Zone>.BuildingElement.<Name>` on the schema branch for each row Type (opaque surfaces, glazing, ground-contact slabs). The Zone column selects the parent zone; Name keys the element. (merge_building_element_rows_under_zone)

Thermal Bridging Elements

Linear/point thermal bridge entries by zone.

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
coordsOutside merged modelCoords (x,y,z)
Required
ZoneString
Required
heat_transfer_coeffNumber
Required when
when Type = ThermalBridgePoint
lengthNumber
Required when
when Type = ThermalBridgeLinear
linear_thermal_transmittanceNumber
Required when
when Type = ThermalBridgeLinear
extra_jsonJSON object
Optional

Notes

  • Rows are skipped for zones where Zone.simplified thermal bridging is true; merged keys are filtered to valid thermal-bridge properties (coords are not copied onto merged TB objects).

Types and row variants

  • linear bridge — Triggered when Type = ThermalBridgeLinear. Often uses `length`, `linear_thermal_transmittance`, `coords`
  • point bridge — Triggered when Type = ThermalBridgePoint. Often uses `heat_transfer_coeff`, `coords`

How rows become part of the model

Rows merge into `Zone.<Zone>.ThermalBridging.<Name>` (skipped for zones where simplified thermal bridging applies). (merge_thermal_bridge_rows_into_zone_thermal_bridging)

Window Shading

Shading objects linked to window elements.

Merged dwelling JSON path: Zone.<Zone>.BuildingElement.<linked_window>.shading

Fields

FieldFormatRequired?
NameOutside merged modelString
Required
TypeEnum
Required
linked_windowString
Required
ZoneString
Required
depthNumber
Required when
when Type is overhang, sidefinright, sidefinleft, or reveal (see row_contract branches).
heightNumber
Required when
when Type is object (CSV), mapped to obstacle in JSON (see row_contract branch object).
distanceNumber
Optional
extra_jsonOutside merged modelJSON object
Optional
transparencyString
Optional
coordsOutside merged modelCoords (x,y,z)
Layout only

Notes

  • Rows reference windows via linked_window and are attached to the matching window in-zone.

Related columns

  • linked_windowWindow Elements (matches window Name within the same Zone)

How rows become part of the model

Attach shading payload to an existing window element in the named zone. (attach_to_existing_window_in_zone)

Lighting

Zone lighting entries used to build Lighting objects.

Fields

FieldFormatRequired?
NameString
Required
countNumber
Required
efficacyString
Required
powerNumber
Required
ZoneString
Required
extra_jsonJSON object
Optional
coordsOutside merged modelCoords (x,y,z)
Layout only

How rows become part of the model

Lighting rows populate the `Lighting` object on each referenced zone (counts, efficacy, power). (merge_lighting_rows_into_zone_lighting)

Ventilation Systems

Ventilation rows for vents, mechanical systems, and ductwork links.

Merged dwelling JSON path: InfiltrationVentilation

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
area_cm2Number
Optional
when Type = Vents
duct_typeString
Optional
when Type = MechanicalVentilationDuctwork
extra_jsonJSON object
Optional
lengthNumber
Optional
when Type = MechanicalVentilationDuctwork
mid_height_air_flow_pathNumber
Optional
when Type = Vents
orientation360Number
Optional
when Type = Vents
parent_elementString
Optional
when Type = MechanicalVentilationDuctwork
pitchNumber
Optional
when Type = Vents
vent_typeString
Optional
when Type = MechanicalVentilation
coordsOutside merged modelCoords (x,y,z)
Layout only

extra_json keys by Type

Extra fields allowed in the row's extra_json cell, besides the normal columns.

  • MechanicalVentilation EnergySupply, SFP, SFP_in_use_factor, cross_section_shape, design_outdoor_air_flow_rate, design_zone_cooling_covered_by_mech_vent, design_zone_heating_covered_by_mech_vent, duct_type, ductwork, external_diameter_mm, insulation_thermal_conductivity, insulation_thickness_mm, internal_diameter_mm, length, measured_air_flow_rate, measured_fan_power, mid_height_air_flow_path, mvhr_eff, mvhr_location, orientation360, pitch, position_exhaust, position_intake, reflective

Types and row variants

  • vents — Triggered when Type = Vents. Often uses `mid_height_air_flow_path`, `area_cm2`, `orientation360`, `pitch`
  • mechanical — Triggered when Type = MechanicalVentilation. Often uses `vent_type`, `extra_json`
  • ductwork — Triggered when Type = MechanicalVentilationDuctwork. Often uses `length`, `duct_type`, `parent_element`, `extra_json`

How rows become part of the model

Merge into a defaults-backed root object (for example InfiltrationVentilation). (merge_into_defaults_backed_root_section)

Water Pipework

DHW pipework segments: one CSV row per segment. `pipework_type` selects primary cylinder runs versus distribution; author primary cylinder piping here instead of only via Systems `extra_json`.

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
extra_jsonJSON object
Optional
lengthNumber
Optional
locationString
Optional
pipework_typeEnum
Optional
coordsOutside merged modelCoords (x,y,z)
Layout only

Notes

  • Primary segments append in file order to `HotWaterSource.<store>.primary_pipework[]`. Distribution segments append in file order to `HotWaterDemand.Distribution`. Blank or non-`primary` values count as distribution.
  • Store keys (e.g. `hw cylinder`) come from the merged model. The same primary_pipework result is applied to each tank-shaped hot-water store; combi boilers do not consume these rows.

Types and row variants

  • primary — Triggered when pipework_type = primary
  • distribution — Triggered when pipework_type = distribution

How rows become part of the model

Split Water Pipework rows by pipework_type: primary→each object appended in order to primary_pipework[]; other values (including missing/empty) →distribution, each object appended in order to HotWaterDemand.Distribution. Seeds each new segment from the first defaults-backed segment in defaults_template when present. (append_water_pipework_rows_to_model)

After building the primary array, conversion writes the same JSON array on every HotWaterSource entry whose type is StorageTank or the legacy `hw cylinder` value. CombiBoiler entries are not written from this section. (replicate_primary_pipework_across_tank_shaped_hws)

Wet Emitters

Wet emitter records (e.g. radiators/UFH/fancoils) by zone.

Merged dwelling JSON path: SpaceHeatSystem.<Zone and subcategory derived name>

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
subcategoryEnum
Required
ZoneString
Required
areaNumber
Optional
extra_jsonJSON object
Optional
unit_numberNumber
Optional
coordsOutside merged modelCoords (x,y,z)
Layout only

extra_json keys by Type

Extra fields allowed in the row's extra_json cell, besides the normal columns. Extra lines below only name keys that row types add on top of the Type list.

  • WetEmitter c, n, frac_convective, equivalent_specific_thermal_mass, system_performance_factor, n_units, fancoil_test_data

Related columns

  • subcategory — determines emitter/system branch and resulting WetDistribution naming

Types and row variants

  • radiator — Triggered when subcategory = radiator
  • ufh — Triggered when subcategory = ufh
  • fancoil — Triggered when subcategory = fancoil

How rows become part of the model

Group Wet Emitters rows, rebuild wet distribution systems from aggregates. (aggregate_rows_by_zone_and_subcategory_then_replace_existing_wet_distribution_systems)

Appliances

Appliance key records used for appliance demand mapping.

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
appliancekeyEnum
Required
coordsOutside merged modelCoords (x,y,z)
Layout only

Hot Water Outlets

Hot water outlet demand records.

Merged dwelling JSON path: HotWaterDemand

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
subcategoryEnum
Required
allow_low_flowrateNumber
Optional
flowrateNumber
Optional
rated_powerNumber
Optional
sizeString
Optional
coordsOutside merged modelCoords (x,y,z)
Layout only

extra_json keys by Type

Extra fields allowed in the row's extra_json cell, besides the normal columns.

  • HotWaterDemand ColdWaterSource, EnergySupply, HotWaterSource, type

Types and row variants

  • mixer shower — Triggered when subcategory = MixerShower. Often uses `type`, `flowrate`, `allow_low_flowrate`
  • instant elec shower — Triggered when subcategory = InstantElecShower. Often uses `type`, `rated_power`
  • bath — Triggered when subcategory = Bath. Often uses `size`
  • other — Triggered when subcategory = OtherWaterUseDetails. Often uses `flowrate`

How rows become part of the model

Overlay HotWaterDemand fragments from defaults-backed structure. (merge_into_existing_defaults_backed_hot_water_demand)

Context Shading

Site/context shading geometry entries.

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Optional
distanceNumber
Optional
end_angleString
Optional
heightNumber
Optional
parent_elementOutside merged modelString
Optional
shading_typeEnum
Optional
start_angleString
Optional
coordsOutside merged modelCoords (x,y,z)
Layout only

Notes

  • Dwelling-model conversion accepts shading_type first, then falls back to Type. It accepts both underscore and spaced angle headers; coords and parent_element are roundtrip-only.

How rows become part of the model

Rows contribute obstacle shading geometry into `ExternalConditions.shading_segments` (10° horizon buckets). (merge_context_shading_into_external_conditions_segments)

On-Site Generation

On-site generation systems (e.g. PV) records.

Fields

FieldFormatRequired?
NameString
Required
TypeEnum
Required
generation_typeEnum
Required
base_heightNumber
Optional
coordsCoords (x,y,z)
Optional
extra_jsonJSON object
Optional
orientation360Number
Optional
peak_powerNumber
Optional
pitchNumber
Optional

extra_json keys by Type

Extra fields allowed in the row's extra_json cell, besides the normal columns.

  • OnSiteGeneration EnergySupply, depth, distance, inverter_is_inside, inverter_peak_power_ac, inverter_peak_power_dc, inverter_type, shading, ventilation_strategy

How rows become part of the model

Rows become named entries under root `OnSiteGeneration` (currently PhotovoltaicSystem payloads). (merge_onsite_generation_named_entries)

Systems

System composition rows for heating/hot water/cooling components.

Merged dwelling JSON path: multiple_root_sections

Fields

FieldFormatRequired?
NameString
Required when
when Required for named SpaceHeatSystem / WetDistribution rows and System fragment rows (see row_kinds); optional for ElectricBattery-only rows.
include this column header
TypeString
Required when
when Effective dispatch uses Type, else system_type, else subcategory; at least one must identify the row or merge errors/skips apply.
include this column header
extra_jsonJSON object
Required when
when Payload for System and ElectricBattery paths; may be minimal when template defaults fill the merged object.
subcategoryString
Required when
when Required when Type is System (PCDB-style); can participate in legacy dispatch when Type/system_type empty.
ZoneString
Required when
when Required where merge assigns zone-linked systems (e.g. named_space_heat_system, some System subcategories); inert for other branches.
system_presetOutside merged modelString
Optional
when Type = System and dispatch_fallback_order in Type, system_type, subcategory
system_typeString
Optional
when Type = ElectricBattery and legacy_subcategory = ElectricBattery and dispatch_fallback_order in Type, system_type, subcategory; Type in SpaceHeatSystem, WetDistribution and dispatch_fallback_order in Type, system_type, subcategory; Type = System and dispatch_fallback_order in Type, system_type, subcategory
coordsOutside merged modelCoords (x,y,z)
Layout only
when Type = ElectricBattery and legacy_subcategory = ElectricBattery and dispatch_fallback_order in Type, system_type, subcategory; Type in SpaceHeatSystem, WetDistribution and dispatch_fallback_order in Type, system_type, subcategory; Type = System and dispatch_fallback_order in Type, system_type, subcategory

Notes

  • System rows are dispatched by Type/system_type (or subcategory fallback for Type=System). Builder merge also reads zone_reference (see references) on some branches alongside Zone. FHS: `HotWaterSource` must be authored (Systems row and/or `HeatSourceWet` `extra_json` composite); the defaults template is not a substitute.

Related columns

  • zone_referenceZone (used by some system branches when assigning zone-linked output)

Types and row variants

  • electric battery — Triggered when Type = ElectricBattery and legacy_subcategory = ElectricBattery and dispatch_fallback_order in Type, system_type, subcategory
  • named space heat system — Triggered when Type in SpaceHeatSystem, WetDistribution and dispatch_fallback_order in Type, system_type, subcategory
  • system fragment — Triggered when Type = System and dispatch_fallback_order in Type, system_type, subcategory

How rows become part of the model

Systems section: route row by Type/system_type/subcategory-specific handlers (see systems_dispatch). (branch_specific_by_Type_system_type_and_subcategory)

Contract appendix

The authoritative contract is the JSON file geometry_csv_contract.json. Below is a human-readable copy of the important parts: error codes, which section headers the importer accepts, how Systems rows are routed into the model, and notes on extra_json fields—so you can read them here instead of digging through the raw JSON.

Minimum file & section titles

Include Metadata as needed, at least one Zone row with Name, and the fabric/system sections your dwelling needs. Skip sections only if your defaults_template already supplies a complete dwelling_calculation_input_json for your workflow—otherwise conversion fails or the model is incomplete.

Titles the importer accepts

Test Section is for tests only—omit it in real files.

  • Metadata
  • Exposed Elements
  • Window Elements
  • Ground Elements
  • Non-Exposed Elements
  • Thermal Bridging Elements
  • Zone
  • Window Shading
  • Lighting
  • Ventilation Systems
  • Water Pipework
  • Wet Emitters
  • Appliances
  • Hot Water Outlets
  • Context Shading
  • On-Site Generation
  • Systems

Error codes

The same numeric label (for example E001) can mean different failures in the CSV parsing stage (PARSER_*) versus conversion (BUILDER_*); always use subsystem prefix plus message text. The conversion step reuses several numeric codes (notably E027–E033, E038–E040) for unrelated failures—rely on the full error message, not the code alone.

While reading the CSV

LabelMeaning
PARSER_E001Unknown section name (not in parser allowed list).
PARSER_E002Duplicate section header in the same file.
PARSER_E003Data row appears before the first section header.
PARSER_E004Row has more columns than the section header with non-empty trailing cells (data would be truncated).
PARSER_E006Required column missing for this section (does not satisfy parser-required headers for the section).
PARSER_E007Duplicate Name within the same section.
PARSER_E008Zone column references a zone name that is not defined on any prior Zone row (forward reference).
PARSER_E009Ventilation Systems row with Type MechanicalVentilation has vent_type outside the allowed enum.

While building the dwelling model

LabelMeaning
BUILDER_E001Schema JSON file path does not exist.
BUILDER_E002Failed to read schema file from disk.
BUILDER_E003Schema file is not valid JSON.
BUILDER_E004Defaults JSON file path does not exist.
BUILDER_E005Failed to read defaults file from disk.
BUILDER_E006Defaults file is not valid JSON.
BUILDER_E007Zone section row missing required Name.
BUILDER_E008Fabric element row (Exposed/Window/Ground/Non-Exposed) missing Name.
BUILDER_E009Fabric element row missing Zone.
BUILDER_E010Fabric element row missing Type.
BUILDER_E011Fabric element references a zone not present in merged Zone map.
BUILDER_E012Thermal Bridging Elements row missing Name.
BUILDER_E013Thermal Bridging Elements row missing Zone.
BUILDER_E014Thermal Bridging Elements row missing Type.
BUILDER_E015Thermal bridging references an undefined zone.
BUILDER_E016Lighting row missing Zone.
BUILDER_E017Lighting references an undefined zone.
BUILDER_E018Appliances row missing appliancekey.
BUILDER_E019Hot Water Outlets row missing Name.
BUILDER_E020Hot Water Outlets row missing subcategory, or Ventilation Systems row missing Name.
BUILDER_E021Hot Water Outlets row missing Type, or Ventilation Systems row missing Type, or related ventilation merge validation.
BUILDER_E023Combustion Appliances row missing Name.
BUILDER_E024Wet Emitters row missing Zone.
BUILDER_E025Wet Emitters row missing subcategory, or JSON Schema compilation failure.
BUILDER_E026Merge/build structural errors not covered by per-row codes (e.g. merged root not an object, invalid SpaceHeatSystem FanCoil n_units). Read message.
BUILDER_E027Generic missing-field or missing-default-fragment error used in many branches (read message).
BUILDER_E028Unknown thermal bridge Type, or no zones in CSV when consolidation requires them.
BUILDER_E029Window Shading missing Zone, or JSON shape not an object when expected.
BUILDER_E030Window Shading missing linked_window, or OnSiteGeneration not an object.
BUILDER_E031Window shading references undefined zone, or Lighting/count validation, or On-Site Generation missing Name, or zone rename failure.
BUILDER_E032Linked window not found in zone, or invalid numeric parse in leaks path, or On-Site Generation missing generation_type.
BUILDER_E033Invalid numeric conversion for leaks metadata, or unsupported On-Site Generation generation_type.
BUILDER_E034Expected JSON object at merge root or branch.
BUILDER_E035Systems row missing Type/system_type/subcategory discriminator.
BUILDER_E036EnergySupply exists but is not a JSON object.
BUILDER_E037Named EnergySupply entry is not a JSON object.
BUILDER_E038EnergySupply aggregate not an object, or SpaceHeatSystem row missing Name.
BUILDER_E039SpaceHeatSystem container is not a JSON object.
BUILDER_E040MechanicalVentilation missing required schema field, ductwork on non-MVHR, or Systems element missing Name.
BUILDER_E041HeatSourceWet merge target is not a JSON object.
BUILDER_E042HotWaterSource merge target is not a JSON object, or intermediate JSON not an object.
BUILDER_E043SpaceCoolSystem merge target is not a JSON object.
BUILDER_E044Wet Emitters in FHS mode require an explicit HeatSourceWet Systems row.
BUILDER_E045Defaults template missing WetDistribution SpaceHeatSystem template when Wet Emitters need it.
BUILDER_E046Merged JSON failed JSON Schema validation against input_fhs.schema.json (post-merge). Each row lists jsonschema message and instance path; read message.
BUILDER_E999Filesystem or serialization failure writing batch outputs (not a CSV shape error).

Systems routing

  • Read Type, then if empty read system_type, then if empty read subcategory—the first non-empty value routes the Systems row.
  • Contract row_kinds map Type values to payload_contract and target_path for nested FHS fragments.
  • For PCDB-style inner payloads chosen by subcategory, the outer row must still route as System; otherwise the row may be skipped without a parser error.
  • ElectricBattery nests under EnergySupply.<extra_json.EnergySupply or mains elec>.ElectricBattery.
Routing diagram (Mermaid)
flowchart TD
  R[System CSV row] --> D{First non-empty among Type, system_type, subcategory}
  D -->|ElectricBattery| EB[Merge into EnergySupply named in extra_json]
  D -->|SpaceHeatSystem / WetDistribution| SHS[SpaceHeatSystem wet distribution branch]
  D -->|HeatSourceWet / HotWaterSource / …| WK[Use row_kinds + fields_by_type from contract]
  D -->|unknown / unroutable| SK[May be skipped silently depending on branch]

extra_json

extra_json carries fields beyond plain CSV columns; values merge after column cells using the usual precedence (CSV > extra_json > defaults unless the section says otherwise).

Use section field tables first; use row_kinds / payload_contract when the contract splits behaviour by branch.

Treat extra_json as strict JSON when exchanging files between tools. Browser import may drop invalid cells; other tooling may keep them as plain text—do not rely on the same error handling everywhere.

Which keys are allowed where

building_elements
Advanced keys mirror the FHS element subschemas, excluding columns already modeled as standard CSV headers and excluding fields the app UI deliberately hides (e.g. HotWaterSource primary_pipework is authored from the `Water Pipework` section, not the System “Hot water” advanced panel in the FHS web app).
ventilation_systems
Advanced keys mirror the FHS-backed editor surface for Vents, MechanicalVentilation, and ductwork rows.
combustion_appliances
Advanced keys mirror the public FHS-backed UI/schema surface (not ad-hoc conversion allowlists).
water_pipework
On each `WaterPipework` row, extra_json overlays the WaterPipework item schema (same keys you could place on a primary_pipework[] element) after CSV column overlay; the section split primary vs distribution is by pipework_type (see that section's notes).
wet_emitters
Advanced keys mirror the public FHS-backed UI/schema surface; the app may still adjust some emitter fields outside raw extra_json editing.
systems
Use row_kinds and payload_contract in this file; field paths follow the FHS-backed editor surface with branch coverage inferred from defaults and shipped presets.

Test CSV merge in your browser

Runs locally using the same engine as Vulcan — your data stays on this device.