Update a scene
Change geometry, materials, structures and endpoints with the Scene with_* methods or in place, and see which version each change advances.
A Scene is immutable. There are two ways to change what it describes:
- Replace parts with the
with_*methods. Each returns a newSceneand advances the right authored version counters. - Update tensors in place, for example in an optimizer step. Every scene that holds the tensor sees the change, and the versions notice it.
Replace parts#
| Method | Use it to | Advances |
|---|---|---|
with_structure_geometry(structure_id, geometry, *, topology_changed=None) | Swap one structure's geometry. | geometry, and topology if it changed |
with_material(material_id, material) | Replace a material everywhere it is used. | material |
with_structure_material(structure_id, material, *, material_id=None, phase_screen=None) | Bind one structure to another material (and phase screen). | assignment, and material if the material is new to the scene |
with_structures(structures) | Replace the whole structure list. | topology and assignment |
with_endpoints(endpoints) | Replace the whole endpoint list. | geometry |
The examples below start from this scene:
from witwin.core import Box, PhysicalMaterial, Scene, Sphere, Structure
concrete = PhysicalMaterial(eps_r=5.24, sigma_e=0.05, name="concrete", material_id=1)
wall = Structure(Box(size=(0.2, 6.0, 3.0)), concrete, structure_id=1, assignment_id=1, surface_id=1)
pillar = Structure(Box(position=(3.0, 0.0, 1.5), size=(0.4, 0.4, 3.0)), concrete, structure_id=2, assignment_id=2, surface_id=2)
scene = Scene(structures=[wall, pillar])
def changed(before, after):
names = ("topology", "geometry", "material", "assignment")
return [n for n in names if getattr(before.versions, n) != getattr(after.versions, n)]Swap a geometry#
moved = scene.with_structure_geometry(2, Box(position=(3.5, 0.0, 1.5), size=(0.4, 0.4, 3.0)))
round_pillar = scene.with_structure_geometry(2, Sphere(position=(3.0, 0.0, 1.5), radius=0.3))
print(changed(scene, moved), changed(scene, round_pillar))['geometry'] ['topology', 'geometry']Core detects a topology change when the geometry class changes or a mesh's face tensor changes. Pass topology_changed=True to force it. Passing topology_changed=False when Core detects a change raises ValueError.
Replace a material everywhere#
Give the replacement the same material_id and a higher version:
wet = PhysicalMaterial(eps_r=7.0, sigma_e=0.2, name="wet concrete", material_id=1, version=1)
rainy = scene.with_material(1, wet)
print(changed(scene, rainy), [s.material.name for s in rainy.structures])['material'] ['wet concrete', 'wet concrete']with_material raises ValueError if the new material's material_id differs from the one you name, and KeyError if no structure uses that ID.
Bind one structure to another material#
steel = PhysicalMaterial.perfect_conductor(name="steel", material_id=2)
steel_pillar = scene.with_structure_material(2, steel)
print(changed(scene, steel_pillar), steel_pillar.structure(2).material.name)['material', 'assignment'] steelThe material's own material_id is used. For a MaterialSpec without one, pass material_id=. To change the material shared by several structures, use with_material instead: with_structure_material refuses to give an existing MaterialId to a different material object.
Replace endpoints#
from witwin.core import AntennaState
with_radio = scene.with_endpoints([AntennaState(1, "tx", (1.0, 1.0, 2.0))])
print(changed(scene, with_radio))['geometry']Update tensors in place#
Build the scene from tensors and change them in place. No new scene is needed:
import torch
eps = torch.tensor(5.24)
position = torch.tensor([3.0, 0.0, 1.5])
live = Scene(structures=[
Structure(Box(position=position, size=(0.4, 0.4, 3.0)), PhysicalMaterial(eps_r=eps, material_id=3), structure_id=3, assignment_id=3, surface_id=3)
])
before = live.versions
with torch.no_grad():
position[0] += 0.5
after_move = live.versions
with torch.no_grad():
eps.mul_(1.1)
print(before.geometry != after_move.geometry, before.material == after_move.material,
after_move.material != live.versions.material)True True TrueEach in-place operation increments PyTorch's modification counter on the tensor, and the version of the domain that owns the tensor changes with it.
Warning
Replacing a Python attribute is not tracked. Only tensors and the authored counters are part of the versions. Do not change attributes of a geometry or material after you put it in a scene; replace it with a with_* method.