Materials

Reference for PhysicalMaterial, layers, roughness, phase screens, power-law dispersion, the material protocols and sample types.

The material classes live in witwin.core.material and are exported from witwin.core. For the formulas, see Material model; for examples, see Define materials.

from witwin.core import (
    DispersionSpec, FrequencyMaterialSample, MaterialCapabilities, MaterialLayer, MaterialSpec,
    PhaseScreen, PhysicalMaterial, PowerLawDispersion, StaticMaterialSample, SurfaceRoughness,
)

Continuous fields are typed ScalarLike (float | torch.Tensor). Python values are range-checked at construction and raise ValueError when out of range; tensors must be 0-d and floating point, and their values are not checked.

PhysicalMaterial#

class PhysicalMaterial(
    eps_r: ScalarLike = 1.0,
    mu_r: ScalarLike = 1.0,
    sigma_e: ScalarLike = 0.0,
    name: str | None = None,
    *,
    material_id: MaterialId | int | None = None,
    version: int = 0,
    thickness_m: ScalarLike = 0.1,
    layers=(),
    geometry_mode: str = "thin_sheet",
    roughness_front: SurfaceRoughness | None = None,
    roughness_back: SurfaceRoughness | None = None,
    dispersion: DispersionSpec | PowerLawDispersion | None = None,
    gain: ScalarLike = 1.0,
    scattering_coefficient: ScalarLike = 0.0,
    xpd_coefficient: ScalarLike = 0.0,
    conductor_model: Literal["finite", "perfect"] = "finite",
)

The canonical solver-neutral electromagnetic and surface material. Immutable (frozen dataclass). It implements MaterialSpec.

eps_rScalarLikedefault 1.0

Relative permittivity. Must be greater than 0.

mu_rScalarLikedefault 1.0

Relative permeability. Must be greater than 0.

sigma_eScalarLikedefault 0.0

Electric conductivity in S/m. Must be at least 0.

namestr | Nonedefault None

A label. Converted to str.

material_idMaterialId | int | Nonedefault None

None allocates a new ID; an integer reserves it.

versionintdefault 0

A counter you raise when you replace a material with new Python values under the same material_id. Read by material_version. Must be at least 0.

thickness_mScalarLikedefault 0.1

Slab thickness in metres. Must be greater than 0.

layersIterable[MaterialLayer]default ()

Layer stack, stored as a tuple. Other item types raise TypeError.

geometry_modestrdefault 'thin_sheet'

"thin_sheet" or "volumetric". Tells a solver how to treat the structure. Other values raise ValueError.

roughness_frontSurfaceRoughness | Nonedefault None

Roughness of the front face.

roughness_backSurfaceRoughness | Nonedefault None

Roughness of the back face.

dispersionDispersionSpec | PowerLawDispersion | Nonedefault None

Frequency dependence. See Material model.

gainScalarLikedefault 1.0

Gain factor, stored for solvers. Must be at least 0.

scattering_coefficientScalarLikedefault 0.0

Scattering coefficient, stored for solvers. Must be in [0, 1].

xpd_coefficientScalarLikedefault 0.0

Cross-polarization (XPD) coefficient, stored for solvers. Must be in [0, 1].

conductor_model'finite' | 'perfect'default 'finite'

"perfect" marks a perfect electric conductor while keeping finite parameters.

PhysicalMaterial.perfect_conductor#

@classmethod
perfect_conductor(name: str | None = "perfect_conductor", **kwargs) -> PhysicalMaterial

Returns a PEC material: eps_r=1.0, mu_r=1.0, sigma_e=0.0, conductor_model="perfect". Other keyword arguments go to the constructor. Raises TypeError if you pass conductor_model.

PhysicalMaterial.evaluate_static#

evaluate_static() -> StaticMaterialSample

Returns the bulk values (eps_r, mu_r, sigma_e) unchanged.

PhysicalMaterial.evaluate_at_frequency#

evaluate_at_frequency(frequency: ScalarLike) -> FrequencyMaterialSample

Returns the complex relative permittivity at frequency in Hz: eps_r - j * sigma_e / (2 * pi * f * eps_0) after any power-law scaling, or the value of a custom DispersionSpec. mu_r is returned unchanged; sigma_e is the (scaled) conductivity.

Raises ValueError for a negative Python frequency, for f = 0 with non-zero Python sigma_e, and when tensor inputs differ in device or dtype; TypeError for integer tensors.

from witwin.core import PhysicalMaterial
 
print(PhysicalMaterial(eps_r=4.0, sigma_e=0.01).evaluate_at_frequency(2.4e9).eps_r)
(4-0.07489626493550977j)

PhysicalMaterial.capabilities#

capabilities() -> MaterialCapabilities

Returns the capability flags described in Material model.

MaterialLayer#

@dataclass(frozen=True)
class MaterialLayer:
    thickness_m: ScalarLike
    eps_r: ScalarLike = 1.0
    sigma_e: ScalarLike = 0.0
    mu_r: ScalarLike = 1.0
    dispersion: DispersionSpec | None = None

One homogeneous layer of a stack. thickness_m, eps_r and mu_r must be greater than 0, sigma_e at least 0. dispersion must implement DispersionSpec (a PowerLawDispersion does).

evaluate_at_frequency(frequency_hz) returns a FrequencyMaterialSample for the layer, with the same formula as PhysicalMaterial.

Note

With a PowerLawDispersion, MaterialLayer.evaluate_at_frequency returns the unscaled sigma_e in the sample, while PhysicalMaterial.evaluate_at_frequency returns the scaled one. The complex eps_r is scaled in both.

SurfaceRoughness#

@dataclass(frozen=True)
class SurfaceRoughness:
    rms_height_m: ScalarLike
    correlation_length_x_m: ScalarLike
    correlation_length_y_m: ScalarLike
    principal_axis_rad: ScalarLike = 0.0
    correlation: str = "gaussian"

Gaussian-correlated surface roughness. rms_height_m must be at least 0, both correlation lengths greater than 0. principal_axis_rad is the angle of the x correlation axis. correlation must be "gaussian".

PhaseScreen#

@dataclass(frozen=True)
class PhaseScreen:
    height: torch.Tensor
    height_scale_m: ScalarLike
    height_offset_m: ScalarLike = 0.0
    realization_id: int = 0
    mode: str = "realization_coherent"
    correlation: SurfaceRoughness | None = None
    quadrature_tolerance: ScalarLike = 1.0e-4

A logical surface height map. Attach it to a Structure (phase_screen=), not to a material. The solver owns its storage and interpretation.

heighttorch.Tensor (H, W)required

Floating-point height map with H, W > 0. Otherwise ValueError or TypeError.

height_scale_mScalarLikerequired

Height scale in metres. Must be greater than 0.

height_offset_mScalarLikedefault 0.0

Height offset in metres.

realization_idintdefault 0

Which random realization the map represents. Must be at least 0.

modestrdefault 'realization_coherent'

"realization_coherent" or "ensemble_bsdf".

correlationSurfaceRoughness | Nonedefault None

The statistics the map was drawn from, if known.

quadrature_toleranceScalarLikedefault 1.0e-4

Tolerance for a solver's numerical integration. Must be greater than 0.

PowerLawDispersion#

@dataclass(frozen=True)
class PowerLawDispersion:
    reference_frequency_hz: ScalarLike
    eps_r_exponent: ScalarLike = 0.0
    sigma_e_exponent: ScalarLike = 0.0

Scales a material's base values with frequency: eps_r * (f / f_ref) ** eps_r_exponent and sigma_e * (f / f_ref) ** sigma_e_exponent. reference_frequency_hz must be greater than 0.

scale(frequency_hz) returns the two factors as a tuple. complex_eps() raises TypeError: the dispersion needs the material's base values, so call PhysicalMaterial.evaluate_at_frequency instead.

DispersionSpec#

@runtime_checkable
class DispersionSpec(Protocol):
    def complex_eps(self, frequency_hz: ScalarLike) -> ComplexLike: ...

Any object with complex_eps(frequency_hz) returning the complex relative permittivity. Core uses the result as is.

MaterialSpec#

@runtime_checkable
class MaterialSpec(Protocol):
    name: str | None
    def capabilities(self) -> MaterialCapabilities: ...
    def evaluate_static(self) -> StaticMaterialSample: ...
    def evaluate_at_frequency(self, frequency: ScalarLike) -> FrequencyMaterialSample: ...

The contract Structure accepts. See Write your own geometry or material.

MaterialCapabilities#

@dataclass(frozen=True)
class MaterialCapabilities:
    conductive: bool = False
    perfect_conductor: bool = False
    magnetic: bool = False
    anisotropic: bool = False
    dispersive: bool = False
    layered: bool = False
    rough: bool = False
    phase_screen: bool = False

Flags a material reports so that a solver can accept or refuse it.

StaticMaterialSample and FrequencyMaterialSample#

@dataclass(frozen=True)
class StaticMaterialSample:
    eps_r: ScalarLike
    mu_r: ScalarLike = 1.0
    sigma_e: ScalarLike = 0.0
 
 
@dataclass(frozen=True)
class FrequencyMaterialSample:
    eps_r: ScalarLike      # complex for PhysicalMaterial
    mu_r: ScalarLike = 1.0
    sigma_e: ScalarLike = 0.0

The values a material returns from evaluate_static() and evaluate_at_frequency().