> ## Documentation Index
> Fetch the complete documentation index at: https://docs.speckle.systems/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Types in Speckle

> Understanding the three types of data you'll encounter in Speckle

## Overview

When receiving data from Speckle, you'll encounter three distinct types of data structures. Understanding which type you're working with helps you choose the right approach.

```python lines icon="python" theme={null}
from specklepy.api import operations
from specklepy.transports.server import ServerTransport

# Receive data from Speckle
transport = ServerTransport(stream_id="project_id", client=client)
obj = operations.receive(object_id, remote_transport=transport)

# What type of data is this?
print(f"Type: {obj.speckle_type}")
print(f"Properties: {obj.get_member_names()}")
```

## The Three Types

### Type 1: Custom Data

**What it is:** Data you create from scratch with your own structure.

**Characteristics:**

* Simple Base objects with custom properties
* No connector-specific structure
* Direct property access
* Full control over schema

**Example:**

```python lines icon="python" theme={null}
from specklepy.objects import Base
from specklepy.objects.geometry import Point

# You create this structure
survey = Base()
survey.name = "Site Survey 2024"
survey.date = "2024-01-15"
survey.points = [
    Point(x=0, y=0, z=10.5),
    Point(x=100, y=50, z=12.3),
]
survey.notes = "Initial survey"
```

**When you see it:**

* Python-to-Python workflows
* Custom data pipelines
* Analysis results you're sending
* Data you control end-to-end

### Type 2: Simple Model Data

**What it is:** Geometry with attached properties, typically from simple exports or scripts.

**Characteristics:**

* Geometry objects (Point, Line, Mesh)
* Properties as dictionaries or direct attributes
* Optional `displayValue` for visualization
* Material and layer information
* Moderate nesting (1-2 levels)

**Example:**

```json theme={null}
{
    "speckle_type": "Objects.Geometry.Mesh",
    "vertices": [...],
    "faces": [...],
    "properties": {
        "Material": "Concrete",
        "Thickness": 200,
        "LoadBearing": true
    },
    "layer": "Walls",
    "color": "0xFF808080"
}
```

**When you see it:**

* Grasshopper exports
* Rhino script outputs
* Simple geometry with metadata
* Blender basic exports
* CAD-like data

### Type 3: Complex BIM Data

**What it is:** Rich application data from BIM tools with complex nested structures.

**Characteristics:**

* Deep nesting (3+ levels)
* Rich property collections
* Display value proxies
* Encoded native data
* Instance/definition patterns
* Data chunk references

**Example:**

```json theme={null}
{
    "speckle_type": "Objects.Data.DataObject",
    "name": "Wall Instance",
    "properties": {
        "Material": "Concrete",
        "Thickness": 200,
        "Width": 0.2,
        "Function": "Exterior",
        "Volume": 15.5,
        "Area": 45.0,
        "LoadBearing": true
    },
    "displayValue": [
        {
            "speckle_type": "Objects.Geometry.Mesh",
            "vertices": [],
            "faces": []
        }
    ]
}
```

**When you see it:**

* Connector exports from any BIM authoring tool
* Rhino exports with complex objects
* ArchiCAD, Revit, Tekla exports
* Any application with rich metadata

## Quick Decision Tree

```python lines icon="python" theme={null}
from specklepy.api import operations
from specklepy.objects.data_objects import DataObject
from specklepy.objects.geometry import Mesh, Point, Line

obj = operations.receive(object_id, remote_transport=transport)

# What type of data is this? Check structural patterns, not speckle_type
if isinstance(obj, DataObject) or (hasattr(obj, "properties") and isinstance(obj.properties, dict)):
    print("Type 3: Complex BIM Data - check properties for semantics")
    if hasattr(obj, "properties"):
        category = obj.properties.get("category", "Unknown")
        print(f"Category: {category}")
    if hasattr(obj, "displayValue"):
        print("Has display value for visualization")

elif isinstance(obj, (Mesh, Point, Line)) or hasattr(obj, "vertices"):
    print("Type 2: Simple Model Data - geometry object")
    if hasattr(obj, "properties"):
        print(f"Properties: {obj.properties}")

else:
    print("Type 1: Custom Data - direct property access")
    print(f"Available properties: {obj.get_member_names()}")
```

<Note>
  **Modern Approach:** Instead of checking `speckle_type` strings, check for structural patterns: Does it have a `properties` dict? Does it have `displayValue`? Is it a geometry class? This works reliably across all Speckle versions.
</Note>

## Key Differences

<Tabs>
  <Tab title="Structure">
    **Custom Data:**

    ```python lines icon="python" theme={null}
    obj.myProperty  # Direct access
    obj.myList[0]   # Simple nesting
    ```

    **Simple Model Data:**

    ```python lines icon="python" theme={null}
    obj.properties["Material"]  # Properties dict
    obj.displayValue.vertices   # Geometry access
    ```

    **Complex BIM Data:**

    ```python lines icon="python" theme={null}
    obj.properties["Volume"]  # Properties dict access
    obj.displayValue  # List of geometry representations
    ```
  </Tab>

  <Tab title="Access Pattern">
    **Custom Data:**

    ```python lines icon="python" theme={null}
    # You know the structure
    name = obj.name
    value = obj.measurements[0].value
    ```

    **Simple Model Data:**

    ```python lines icon="python" theme={null}
    # Check for common properties
    if hasattr(obj, "properties"):
        material = obj.properties.get("Material")
    ```

    **Complex BIM Data:**

    ```python lines icon="python" theme={null}
    # Defensive traversal required
    properties = getattr(obj, "properties", {})
    volume = properties.get("Volume", 0)
    material = properties.get("Material", "Unknown")
    ```
  </Tab>

  <Tab title="Traversal">
    **Custom Data:**

    ```python lines icon="python" theme={null}
    # Simple iteration
    for item in obj.items:
        process(item)
    ```

    **Simple Model Data:**

    ```python lines icon="python" theme={null}
    # Check a few known properties
    if hasattr(obj, "displayValue"):
        mesh = obj.displayValue
    ```

    **Complex BIM Data:**

    ```python lines icon="python" theme={null}
    # Recursive traversal needed
    def traverse(obj):
        if isinstance(obj, Base):
            for name in obj.get_member_names():
                value = getattr(obj, name)
                traverse(value)
    ```
  </Tab>
</Tabs>

## Practical Implications

### Custom Data

✅ **Pros:** Simple, predictable, fast\
❌ **Cons:** No standard structure, manual schema

**Best for:** Analysis results, custom pipelines, Python-to-Python

### Simple Model Data

✅ **Pros:** Geometry-focused, moderate complexity\
❌ **Cons:** Limited metadata, manual property inspection

**Best for:** Visualization, simple exports, geometry processing

### Complex BIM Data

✅ **Pros:** Rich information, application semantics\
❌ **Cons:** Complex structure, defensive code needed, larger data

**Best for:** BIM workflows, detailed analysis, cross-application exchange

## Collections and Hierarchy

### The `elements` Convention

Speckle uses a standardized convention for organizing hierarchical data: the **`elements`** property. This property has a special meaning and is the predictable way to traverse object hierarchies.

**Key Concepts:**

* **Atomic Objects** - Objects that represent actual "things" (walls, beams, points, etc.)
* **Property Objects** - Objects stored as properties for reference (materials, styles, etc.)
* **Container Objects** - Objects that organize other objects via `elements`

```python lines icon="python" theme={null}
from specklepy.objects.models.collections import Collection
from specklepy.objects.geometry import Point, Line

# elements holds the hierarchy of atomic objects
layer = Collection(name="Site Points")
layer.elements = [
    Point(x=0, y=0, z=0),
    Point(x=10, y=0, z=0),
    Point(x=10, y=10, z=0)
]

# Nested collections create hierarchy
project = Collection(name="Project")
project.elements = [
    layer,  # Nested collection
    Line(start=Point(x=0, y=0, z=0), end=Point(x=100, y=0, z=0))
]
```

<Info>
  **Why `elements`?**

  While *any* property can technically contain Speckle objects, `elements` is the internal convention for managing hierarchy in a predictable way. This allows traversal code, connectors, and the viewer to consistently understand object relationships.

  Some typed properties also expect arrays of objects (like `displayValue`), but these serve different purposes - `displayValue` is for visualization, while `elements` is for logical hierarchy.
</Info>

### Collections

The `Collection` class formalizes this pattern:

```python lines icon="python" theme={null}
from specklepy.objects.models.collections import Collection

# Collections represent organizational units
# Examples: Layers (Rhino/AutoCAD), Collections (Blender), Categories (Revit)
structural = Collection(
    name="Structural",
    applicationId="unique-id-123"  # Unique identifier
)

# Add elements to the collection
structural.elements = [
    column1,
    column2,
    beam1,
    beam2
]

# Nest collections for deeper hierarchy
building = Collection(name="Building")
building.elements = [
    structural,  # Nested collection
    architectural,  # Another nested collection
    mep  # Another nested collection
]
```

**Collection Properties:**

* `name` - Human-readable name (not necessarily unique)
* `elements` - List of contained objects (can include nested Collections)
* `applicationId` - Unique identifier for the collection
* `speckle_type` - `"Speckle.Core.Models.Collections.Collection"`

<Note>
  Collections map to familiar organizational structures:

  * **Rhino/AutoCAD** - Layers
  * **Blender** - Collections
  * **Revit** - Categories or Worksets
  * **Custom** - Any logical grouping you define
</Note>

### Atomic vs Property Objects

Understanding the difference helps you structure data correctly:

**Atomic Objects** (stored in `elements`):

* Represent actual entities in your model
* Should be traversed when processing the model
* Examples: walls, beams, points, meshes, equipment

**Property Objects** (stored as typed properties):

* Referenced by atomic objects
* Provide metadata or configuration
* Examples: materials, render properties, styles, definitions

```python lines icon="python" theme={null}
from specklepy.objects import Base
from specklepy.objects.other import RenderMaterial
from specklepy.objects.geometry import Mesh

# Material is a property object (not in elements)
steel_material = RenderMaterial(
    name="Steel",
    diffuse=0xFF808080,
    metalness=0.9
)

# Beam is an atomic object (goes in elements)
beam = Base()
beam.name = "Beam-001"
beam.geometry = Mesh(...)
beam.material = steel_material  # Reference as property

# Collection holds atomic objects
structure = Collection(name="Structure")
structure.elements = [beam]  # Atomic object in elements
# structure.elements does NOT contain steel_material
```

### Traversing with `elements`

The `elements` convention enables predictable traversal:

```python lines icon="python" theme={null}
def traverse_elements(obj, depth=0):
    """Traverse object hierarchy using elements convention"""
    indent = "  " * depth
    print(f"{indent}{obj.speckle_type}: {getattr(obj, 'name', 'unnamed')}")
    
    # Check for elements property
    if hasattr(obj, "elements"):
        for element in obj.elements:
            traverse_elements(element, depth + 1)

# Works consistently across all data types
project = operations.receive(object_id, remote_transport=transport)
traverse_elements(project)
```

<Warning>
  **Not all objects use `elements`**

  Simple geometry objects (Point, Line, Mesh) typically don't have `elements` - they are leaf nodes. Always check `hasattr(obj, "elements")` before accessing it.
</Warning>

## Mixing Types

Real-world data often mixes types, organized using the `elements` convention:

```python lines icon="python" theme={null}
from specklepy.api import operations
from specklepy.objects.models.collections import Collection

obj = operations.receive(object_id, remote_transport=transport)

# Root might be a Collection
print(f"Project: {obj.name}")

# Iterate through elements (the standard way)
for element in obj.elements:
    print(f"Element: {element.speckle_type}")
    
    # Some might be simple geometry
    if element.speckle_type.startswith("Objects.Geometry"):
        print(f"  Simple geometry: {element}")
    
    # Some might be complex BIM objects
    elif hasattr(element, "properties"):
        print(f"  BIM object with properties")
    
    # Some might be nested Collections
    elif isinstance(element, Collection):
        print(f"  Nested collection: {element.name}")
        for child in element.elements:
            print(f"    Child: {child.speckle_type}")
```

## Summary

<CardGroup cols={3}>
  <Card title="Custom Data" icon="code">
    **You create it**

    * Direct access
    * Simple structure
    * Full control
  </Card>

  <Card title="Simple Model" icon="cube">
    **Geometry + Props**

    * Properties dict
    * DisplayValue
    * Moderate nesting
  </Card>

  <Card title="Complex BIM" icon="building">
    **Rich BIM Data**

    * Deep nesting
    * Properties dict
    * Defensive code
  </Card>
</CardGroup>

Choose your approach based on the data type:

* **Custom** → Direct property access
* **Simple** → Check common properties
* **Complex** → Defensive traversal

## Next Steps

<CardGroup cols={2}>
  <Card title="Simple Data Patterns" icon="shapes" href="/developers/sdks/python/guides/simple-data-patterns">
    Work with custom and simple model data
  </Card>

  <Card title="BIM Data Patterns" icon="building" href="/developers/sdks/python/guides/bim-data-patterns">
    Handle complex BIM and connector data
  </Card>

  <Card title="Data Traversal" icon="diagram-project" href="/developers/sdks/python/concepts/data-traversal">
    Learn traversal techniques for any structure
  </Card>

  <Card title="Objects & Base" icon="cube" href="/developers/sdks/python/concepts/objects">
    Understand the Base class
  </Card>
</CardGroup>
