Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/source/api/radio_materials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ Moreover, by default, the scattering coefficient, :math:`S`, of these materials
.. autoclass:: sionna.rt.ITURadioMaterial
:members:

.. autofunction:: sionna.rt.register_radio_material

.. autofunction:: sionna.rt.register_itu_radio_material


Scattering Patterns
-------------------
Expand Down
171 changes: 158 additions & 13 deletions doc/source/developer/dev_custom_radio_materials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ They implement all necessary components to simulate the interaction
between radio waves and objects composed of specific materials.

Modifying Parameters of Radio Materials
******************************************
***************************************

To show how to modify the parameters of radio materials, we start by loading
a scene that consists only of a single reflector.
Expand Down Expand Up @@ -96,7 +96,7 @@ We can see how the reflected path gain increases as the conductivity of the refl
is set to higher values.

Calibrating Material Parameters Through Gradient Descent
************************************************************
********************************************************

We consider a simple example in which we aim to retrieve the conductivity of the
a radio material through gradient descent.
Expand Down Expand Up @@ -234,7 +234,7 @@ obtained using the reference scene instantiated at the beginning of this guide.
:width: 70 %

Custom Radio Materials
************************
**********************

Compared to what was done in the previous section, we will now implement a
scattering model by defining a new class that inherits from the
Expand All @@ -247,7 +247,7 @@ Sionna RT. It is highly recommended to first read the
radio wave propagation.

Representation of Jones vector and Matrices
=============================================
===========================================

As detailed in the `Primer on Electromagnetics <../em_primer.html>`_, a wave phasor
is typically represented by a Jones vector :math:`\mathbf{E} \in \mathbb{C}^2`.
Expand Down Expand Up @@ -279,7 +279,7 @@ write equations.
However, all implementations use the real-valued representation.

Implicit Basis
===============
==============

A wave phasor :math:`\mathbf{E}` is expressed by two arbitrary orthogonal
polarization directions S and P:
Expand Down Expand Up @@ -313,7 +313,7 @@ Moreover, it is required that the result of applying this Jones matrix is a
Jones vector that also describes the scattered wave using the implicit basis.

The Local Interaction Basis
=============================
===========================

Computing the Jones matrix and direction of propagation of the scattered wave
resulting from an interaction is facilitated in Sionna RT by defining a local
Expand All @@ -332,13 +332,13 @@ in the local coordinate system, we therefore have
:width: 100 %

Mandatory Subclass Methods
============================
==========================

Implementing a custom radio material requires defining a class that inherits from
:class:`~sionna.rt.RadioMaterialBase` and implements the following methods:

`sample()`
-----------
----------
Samples an interaction type and the direction of propagation of the scattered wave
(which typically depend on the sampled interaction type).
This function must return, among others, the sample interaction type, direction
Expand All @@ -356,7 +356,7 @@ of incident rays interacting with the material resulting in independently sample
scattered rays that model well the scattered field.

`eval()`
---------
--------
Evaluates the Jones matrix for a given interaction type, direction of incidence,
and direction of scattering. Compared to :meth:`~sionna.rt.RadioMaterialBase.sample`,
this method does not sample the material.
Expand All @@ -367,17 +367,17 @@ Returns the probability that a given interaction type and direction of scatterin
are sampled conditioned on a given direction of incidence.

`traverse()`
-------------
------------
Traverses the attributes and objects of the material. This method is used to
record the material parameters, and especially the differentiable parameters.

`to_string()`
--------------
-------------
Returns a string describing the material. This is used to "print" the material
in a humanly readable way.

Implementation of a Simple Radio Material Model
=================================================
===============================================

For simplicity, we will start by implementing a scattering model that only
reflects incident radio waves specularly, and such that the energy of the reflected
Expand Down Expand Up @@ -807,7 +807,7 @@ As expected, the gradient is positive as increasing the path gain requires
increasing :math:`g`.

A More Complex Material Model
===============================
=============================

Let's now enhance the previous radio material model by incorporating support for
refraction, which refers to radio waves passing through the material.
Expand Down Expand Up @@ -1160,3 +1160,148 @@ reflected and a transmitted path.
{'custom-mat-instance': EnhancedCustomRadioMaterial[g=[0.7]]}

Tracing paths can be done as for the previous example.

Registering Custom Radio Materials Before Loading Scenes
********************************************************

When loading scene files (e.g. exported from Blender), shapes may reference custom radio materials or new ITU material names.
To allow Sionna RT to recognize and assign these materials automatically during scene loading, you can register them prior to calling :func:`~sionna.rt.load_scene`. While it is possible to register a custom radio material by registering a custom BSDF plugin (see above), it is recommended, for simpler use cases, to use the :func:`~sionna.rt.register_radio_material` function to register a :class:`~sionna.rt.RadioMaterialBase` instance or the :func:`~sionna.rt.register_itu_radio_material` function to register a new ITU material.

Registering Custom RadioMaterial Instances
==========================================

You can register a :class:`~sionna.rt.RadioMaterialBase` instance using :func:`~sionna.rt.register_radio_material`:

.. code-block:: python

from sionna.rt import RadioMaterial, register_radio_material, load_scene

# Instantiate custom radio material
my_mat = RadioMaterial("my_custom_mat", relative_permittivity=5.0, conductivity=0.01)

# Register it so scenes can reference "my_custom_mat"
register_radio_material(my_mat)

# Load scene file referencing "my_custom_mat"
scene = load_scene("my_scene.xml")

In the scene file (e.g., ``my_scene.xml``), the BSDF node can explicitly use ``type="radio-material"`` (or a generic BSDF type matching the registered material name):

.. code-block:: xml

<scene version="2.1.0">
<bsdf type="radio-material" id="my_custom_mat"/>
<shape type="ply" id="wall">
<string name="filename" value="wall.ply"/>
<ref name="bsdf" id="my_custom_mat"/>
</shape>
</scene>

Registering Custom ITU Radio Materials
======================================

To define a new ITU material or update parameters of existing ITU materials, use :func:`~sionna.rt.register_itu_radio_material`:

.. code-block:: python

from sionna.rt import register_itu_radio_material, load_scene

# Register a custom ITU material with frequency parameters (a, b, c, d) and color
register_itu_radio_material(
"custom_wood",
{(0.1, 100.0): (2.5, 0.0, 0.005, 1.0)},
(0.4, 0.2, 0.1)
)

# Load scene referencing "itu_custom_wood"
scene = load_scene("my_itu_scene.xml")

In the XML scene file, ITU radio materials can be declared using the explicit ``type="itu-radio-material"`` syntax, specifying the ITU material name via the ``type`` property:

.. code-block:: xml

<scene version="2.1.0">
<bsdf type="itu-radio-material" id="itu_custom_wood">
<string name="type" value="custom_wood"/>
</bsdf>
<shape type="ply" id="wall">
<string name="filename" value="wall.ply"/>
<ref name="bsdf" id="itu_custom_wood"/>
</shape>
</scene>

Note that Sionna RT also supports legacy or Blender exported scenes where the BSDF element ID starts with ``itu_`` or ``mat-itu_`` (e.g. ``<bsdf type="diffuse" id="itu_custom_wood"/>``), which is automatically converted to an ITU radio material during scene preprocessing.

Multiple Material Customizations Referencing the Same ITU Material Type
-----------------------------------------------------------------------

Using the explicit XML syntax (``type="itu-radio-material"``), you can define multiple distinct BSDF nodes with unique IDs that all reference the same ITU material type (whether built-in or custom registered), while providing different overrides such as thickness or color:

.. code-block:: xml

<scene version="2.1.0">
<bsdf type="itu-radio-material" id="my_custom_thick_wood">
<string name="type" value="custom_wood"/>
<float name="thickness" value="0.25"/>
</bsdf>

<bsdf type="itu-radio-material" id="my_custom_thin_wood">
<string name="type" value="custom_wood"/>
<float name="thickness" value="0.02"/>
</bsdf>

<shape type="ply" id="thick_wall">
<string name="filename" value="wall.ply"/>
<ref name="bsdf" id="my_custom_thick_wood"/>
</shape>
<shape type="ply" id="thin_wall">
<string name="filename" value="panel.ply"/>
<ref name="bsdf" id="my_custom_thin_wood"/>
</shape>
</scene>

Overriding Material Attributes (Thickness & Color) in Scene Files
-----------------------------------------------------------------

For ITU radio materials, since each BSDF node with an ``itu-radio-material``
type (or the legacy ``itu_``/``mat-itu_`` naming convention) creates its own,
independent, material instance, specifying attributes directly inside the
XML scene file (such as ``<float name="thickness" value="..."/>`` or
``<rgb name="color" value="..."/>``) freely overrides the predefined
attributes for that instance:

.. code-block:: xml

<scene version="2.1.0">
<!-- Thickness and color overrides for an ITU radio material -->
<bsdf type="itu-radio-material" id="itu_custom_wood">
<string name="type" value="custom_wood"/>
<float name="thickness" value="0.25"/>
<rgb name="color" value="0.9, 0.1, 0.1"/>
</bsdf>
</scene>

For custom materials registered with :func:`~sionna.rt.register_radio_material`,
however, only ``color`` can be overridden this way:

.. code-block:: xml

<scene version="2.1.0">
<!-- Color override for a registered RadioMaterial -->
<bsdf type="radio-material" id="my_custom_mat">
<rgb name="color" value="0.8, 0.2, 0.2"/>
</bsdf>
</scene>

This is because a registered material is a single, shared, Python instance
that may be referenced by many shapes across many scenes. ``color`` is
purely a visual attribute that never affects ray tracing results, so
overriding it per shape simply returns an independent copy of the material
(see :meth:`~sionna.rt.RadioMaterialBase.with_color`); the registered
instance itself, and every other shape using it, is left untouched. Other
attributes, such as ``thickness``, do affect ray tracing results, so they
cannot be overridden this way: doing so would make shapes that are
supposed to share one physical material behave differently depending on
which scene happened to load last. Set those attributes directly on the
registered material instead, e.g. through :func:`~sionna.rt.register_radio_material`
or :func:`~sionna.rt.register_itu_radio_material`.
4 changes: 2 additions & 2 deletions src/sionna/rt/radio_materials/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"""Module implementing radio materials for the Sionna RT"""

from .radio_material_base import RadioMaterialBase
from .radio_material import RadioMaterial
from .itu_material import ITURadioMaterial
from .radio_material import RadioMaterial, radio_material_registry, register_radio_material
from .itu_material import ITURadioMaterial, register_itu_radio_material
from .scattering_pattern import register_scattering_pattern, \
scattering_pattern_registry, \
ScatteringPattern, \
Expand Down
56 changes: 53 additions & 3 deletions src/sionna/rt/radio_materials/itu_material.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""ITU radio materials"""

import mitsuba as mi
from typing import Tuple, Callable
from typing import Tuple, Callable, Mapping

from .itu import itu_material, ITU_MATERIALS_PROPERTIES
from .radio_material import RadioMaterial
Expand Down Expand Up @@ -109,14 +109,19 @@ def __init__(
# 2. `color`, `reflectance` or `base_color` property specified in the
# props (scene dictionary or XML file).
# 3. Default color from `ITU_MATERIAL_COLORS`.
# 4. Set color to :py:class:`None`, which results in a random color being used.
if color is None:
color = ITURadioMaterial.ITU_MATERIAL_COLORS[itu_type]
if has_props:
for pname in ("color", "reflectance", "base_color"):
if pname in props:
color = tuple(props[pname])
del props[pname]
props["color"] = mi.ScalarColor3f(color)
break
if color is None:
color = ITURadioMaterial.ITU_MATERIAL_COLORS.get(itu_type, None) # Color is allowed to be left unspecified (e.g., for custom user-defined ITU materials)

if color is not None and has_props:
props["color"] = mi.ScalarColor3f(color)

# Frequency update callback
def cb(f: float):
Expand Down Expand Up @@ -146,6 +151,33 @@ def itu_type(self):
"""
return self._itu_type

def with_color(self, color: tuple[float, float, float]) -> "ITURadioMaterial":
r"""
Returns a new :class:`ITURadioMaterial`, identical to this one
except for its ``color``

See :meth:`~sionna.rt.RadioMaterialBase.with_color` for details.

:param color: RGB (red, green, blue) color of the returned material

:return: New :class:`ITURadioMaterial` with the same properties as this one, but with the specified ``color``.
"""
new = ITURadioMaterial(
name=self.name,
itu_type=self.itu_type,
thickness=self.thickness,
scattering_coefficient=self.scattering_coefficient,
xpd_coefficient=self.xpd_coefficient,
color=color,
)
# `scattering_pattern` cannot be passed to the constructor above:
# it expects the *name* of a registered scattering pattern factory,
# whereas the `scattering_pattern` attribute returns the
# already-built `ScatteringPattern` instance. It must therefore be
# copied onto the new instance directly instead.
new.scattering_pattern = self.scattering_pattern
return new

def to_string(self) -> str:
r"""
Returns a string describing the object
Expand All @@ -161,3 +193,21 @@ def to_string(self) -> str:

mi.register_bsdf("itu-radio-material",
lambda props: ITURadioMaterial(props=props))


def register_itu_radio_material(
name: str,
parameters: Mapping[tuple[float, float], tuple[float, float, float, float]],
color: tuple[float, float, float] | None = None
) -> None:
# pylint: disable=line-too-long
r"""
Registers a custom ITU radio material or updates an existing ITU material definition.

:param name: Name of the ITU radio material to register.
:param parameters: A mapping of frequency ranges in GHz ``(f_min, f_max)`` to tuples of ITU parameters ``(a, b, c, d)`` as defined in recommendation ITU-R P.2040.
:param color: Optional RGB (red, green, blue) color tuple for rendering/previewing, where each component is in :math:`[0, 1]`. If set to :py:class:`None`, then a random color is used.
"""
ITU_MATERIALS_PROPERTIES[name] = dict(parameters)
if color is not None:
ITURadioMaterial.ITU_MATERIAL_COLORS[name] = color
Loading