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 new Scene and 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#

MethodUse it toAdvances
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'] steel

The 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 True

Each 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.