Quickstart

Build a Core scene, turn its geometry into meshes and occupancy grids, evaluate a material, and take a gradient through a shape parameter.

This page builds a small scene and uses each part of Core once. It runs on CPU in a few seconds. You need witwin installed (see Installation).

  1. Build a scene

    A scene is a list of structures and a list of antenna endpoints. A structure binds one geometry to one material. Lengths are in metres.

    import torch
     
    from witwin.core import AntennaState, Box, PhysicalMaterial, Scene, Sphere, Structure
     
    concrete = PhysicalMaterial(eps_r=5.24, sigma_e=0.05, name="concrete")
    metal = PhysicalMaterial.perfect_conductor(name="metal")
     
    wall = Structure(Box(position=(4.0, 0.0, 1.5), size=(0.2, 6.0, 3.0)), concrete, name="wall")
    ball = Structure(Sphere(position=(2.0, 1.0, 1.0), radius=0.3), metal, name="ball")
     
    tx = AntennaState(1, "tx", (0.0, 0.0, 1.5), power_w=1.0)
    rx = AntennaState(2, "rx", (3.0, -1.0, 1.5))
     
    scene = Scene(structures=[wall, ball], endpoints=[tx, rx])
    print([s.name for s in scene.structures], [e.role for e in scene.endpoints])
    ['wall', 'ball'] ['tx', 'rx']
  2. Read the identities

    Every structure gets a stable StructureId, MaterialId, AssignmentId and SurfaceId. Solvers key their caches on them. In a fresh Python process the automatic IDs start at 0.

    for s in scene.structures:
        print(s.name, int(s.structure_id), int(s.material_id), int(s.assignment_id), int(s.surface_id))
    wall 0 0 0 0
    ball 1 1 1 1

    Pass explicit IDs (for example Structure(..., structure_id=10)) when you need the same IDs in every run. See Identity and versions.

  3. Turn geometry into a mesh

    Every geometry can produce a world-space triangle mesh as two tensors.

    vertices, faces = ball.geometry.to_mesh(segments=8)
    print(vertices.shape, faces.shape, faces.dtype)
    torch.Size([144, 3]) torch.Size([224, 3]) torch.int64
  4. Query distances and occupancy

    signed_distance is negative inside, zero on the surface and positive outside. to_mask turns it into a soft occupancy between 0 and 1.

    x = torch.linspace(3.5, 4.5, 51)   # 2 cm spacing across the wall
    y = torch.linspace(-3.5, 3.5, 71)  # 10 cm spacing
    z = torch.linspace(-0.5, 3.5, 41)  # 10 cm spacing
    xx, yy, zz = torch.meshgrid(x, y, z, indexing="ij")
     
    distance = wall.geometry.signed_distance(xx, yy, zz)
    occupancy = wall.geometry.to_mask(xx, yy, zz)
    print(tuple(distance.shape), round(float(distance.min()), 3))
    print(round(float(occupancy.sum()) * 0.02 * 0.1 * 0.1, 2), "m^3")
    (51, 71, 41) -0.1
    3.62 m^3

    The deepest point is 0.1 m inside the 0.2 m thick wall. Summing occupancy times the cell volume gives about the wall volume, 0.2 × 6 × 3 = 3.6 m³. The small excess comes from the 2 cm sampling of the wall thickness.

  5. Evaluate a material

    Materials report their complex relative permittivity at a frequency in Hz.

    sample = concrete.evaluate_at_frequency(3.5e9)
    print(sample.eps_r, metal.capabilities().perfect_conductor)
    (5.24-0.25678719406460493j) True
  6. Take a gradient

    Pass a tensor with requires_grad=True anywhere a continuous value goes. Core keeps the tensor itself, so the autograd graph reaches it.

    radius = torch.tensor(0.3, requires_grad=True)
    ball_geometry = Sphere(position=(2.0, 1.0, 1.0), radius=radius)
     
    axis = torch.linspace(-0.5, 0.5, 41)
    xx, yy, zz = torch.meshgrid(axis + 2.0, axis + 1.0, axis + 1.0, indexing="ij")
    volume = ball_geometry.to_mask(xx, yy, zz, beta=0.01).sum() * 0.025**3
    volume.backward()
     
    print(round(volume.item(), 4), round(radius.grad.item(), 3))
    0.1133 1.131

    The volume is close to 4/3 π r³ = 0.113 m³, and the gradient is close to its derivative 4 π r² = 1.13 m².

  7. Take a snapshot

    Solvers read a SceneSnapshot: the scene at one time. A static scene gives the same snapshot at every time.

    snapshot = scene.snapshot(time_s=0.0)
    print(len(snapshot.structures), snapshot.versions == scene.versions)
    2 True

Next steps#