Skip to content

Quick Start: Complete Workflow

This guide walks you through the complete HHW Brick workflow in 10 minutes:

Convert CSV data → Validate Brick model → Analyze with portable analytics

What You'll Build

By the end of this guide, you'll have:

  • ✅ Converted a building from CSV to Brick Schema format
  • ✅ Validated the model for correctness and completeness
  • ✅ Run a portable analytics application on the building
  • ✅ Understood the complete workflow

Step 1: Install the Package

If you haven't already, clone the repository and install in editable mode:

# Clone the repository
git clone https://github.com/CenterForTheBuiltEnvironment/HHW_brick.git
cd HHW_brick

# Install in editable mode
pip install -e .

Note: Once published to PyPI, you'll be able to install with pip install hhw-brick.

Step 2: Prepare Your Data

You need two types of CSV files for the complete workflow:

A. Building Metadata (for conversion)

  1. metadata.csv - Building information (system type, organization, etc.)
  2. vars_available_by_building.csv - Sensor/point availability for each building

B. Timeseries Data (for analytics)

  1. [building]_timeseries.csv - Time-indexed sensor readings (used in Step 6)

For this tutorial, we'll use the included test data for metadata (Steps 3-5).

Download Test Data

The package includes test data in the repository:

Download from GitHub →

You can find: - metadata.csv - Building metadata - vars_available_by_building.csv - Sensor availability data - TimeSeriesData/*.csv - Example timeseries data (for Step 6 analytics)

Or if you've cloned the repository, they're located at:

tests/fixtures/metadata.csv
tests/fixtures/vars_available_by_building.csv
tests/fixtures/TimeSeriesData/building_105_timeseries.csv  (example)

Or create a simple example:

import pandas as pd

# Create metadata.csv
metadata = pd.DataFrame({
    'tag': [105],
    'system': ['Non-condensing'],
    'org': ['Organization A']
})
metadata.to_csv('metadata.csv', index=False)

# Create vars_available_by_building.csv
# IMPORTANT: First 3 columns are skipped, sensors start from column 4
vars_data = pd.DataFrame({
    'tag': [105],
    'org': ['A'],  # Column 2 (skipped)
    'datetime': [1],  # Column 3 (skipped)
    'sup': [1],  # Column 4+ are sensors
    'ret': [1],
    'hw': [1],
    'flow': [1]
})
vars_data.to_csv('vars_available_by_building.csv', index=False)

Step 3: Convert Your First Building

Create a Python script (my_first_conversion.py):

from hhw_brick import CSVToBrickConverter

# Create the converter
converter = CSVToBrickConverter()

# Convert building #105
result = converter.convert_to_brick(
    metadata_csv="metadata.csv",
    vars_csv="vars_available_by_building.csv",
    building_tag="105",  # Building ID to convert
    output_path="building_105.ttl"
)

print(f"✓ Conversion complete!")
print(f"✓ Created {len(result)} RDF triples")
print(f"✓ Output: building_105.ttl")

Run it:

python my_first_conversion.py

Expected output:

✓ Conversion complete!
✓ Created 156 RDF triples
✓ Output: building_105.ttl

Step 4: Inspect the Output

Your building_105.ttl file now contains a Brick model. Let's peek inside:

from rdflib import Graph

# Load the Brick model
g = Graph()
g.parse("building_105.ttl", format="turtle")

# Count elements
print(f"Total statements: {len(g)}")

# Query for equipment
query = """
SELECT ?equip ?type WHERE {
    ?equip a ?type .
    FILTER(STRSTARTS(STR(?type), "https://brickschema.org/schema/Brick#"))
}
"""
for row in g.query(query):
    print(f"  - {row.equip.split('#')[-1]}: {row.type.split('#')[-1]}")

Step 5: Validate the Model

Ensure your model is correct and complete:

from hhw_brick import BrickModelValidator

# Create validator
validator = BrickModelValidator()

# Validate the model
report = validator.validate_ontology("building_105.ttl")

if report['valid']:
    print("✓ Model passed ontology validation!")
    print(f"  - Accuracy: {report['accuracy_percentage']}%")
else:
    print("⚠ Validation found issues:")
    print(f"  - Error: {report.get('error', 'Unknown')}")

Expected output:

✓ Model passed ontology validation!
  - Accuracy: 100.0%

Step 6: Run Portable Analytics

Now for the key advantage of Brick Schema - run analytics that work across any building!

Discover Available Applications

from hhw_brick import apps

# List all available applications
available = apps.list_apps()
print("Available applications:")
for app_info in available:
    print(f"  - {app_info['name']}: {app_info['description']}")

Expected output:

Available applications:
  - secondary_loop_temp_diff: Analyzes temperature difference in secondary loop
  - primary_loop_temp_diff: Analyzes temperature difference in primary loop

Check if Building Qualifies

# Load an application
app = apps.load_app("secondary_loop_temp_diff")

# Check if building has required equipment
qualified, details = app.qualify("building_105.ttl")

if qualified:
    print("✓ Building qualifies for this analysis!")
    print(f"  Required sensors: {details['required_sensors']}")
    print(f"  Found sensors: {details['found_sensors']}")
else:
    print("✗ Building does not qualify")
    print(f"  Missing: {details['missing']}")

Prepare Timeseries Data

To run analytics, you need timeseries data in CSV format with timestamps and sensor readings:

Example timeseries CSV format:

dt,datetime_UTC,sup,ret,flow,hw,t_out
2020-02-05,2020-02-05T13:00:00Z,85.2,72.1,12.5,685000,7.5
2020-02-05,2020-02-05T14:00:00Z,84.8,71.9,12.3,678000,7.6
2020-02-05,2020-02-05T15:00:00Z,85.0,72.0,12.4,680000,8.2
...

Required columns:

  • dt - Local date (YYYY-MM-DD format)
  • datetime_UTC - UTC timestamp (ISO8601 format)

Sensor columns (match your vars file sensor names):

  • sup - Supply water temperature (°C)
  • ret - Return water temperature (°C)
  • flow - Flow rate (l/s)
  • hw - Heating power (W)
  • t_out - Outdoor air temperature (°C)
  • oper - Operating state (0-1)

Download example timeseries data:

Example Timeseries Data →

Or create a simple example:

import pandas as pd
import numpy as np

# Create sample timeseries data
dates = pd.date_range('2020-01-01', periods=1000, freq='1H')
df = pd.DataFrame({
    'dt': dates.date,  # Local date
    'datetime_UTC': dates.strftime('%Y-%m-%dT%H:%M:%SZ'),  # UTC timestamp
    'sup': np.random.normal(85, 5, 1000),  # Supply temp (°C)
    'ret': np.random.normal(72, 5, 1000),  # Return temp (°C)
    'flow': np.random.normal(12, 2, 1000),  # Flow rate (l/s)
    'hw': np.random.normal(680000, 50000, 1000),  # Heating power (W)
    't_out': np.random.normal(10, 5, 1000),  # Outdoor temp (°C)
    'oper': np.random.choice([0, 1], 1000, p=[0.2, 0.8])  # Operating state
})
df.to_csv('building_105_timeseries.csv', index=False)

Run Analysis

With timeseries data ready, run the complete analysis:

# Get default configuration
config = apps.get_default_config("secondary_loop_temp_diff")

# Customize configuration if needed
config["analysis_period"] = "2023-01-01 to 2023-12-31"
config["output_directory"] = "results/"

# Run the analysis
results = app.analyze(
    brick_model="building_105.ttl",
    timeseries_csv="building_105_timeseries.csv",
    config=config
)

print("✓ Analysis complete!")
print(f"  - Temperature difference mean: {results['mean_temp_diff']:.2f}°F")
print(f"  - Anomalies detected: {results['anomaly_count']}")
print(f"  - Report saved to: {results['output_path']}")

Expected output:

✓ Analysis complete!
  - Temperature difference mean: 20.35°F
  - Anomalies detected: 12
  - Report saved to: results/building_105_analysis.html

Note: For detailed timeseries format requirements and advanced analysis options, see Applications Guide.

What Just Happened?

You completed the complete HHW Brick workflow:

graph LR
    A[CSV Files] -->|1. Convert| B[Brick Model]
    B -->|2. Validate| C[Validated Model]
    C -->|3. Qualify| D[Check Requirements]
    D -->|4. Analyze| E[Insights]

    style A fill:#e1f5ff
    style B fill:#fff9c4
    style C fill:#c8e6c9
    style D fill:#ffe0b2
    style E fill:#f8bbd0

Step-by-Step Breakdown

1. Conversion (CSV → Brick) - Read CSV files containing equipment metadata and sensor availability - Identified system type (e.g., "Non-condensing boiler") - Mapped CSV columns to Brick Schema classes and relationships - Generated RDF triples in Turtle format

2. Validation (Quality Check) - Verified model conforms to Brick Schema 1.4 ontology - Checked point counts match source CSV data - Validated equipment relationships and structure

3. Analytics (Portable Application) - Used SPARQL to auto-discover required sensors in the model - Checked if building has necessary equipment for analysis - Ready to run analytics without hardcoded point names

Key Insight: You created a standardized, validated, analysis-ready building model!

Why This Matters

Traditional building analytics require manual recoding for each building:

# ❌ Traditional approach - hardcoded point names
supply_temp = data["HW_Supply_Temp"]  # Only works for this building!
return_temp = data["HWReturnTemp"]    # Different name in next building

With HHW Brick, analytics are portable:

# ✅ Brick approach - semantic queries
query = """
SELECT ?sensor WHERE {
    ?sensor a brick:Hot_Water_Supply_Temperature_Sensor .
}
"""
# Works on ANY building with Brick model!

Result: Write analytics once, deploy across hundreds of buildings.

Next Steps

Congratulations! You've completed the full workflow. Now dive deeper:

📚 Deepen Your Understanding

🔧 Master the Tools

  • Conversion Guide - Advanced conversion techniques
  • Single building conversion with custom options
  • Batch conversion for multiple buildings
  • Supported system types and configurations

  • Validation Guide - Ensure model quality

  • Ontology conformance validation
  • Point count validation
  • Equipment structure validation

  • Applications Guide - Build portable analytics

  • Creating custom analytics apps
  • Timeseries data format requirements
  • Configuration and deployment

💡 See More Examples

  • Example Scripts - Copy-paste ready code
  • 01_convert_csv_to_brick.py - Basic conversion
  • 02_ontology_validation.py - Validation examples
  • 06_application_management.py - App discovery and loading
  • 07_run_application.py - Running analytics

Common Next Tasks

Convert Multiple Buildings

from hhw_brick import BatchConverter

batch = BatchConverter()
results = batch.convert_all_buildings(
    metadata_csv="metadata.csv",
    vars_csv="vars_available_by_building.csv",
    output_dir="brick_models/",
    show_progress=True
)

print(f"Converted {results['successful']} buildings")

Filter by System Type

# Convert only condensing systems
result = converter.convert_to_brick(
    metadata_csv="metadata.csv",
    vars_csv="vars_available_by_building.csv",
    system_type="Condensing",  # Filter
    output_path="condensing_buildings.ttl"
)

Run an Analytics Application

from hhw_brick import apps

# Load temperature difference analysis app
app = apps.load_app("secondary_loop_temp_diff")

# Check if building qualifies
qualified, details = app.qualify("building_105.ttl")

if qualified:
    # Run analysis (need timeseries data)
    results = app.analyze(model, timeseries_data, config)

Troubleshooting

Error: "FileNotFoundError"

Make sure your CSV files exist:

import os
print(os.path.exists("metadata.csv"))  # Should be True

Error: "No data found for building tag"

Check that the building ID exists in your CSV:

import pandas as pd
df = pd.read_csv("metadata.csv")
print(df['tag'].unique())  # List all building IDs

Warning: "Could not convert value to float"

Some sensor values might be missing (NA). This is normal and the converter handles it automatically.

Complete Example Script

Here's a complete end-to-end script you can copy and run:

"""
HHW Brick - Complete Workflow Example

This script demonstrates the complete workflow:
1. Convert CSV to Brick model
2. Validate the model
3. Check analytics qualification
"""

from hhw_brick import CSVToBrickConverter, BrickModelValidator, apps
from pathlib import Path

def main():
    print("=" * 60)
    print("HHW Brick - Complete Workflow")
    print("=" * 60)

    # Configuration
    metadata_csv = "metadata.csv"
    vars_csv = "vars_available_by_building.csv"
    building_tag = "105"
    output_file = f"building_{building_tag}.ttl"

    # Step 1: Convert CSV to Brick
    print("\n[Step 1/3] Converting CSV to Brick Schema...")
    converter = CSVToBrickConverter()
    graph = converter.convert_to_brick(
        metadata_csv=metadata_csv,
        vars_csv=vars_csv,
        building_tag=building_tag,
        output_path=output_file
    )
    print(f"✓ Created {len(graph)} RDF triples")
    print(f"✓ Saved to: {output_file}")

    # Step 2: Validate the model
    print("\n[Step 2/3] Validating Brick model...")
    validator = BrickModelValidator()
    report = validator.validate_ontology(output_file)

    is_valid = report.get('valid', False)
    if is_valid:
        print("✓ Model passed ontology validation!")
    else:
        print("⚠ Validation issues found:")
        print(f"  Error: {report.get('error', 'Unknown')}")

    # Step 3: Check analytics qualification
    print("\n[Step 3/3] Checking analytics qualification...")

    # Discover available apps
    available_apps = apps.list_apps()
    print(f"Found {len(available_apps)} analytics applications")

    # Test qualification for each app
    for app_info in available_apps:
        app_name = app_info['name']
        try:
            app = apps.load_app(app_name)
            qualified, details = app.qualify(output_file)

            if qualified:
                print(f"  ✓ {app_name}: QUALIFIED")
            else:
                print(f"  ✗ {app_name}: Not qualified")
                if 'missing' in details:
                    print(f"    Missing: {', '.join(details['missing'][:3])}")
        except Exception as e:
            print(f"  ! {app_name}: Error - {e}")

    # Summary
    print("\n" + "=" * 60)
    print("Summary")
    print("=" * 60)
    print(f"Input:  {metadata_csv}, {vars_csv}")
    print(f"Output: {output_file} ({Path(output_file).stat().st_size / 1024:.1f} KB)")
    print(f"Status: {'Valid' if is_valid else 'Has warnings'}")
    print("\n✓ Complete workflow finished!")
    print("\nNext: View the TTL file or run analytics with timeseries data")

if __name__ == "__main__":
    main()

Run it:

python complete_workflow.py

Expected output:

============================================================
HHW Brick - Complete Workflow
============================================================

[Step 1/3] Converting CSV to Brick Schema...
✓ Created 156 RDF triples
✓ Saved to: building_105.ttl

[Step 2/3] Validating Brick model...
✓ Model passed ontology validation!

[Step 3/3] Checking analytics qualification...
Found 2 analytics applications
  ✓ secondary_loop_temp_diff: QUALIFIED
  ✓ primary_loop_temp_diff: QUALIFIED

============================================================
Summary
============================================================
Input:  metadata.csv, vars_available_by_building.csv
Output: building_105.ttl (12.3 KB)
Status: Valid

✓ Complete workflow finished!

Next: View the TTL file or run analytics with timeseries data

🎉 Congratulations! You've completed the HHW Brick quick start and experienced the full workflow.

What's Next?