Build a scene
Create structures and endpoints, share materials, set reproducible IDs, attach tags and metadata, and look structures up again.
This guide builds a small indoor scene step by step. Each step adds one feature of Scene and Structure.
Create structures#
A Structure takes a geometry and a material. Everything else is optional.
from witwin.core import Box, PhysicalMaterial, Scene, Structure
concrete = PhysicalMaterial(eps_r=5.24, sigma_e=0.05, name="concrete")
glass = PhysicalMaterial(eps_r=6.27, sigma_e=0.004, thickness_m=0.006, name="glass")
floor = Structure(Box(position=(0.0, 0.0, -0.1), size=(10.0, 8.0, 0.2)), concrete, name="floor")
north = Structure(Box(position=(0.0, 4.0, 1.5), size=(10.0, 0.2, 3.0)), concrete, name="north wall")
window = Structure(Box(position=(5.0, 0.0, 1.5), size=(0.05, 2.0, 1.2)), glass, name="window")
scene = Scene(structures=[floor, north, window])
print(len(scene.structures), [int(s.material_id) for s in scene.structures])3 [0, 0, 1]floor and north share the same concrete object, so they share one MaterialId. A scene allows that: one material object may cover many structures. It refuses two different material objects with the same ID.
Note
Build structures with Structure(geometry, material). The convenience method geometry.with_material(material) exists on every primitive, but in 0.4.0 it raises ImportError.
Add endpoints#
Endpoints are transmitters and receivers. AntennaState needs an ID, a role ("tx" or "rx") and a position. A scene rejects two endpoints with the same ID.
from witwin.core import AntennaState
access_point = AntennaState(1, "tx", (0.0, 0.0, 2.5), power_w=0.1)
laptop = AntennaState(2, "rx", (3.0, -2.0, 0.8))
scene = Scene(structures=[floor, north, window], endpoints=[access_point, laptop])For orientation, polarization, arrays and receiver grids, see Place antennas and receiver grids.
Use reproducible IDs#
Automatic IDs depend on construction order. When the IDs must be stable across runs (for example because results are stored with them), pass them yourself:
concrete = PhysicalMaterial(eps_r=5.24, sigma_e=0.05, name="concrete", material_id=1)
floor = Structure(
Box(position=(0.0, 0.0, -0.1), size=(10.0, 8.0, 0.2)),
concrete,
name="floor",
structure_id=100,
assignment_id=100,
surface_id=100,
)
print(int(floor.structure_id), int(floor.material_id), int(floor.surface_id))100 1 100The structure takes its material_id from the material. Pass material_id= to Structure only to override it. See Identity and versions.
Attach tags, metadata and priority#
door = Structure(
Box(position=(-5.0, 0.0, 1.0), size=(0.05, 0.9, 2.0)),
PhysicalMaterial(eps_r=2.0, name="wood"),
name="door",
tags=("openable", "interior"),
metadata={"source": "floor plan rev B"},
priority=1,
enabled=True,
)
print(door.tags, dict(door.metadata), door.priority)('openable', 'interior') {'source': 'floor plan rev B'} 1Core stores tags, metadata, priority and enabled for solvers and tools. It does not act on them. A solver may, for example, use priority to decide which structure wins where two overlap; check the solver's documentation. metadata is a read-only mapping. A Scene also takes metadata=.
Look structures up#
scene = Scene(structures=[floor, door])
print(scene.structure(100).name)
for assignment in scene.assignments:
print(int(assignment.surface_id), assignment.material.name)floor
100 concrete
101 woodscene.structure(id) raises KeyError for an unknown ID. scene.assignments lists one MaterialAssignment per structure: which material (and phase screen) covers which surface. The door's surface ID is automatic. It is 101 because reserving 100 for the floor moved the counter past it.
Next steps#
- Shapes: Create geometry and Load a mesh.
- Materials: Define materials.
- Changes: Update a scene.