diff --git a/README.md b/README.md index 06291205..40888145 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ On macOS, you need to install [tensorflow-macos](https://github.com/apple/tensor ``` >>> import sionna >>> print(sionna.__version__) - 0.19.0 + 0.19.1 ``` 3.) Once Sionna is installed, you can run the [Sionna "Hello, World!" example](https://nvlabs.github.io/sionna/examples/Hello_World.html), have a look at the [quick start guide](https://nvlabs.github.io/sionna/quickstart.html), or at the [tutorials](https://nvlabs.github.io/sionna/tutorials.html). @@ -97,7 +97,7 @@ We recommend to do this within a [virtual environment](https://docs.python.org/3 ``` >>> import sionna >>> print(sionna.__version__) - 0.19.0 + 0.19.1 ``` ## License and Citation diff --git a/doc/source/installation.rst b/doc/source/installation.rst index d96e04fa..018859db 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -39,7 +39,7 @@ e.g., using `conda `_. On macOS, you need to install `ten >>> import sionna >>> print(sionna.__version__) - 0.19.0 + 0.19.1 3.) Once Sionna is installed, you can run the `Sionna "Hello, World!" example `_, have a look at the `quick start guide `_, or at the `tutorials `_. @@ -111,4 +111,4 @@ e.g., using `conda `_. >>> import sionna >>> print(sionna.__version__) - 0.19.0 + 0.19.1 diff --git a/doc/source/made_with_sionna.rst b/doc/source/made_with_sionna.rst index 32620f41..fbbc8a3a 100644 --- a/doc/source/made_with_sionna.rst +++ b/doc/source/made_with_sionna.rst @@ -6,6 +6,15 @@ We love to see how Sionna is used by other researchers! For this reason, you fin If you want your paper/project and code be listed here, please send an email to `sionna@nvidia.com `_ with links to the paper (e.g., `arXiv `_) and code repository (e.g., `GitHub `_). +.. made-with-sionna:: + :title: Advancing Spectrum Anomaly Detection through Digital Twins + :authors: Anton Schösser, Friedrich Burmeister, Philipp Schulz, Mohd Danish Khursheed, Sinuo Ma, Gerhard Fettweis + :year: 2024 + :version: 0.15.1 + :link_arxiv: https://www.techrxiv.org/users/775914/articles/883996-advancing-spectrum-anomaly-detection-through-digital-twins + :link_github: https://github.com/akdd11/advancing-spectrum-anomaly-detection + :abstract: 6th generation (6G) cellular networks are expected to enable various safety-critical use cases, e.g., in the industrial domain, which require flawless operation of the network. Thus, resilience is one of the key requirements for 6G. A particularly critical point is that 6G, as any other wireless technology, is based on the open radio medium, making it susceptible to interference. Especially intentional interference, i.e., jamming, can severely degrade the network operability. Therefore, a new approach for detecting anomalies in the radio spectrum using a digital twin (DT) of the radio environment is presented in this work. This allows the integration of contextual awareness in the anomaly detection process and is thereby superior to state-of-the-art methods for spectrum anomaly detection. We propose a suitable system architecture and discuss the tasks of machine learning (ML) therein, particularly for reducing the computational complexity and to detect anomalies in an unsupervised manner. The feasibility of the approach is demonstrated by ray tracing simulations. The results indicate a strong detection capability in case of an accurate DT and thereby illustrate the potential of DTs to enhance monitoring of wireless networks in the future. + .. made-with-sionna:: :title: Physically Consistent RIS: From Reradiation Mode Optimization to Practical Realization :authors: Javad Shabanpour, Constantin Simovski, Giovanni Geraci diff --git a/examples/Autoencoder.ipynb b/examples/Autoencoder.ipynb index f8bc18f5..0341158d 100644 --- a/examples/Autoencoder.ipynb +++ b/examples/Autoencoder.ipynb @@ -85,21 +85,6 @@ " os.environ[\"CUDA_VISIBLE_DEVICES\"] = f\"{gpu_num}\"\n", "os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n", "\n", - "# Configure the notebook to use only a single GPU and allocate only as much memory as needed\n", - "# For more details, see https://www.tensorflow.org/guide/gpu\n", - "import tensorflow as tf\n", - "gpus = tf.config.list_physical_devices('GPU')\n", - "if gpus:\n", - " try:\n", - " tf.config.experimental.set_memory_growth(gpus[0], True)\n", - " except RuntimeError as e:\n", - " print(e)\n", - "# Avoid warnings from TensorFlow\n", - "tf.get_logger().setLevel('ERROR')\n", - "\n", - "from tensorflow.keras import Model\n", - "from tensorflow.keras.layers import Layer, Dense\n", - "\n", "# Import Sionna\n", "try:\n", " import sionna\n", diff --git a/examples/Weighted_BP_Algorithm.ipynb b/examples/Weighted_BP_Algorithm.ipynb index 19ac573e..d12d586c 100644 --- a/examples/Weighted_BP_Algorithm.ipynb +++ b/examples/Weighted_BP_Algorithm.ipynb @@ -77,18 +77,6 @@ " os.environ[\"CUDA_VISIBLE_DEVICES\"] = f\"{gpu_num}\"\n", "os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n", "\n", - "import tensorflow as tf\n", - "# Configure the notebook to use only a single GPU and allocate only as much memory as needed\n", - "# For more details, see https://www.tensorflow.org/guide/gpu\n", - "gpus = tf.config.list_physical_devices('GPU')\n", - "if gpus:\n", - " try:\n", - " tf.config.experimental.set_memory_growth(gpus[0], True)\n", - " except RuntimeError as e:\n", - " print(e)\n", - "# Avoid warnings from TensorFlow\n", - "tf.get_logger().setLevel('ERROR')\n", - "\n", "# Import Sionna\n", "try:\n", " import sionna\n", diff --git a/requirements.txt b/requirements.txt index fccbb53f..740bd9cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ numpy scipy >=1.6.0 matplotlib >= 3.5.3 importlib_resources -mitsuba >= 3.2.0 +mitsuba >= 3.2.0, <3.6.0 pythreejs >= 2.4.2 ipywidgets >= 8.0.4 ipydatawidgets == 4.3.2 diff --git a/setup.cfg b/setup.cfg index 4d0bb41b..aeeecde4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,10 +27,10 @@ python_requires = >=3.8 install_requires = tensorflow >=2.13.0, <2.16.0 numpy - matplotlib + matplotlib >= 3.5.3 scipy >=1.6.0 importlib_resources - mitsuba >= 3.2.0 + mitsuba >= 3.2.0, <3.6.0 pythreejs >= 2.4.2 ipywidgets >= 8.0.4 ipydatawidgets == 4.3.2 diff --git a/sionna/__init__.py b/sionna/__init__.py index 9586f8ed..1309f82e 100644 --- a/sionna/__init__.py +++ b/sionna/__init__.py @@ -5,7 +5,7 @@ """This is the Sionna library. """ -__version__ = '0.19.0' +__version__ = '0.19.1' from .config import config from .constants import * diff --git a/sionna/ofdm/pilot_pattern.py b/sionna/ofdm/pilot_pattern.py index 7b15b1d8..62e30494 100644 --- a/sionna/ofdm/pilot_pattern.py +++ b/sionna/ofdm/pilot_pattern.py @@ -335,7 +335,7 @@ def __init__(self, # Compute the length of a pilot sequence num_pilots = num_pilot_symbols*num_effective_subcarriers/num_seq - assert num_pilots%1==0, \ + assert (num_pilots/num_pilot_symbols)%1==0, \ """`num_effective_subcarriers` must be an integer multiple of `num_tx`*`num_streams_per_tx`.""" diff --git a/sionna/rt/coverage_map.py b/sionna/rt/coverage_map.py index b728199c..210918d5 100644 --- a/sionna/rt/coverage_map.py +++ b/sionna/rt/coverage_map.py @@ -506,19 +506,10 @@ def show(self, If set to `True`, then the position of the RIS are shown. Defaults to `False`. - show_association : bool - If set to `True`, then the cell-to-transmitter association - is shown ina second figure (based on the selected metric). - Defaults to `False`. - Output ------ : :class:`~matplotlib.pyplot.Figure` Figure showing the coverage map - - : :class:`~matplotlib.pyplot.Figure` - Figure showing the cell-to-transmitter association - Only returned if ``show_association`` is `True`. """ if metric not in ["path_gain", "rss", "sinr"]: diff --git a/sionna/rt/scene.py b/sionna/rt/scene.py index 4c0bb374..35dbf024 100644 --- a/sionna/rt/scene.py +++ b/sionna/rt/scene.py @@ -583,10 +583,6 @@ def trace_paths(self, max_depth=3, method="fibonacci", num_samples=int(1e6), If set to `True`, then the paths involving RIS are computed. Defaults to `True`. - ris : bool - If set to `True`, then the paths involving RIS are computed. - Defaults to `True`. - scat_keep_prob : float Probability with which to keep scattered paths. This is helpful to reduce the number of scattered paths computed, @@ -618,9 +614,6 @@ def trace_paths(self, max_depth=3, method="fibonacci", num_samples=int(1e6), ris_paths : :class:`~sionna.rt.Paths` Computed paths involving RIS - ris_paths : :class:`~sionna.rt.Paths` - Computed paths involving RIS - spec_paths_tmp : :class:`~sionna.rt.PathsTmpData` Additional data required to compute the EM fields of the specular paths @@ -637,9 +630,6 @@ def trace_paths(self, max_depth=3, method="fibonacci", num_samples=int(1e6), Additional data required to compute the EM fields of the paths involving RIS - ris_paths_tmp : :class:`~sionna.rt.PathsTmpData` - Additional data required to compute the EM fields of the paths - involving RIS """ if scat_keep_prob < 0. or scat_keep_prob > 1.: diff --git a/sionna/rt/solver_cm.py b/sionna/rt/solver_cm.py index c66d468d..0991607f 100644 --- a/sionna/rt/solver_cm.py +++ b/sionna/rt/solver_cm.py @@ -1686,9 +1686,9 @@ def _extract_active_ris_rays(self, active_ind, int_point, return output def _extract_active_rays(self, active_ind, int_point, previous_int_point, - primitives, e_field, field_es, field_ep, etas, scattering_coefficient, - xpd_coefficient, alpha_r, alpha_i, lambda_, ris, radii_curv, dirs_curv, - angular_opening): + primitives, e_field, field_es, field_ep, samples_tx_indices, k_tx, + etas, scattering_coefficient, xpd_coefficient, alpha_r, alpha_i, + lambda_, ris, radii_curv, dirs_curv, angular_opening): r""" Extracts the active rays. @@ -1718,6 +1718,12 @@ def _extract_active_rays(self, active_ind, int_point, previous_int_point, field_ep : [num_samples, 3], tf.float Direction of the P component of the field + samples_tx_indices : [num_samples], tf.int + Index of the source from which the path originates + + k_tx : [num_samples, 3], tf.float + Direction of departure from the source + etas : [num_shape], tf.complex | `None` Tensor containing the complex relative permittivities of all shapes @@ -1800,6 +1806,12 @@ def _extract_active_rays(self, active_ind, int_point, previous_int_point, Length of the last path segment, i.e., distance between `int_point` and `previous_int_point` + samples_tx_indices : [num_active_samples], tf.int + Index of the source from which the path originates + + k_tx : [num_active_samples, 3], tf.float + Direction of departure from the source + act_radii_curv : [num_active_samples, 2], tf.float Principal radii of curvature of the ray tubes @@ -1839,6 +1851,11 @@ def _extract_active_rays(self, active_ind, int_point, previous_int_point, # [num_active_samples] act_objects = tf.gather(self._primitives_2_objects, act_primitives, axis=0) + # [num_active_samples] + act_samples_tx_indices = tf.gather(samples_tx_indices, active_ind, + axis=0) + # [num_active_samples, 3] + act_k_tx = tf.gather(k_tx, active_ind, axis=0) # Extract the normals to the intersected primitives # [num_active_samples, 3] @@ -1903,8 +1920,9 @@ def _extract_active_rays(self, active_ind, int_point, previous_int_point, output = (act_e_field, act_field_es, act_field_ep, int_point, act_normals, act_etas, act_scat_coeff, act_k_i, act_xpd_coefficient, act_alpha_r, act_alpha_i, act_lambda_, - act_objects, act_dist, act_radii_curv, act_dirs_curv, - act_primitives, act_angular_opening) + act_objects, act_dist, act_samples_tx_indices, act_k_tx, + act_radii_curv, act_dirs_curv, act_primitives, + act_angular_opening) return output @@ -2010,8 +2028,9 @@ def _sample_interaction_phenomena(self, active, int_point, primitives, return reflect_ind, scatter_ind def _apply_reflection(self, active_ind, int_point, previous_int_point, - primitives, e_field, field_es, field_ep, etas, scattering_coefficient, - scattering, ris, radii_curv, dirs_curv, angular_opening): + primitives, e_field, field_es, field_ep, samples_tx_indices, k_tx, + etas, scattering_coefficient, scattering, ris, radii_curv, dirs_curv, + angular_opening): r""" Apply reflection. @@ -2038,6 +2057,12 @@ def _apply_reflection(self, active_ind, int_point, previous_int_point, field_ep : [num_samples, 3], tf.float Direction of the P component of the field + samples_tx_indices : [num_samples], tf.int + Index of the source from which the path originates + + k_tx : [num_samples, 3], tf.float + Direction of departure from the source + etas : [num_shape], tf.complex Complex relative permittivities of all shapes @@ -2076,6 +2101,12 @@ def _apply_reflection(self, active_ind, int_point, previous_int_point, k_r : [num_reflected_samples, 3], tf.float Direction of the reflected ray + samples_tx_indices : [num_reflected_samples], tf.int + Index of the source from which the path originates + + k_tx : [num_reflected_samples, 3], tf.float + Direction of departure from the source + normals : [num_reflected_samples, 3], tf.float Normals at the intersection points @@ -2094,8 +2125,8 @@ def _apply_reflection(self, active_ind, int_point, previous_int_point, # must be applied, and ensures that the normals are correctly oriented. act_data = self._extract_active_rays(active_ind, int_point, previous_int_point, primitives, e_field, field_es, field_ep, - etas, scattering_coefficient, None, None, None, None, ris, - radii_curv, dirs_curv, angular_opening) + samples_tx_indices, k_tx, etas, scattering_coefficient, None, None, + None, None, ris, radii_curv, dirs_curv, angular_opening) # [num_reflected_samples, num_tx_patterns, 2] e_field = act_data[0] # [num_reflected_samples, 3] @@ -2112,14 +2143,18 @@ def _apply_reflection(self, active_ind, int_point, previous_int_point, # Length of the last path segment # [num_reflected_samples] length = act_data[13] + # Index of the intersected source + samples_tx_indices = act_data[14] + # Direction of departure form the source + k_tx = act_data[15] if ris: # Principal radii and directions of curvatures # [num_reflected_samples, 2] - radii_curv = act_data[14] + radii_curv = act_data[16] # [num_reflected_samples, 2, 3] - dirs_curv = act_data[15] + dirs_curv = act_data[17] # [num_reflected_samples] - angular_opening = act_data[17] + angular_opening = act_data[18] # Compute the reflected field e_field, field_es, field_ep, k_r, radii_curv, dirs_curv\ @@ -2128,13 +2163,14 @@ def _apply_reflection(self, active_ind, int_point, previous_int_point, scattering, ris, length, radii_curv, dirs_curv) output = (e_field, field_es, field_ep, int_point, k_r, act_normals, - radii_curv, dirs_curv, angular_opening) + samples_tx_indices, k_tx, radii_curv, dirs_curv, + angular_opening) return output def _apply_scattering(self, active_ind, int_point, previous_int_point, - primitives, e_field, field_es, field_ep, etas, scattering_coefficient, - xpd_coefficient, alpha_r, alpha_i, lambda_, reflection, ris, radii_curv, - dirs_curv, angular_opening): + primitives, e_field, field_es, field_ep, samples_tx_indices, k_tx, + etas, scattering_coefficient, xpd_coefficient, alpha_r, alpha_i, + lambda_, reflection, ris, radii_curv, dirs_curv, angular_opening): r""" Apply scattering. @@ -2161,6 +2197,12 @@ def _apply_scattering(self, active_ind, int_point, previous_int_point, field_ep : [num_samples, 3], tf.float Direction of the P component of the field + samples_tx_indices : [num_samples], tf.int + Index of the source from which the path originates + + k_tx : [num_samples, 3], tf.float + Direction of departure from the source + etas : [num_shape], tf.complex Complex relative permittivities of all shapes @@ -2211,6 +2253,12 @@ def _apply_scattering(self, active_ind, int_point, previous_int_point, k_r : [num_scattered_samples, 3], tf.float Direction of the scattered ray + samples_tx_indices : [num_scattered_samples], tf.int + Index of the source from which the path originates + + k_tx : [num_scattered_samples, 3], tf.float + Direction of departure from the source + normals : [num_scattered_samples, 3], tf.float Normals at the intersection points @@ -2229,8 +2277,9 @@ def _apply_scattering(self, active_ind, int_point, previous_int_point, # must be applied, and ensures that the normals are correcly oriented. act_data = self._extract_active_rays(active_ind, int_point, previous_int_point, primitives, e_field, field_es, field_ep, - etas, scattering_coefficient, xpd_coefficient, alpha_r, alpha_i, - lambda_, ris, radii_curv, dirs_curv, angular_opening) + samples_tx_indices, k_tx, etas, scattering_coefficient, + xpd_coefficient, alpha_r, alpha_i, lambda_, ris, radii_curv, + dirs_curv, angular_opening) # [num_scattered_samples, num_tx_patterns, 2] e_field = act_data[0] # [num_scattered_samples, 3] @@ -2253,14 +2302,18 @@ def _apply_scattering(self, active_ind, int_point, previous_int_point, # Length of the last path segment # [num_scattered_samples] length = act_data[13] + # Index of the intersected source + samples_tx_indices = act_data[14] + # Direction of departure form the source + k_tx = act_data[15] if ris: # Principal radii and directions of curvatures # [num_scattered_samples, 2] - radii_curv = act_data[14] + radii_curv = act_data[16] # [num_scattered_samples, 2, 3] - dirs_curv = act_data[15] + dirs_curv = act_data[17] # [num_scattered_samples] - angular_opening = act_data[17] + angular_opening = act_data[18] # Compute the scattered field e_field, field_es, field_ep, k_r, radii_curv, dirs_curv,\ @@ -2271,12 +2324,13 @@ def _apply_scattering(self, active_ind, int_point, previous_int_point, radii_curv, angular_opening) output = (e_field, field_es, field_ep, int_point, k_r, act_normals, - radii_curv, dirs_curv, angular_opening) + samples_tx_indices, k_tx, radii_curv, dirs_curv, + angular_opening) return output def _apply_ris_reflection(self, active_ind, int_point, previous_int_point, - primitives, e_field, field_es, field_ep, radii_curv, dirs_curv, - angular_opening): + primitives, e_field, field_es, field_ep, samples_tx_indices, k_tx, + radii_curv, dirs_curv, angular_opening): r""" Apply scattering. @@ -2303,6 +2357,12 @@ def _apply_ris_reflection(self, active_ind, int_point, previous_int_point, field_ep : [num_samples, 3], tf.float Direction of the P component of the field + samples_tx_indices : [num_samples], tf.int + Index of the source from which the path originates + + k_tx : [num_samples, 3], tf.float + Direction of departure from the source + radii_curv : [num_active_samples, 2], tf.float Principal radii of curvature @@ -2329,7 +2389,13 @@ def _apply_ris_reflection(self, active_ind, int_point, previous_int_point, k_r : [num_ris_reflected_samples, 3], tf.float Direction of the reflected ray - normals : [num_scattered_samples, 3], tf.float + samples_tx_indices : [num_ris_reflected_samples], tf.int + Index of the intersected transmitter + + k_tx : [num_ris_reflected_samples, 3], tf.float + Direction of departure from the source + + normals : [num_ris_reflected_samples, 3], tf.float Normals at the intersection points radii_curv : [num_ris_reflected_samples, 2], tf.float @@ -2346,7 +2412,7 @@ def _apply_ris_reflection(self, active_ind, int_point, previous_int_point, # must be applied, and ensures that the normals are correctly oriented. act_data = self._extract_active_ris_rays(active_ind, int_point, previous_int_point, primitives, e_field, field_es, field_ep, - radii_curv, dirs_curv, angular_opening) + samples_tx_indices, k_tx, radii_curv, dirs_curv, angular_opening) # [num_ris_reflected_samples, num_tx_patterns, 2] e_field = act_data[0] # [num_ris_reflected_samples, 3] @@ -2367,6 +2433,10 @@ def _apply_ris_reflection(self, active_ind, int_point, previous_int_point, ris_ind = act_data[8] # [num_ris_reflected_samples] angular_opening = act_data[9] + # Index of the intersected source + samples_tx_indices = act_data[14] + # Direction of departure form the source + k_tx = act_data[15] # Compute the reflected field e_field, field_es, field_ep, k_r, normals, radii_curv, dirs_curv,\ @@ -2374,7 +2444,8 @@ def _apply_ris_reflection(self, active_ind, int_point, previous_int_point, e_field, field_es, field_ep, length, radii_curv, dirs_curv) output = (e_field, field_es, field_ep, int_point, k_r, normals, - radii_curv, dirs_curv, angular_opening) + samples_tx_indices, k_tx, radii_curv, dirs_curv, + angular_opening) return output def _shoot_and_bounce(self, @@ -2772,6 +2843,8 @@ def _shoot_and_bounce(self, updated_int_point = tf.zeros([0, 3], self._rdtype) updated_k_r = tf.zeros([0, 3], self._rdtype) normals = tf.zeros([0, 3], self._rdtype) + updated_samples_tx_indices = tf.zeros([0], tf.int32) + updated_k_tx = tf.zeros([0, 3], self._rdtype) if ris: updated_radii_curv = tf.zeros([0, 2], self._rdtype) updated_dirs_curv = tf.zeros([0, 2, 3], self._rdtype) @@ -2787,12 +2860,16 @@ def _shoot_and_bounce(self, # ref_radii_curv : [num_reflected_samples, 2] # ref_dirs_curv : [num_reflected_samples, 2, 3] # ref_ang_opening : [num_reflected_samples] + # ref_samples_tx_indices : [num_reflected_samples] + # ref_k_tx : [num_reflected_samples, 3] ref_e_field, ref_field_es, ref_field_ep, ref_int_point,\ - ref_k_r, ref_n, ref_radii_curv, ref_dirs_curv,\ - ref_ang_opening = self._apply_reflection(reflect_ind, + ref_k_r, ref_n, ref_samples_tx_indices, ref_k_tx,\ + ref_radii_curv, ref_dirs_curv, ref_ang_opening\ + = self._apply_reflection(reflect_ind, int_point, previous_int_point, primitives, e_field, - field_es, field_ep, etas, scattering_coefficient, - scattering, ris, radii_curv, dirs_curv, angular_opening) + field_es, field_ep, samples_tx_indices, k_tx, + etas, scattering_coefficient, scattering, ris, + radii_curv, dirs_curv, angular_opening) updated_e_field = tf.concat([updated_e_field, ref_e_field], axis=0) @@ -2804,6 +2881,10 @@ def _shoot_and_bounce(self, axis=0) updated_k_r = tf.concat([updated_k_r, ref_k_r], axis=0) normals = tf.concat([normals, ref_n], axis=0) + updated_samples_tx_indices =\ + tf.concat([updated_samples_tx_indices, + ref_samples_tx_indices], axis=0) + updated_k_tx = tf.concat([updated_k_tx, ref_k_tx], axis=0) if ris: updated_radii_curv = tf.concat([updated_radii_curv, ref_radii_curv], axis=0) @@ -2822,13 +2903,17 @@ def _shoot_and_bounce(self, # scat_radii_curv : [num_scattered_samples, 2] # scat_dirs_curv : [num_scattered_samples, 2, 3] # scat_ang_opening : [num_scattered_samples] + # scat_samples_tx_indices : [num_scattered_samples] + # scat_k_tx : [num_scattered_samples, 3] scat_e_field, scat_field_es, scat_field_ep, scat_int_point,\ - scat_k_r, scat_n, scat_radii_curv, scat_dirs_curv,\ - scat_ang_opening = self._apply_scattering(scatter_ind, + scat_k_r, scat_n, scat_samples_tx_indices, scat_k_tx,\ + scat_radii_curv, scat_dirs_curv, scat_ang_opening\ + = self._apply_scattering(scatter_ind, int_point, previous_int_point, primitives, e_field, - field_es, field_ep, etas, scattering_coefficient, - xpd_coefficient, alpha_r, alpha_i, lambda_, reflection, - ris, radii_curv, dirs_curv, angular_opening) + field_es, field_ep, samples_tx_indices, k_tx, + etas, scattering_coefficient, xpd_coefficient, alpha_r, + alpha_i, lambda_, reflection, ris, radii_curv, + dirs_curv, angular_opening) updated_e_field = tf.concat([updated_e_field, scat_e_field], axis=0) @@ -2840,6 +2925,10 @@ def _shoot_and_bounce(self, scat_int_point], axis=0) updated_k_r = tf.concat([updated_k_r, scat_k_r], axis=0) normals = tf.concat([normals, scat_n], axis=0) + updated_samples_tx_indices =\ + tf.concat([updated_samples_tx_indices, + scat_samples_tx_indices], axis=0) + updated_k_tx = tf.concat([updated_k_tx, scat_k_tx], axis=0) if ris: updated_radii_curv = tf.concat([updated_radii_curv, scat_radii_curv], axis=0) @@ -2858,12 +2947,15 @@ def _shoot_and_bounce(self, # ris_radii_curv : [num_ris_reflected_samples, 2] # ris_dirs_curv : [num_ris_reflected_samples, 2, 3] # ris_ang_opening : [num_ris_reflected_samples] + # ris_samples_tx_indices : [num_ris_reflected_samples] + # ris_k_tx : [num_ris_reflected_samples, 3] ris_e_field, ris_field_es, ris_field_ep, ris_int_point,\ - ris_k_r, ris_n, ris_radii_curv, ris_dirs_curv,\ - ris_ang_opening = self._apply_ris_reflection(ris_reflect_ind, + ris_k_r, ris_n, ris_samples_tx_indices, ris_k_tx,\ + ris_radii_curv, ris_dirs_curv, ris_ang_opening\ + = self._apply_ris_reflection(ris_reflect_ind, int_point, previous_int_point, primitives, e_field, - field_es, field_ep, radii_curv, dirs_curv, - angular_opening) + field_es, field_ep, samples_tx_indices, k_tx, + radii_curv, dirs_curv, angular_opening) updated_e_field = tf.concat([updated_e_field, ris_e_field], axis=0) updated_field_es = tf.concat([updated_field_es, ris_field_es], @@ -2880,21 +2972,23 @@ def _shoot_and_bounce(self, ris_dirs_curv], axis=0) updated_ang_opening = tf.concat([updated_ang_opening, ris_ang_opening], axis=0) + updated_samples_tx_indices =\ + tf.concat([updated_samples_tx_indices, + ris_samples_tx_indices], axis=0) + updated_k_tx = tf.concat([updated_k_tx, ris_k_tx], axis=0) e_field = updated_e_field field_es = updated_field_es field_ep = updated_field_ep k_r = updated_k_r int_point = updated_int_point + samples_tx_indices = updated_samples_tx_indices + k_tx = updated_k_tx if ris: radii_curv = updated_radii_curv dirs_curv = updated_dirs_curv angular_opening = updated_ang_opening - # Only keep TX indices for active rays - # [num_active_samples] - samples_tx_indices = tf.boolean_mask(samples_tx_indices, active) - ############################################### # Discard paths which path loss is below a # threshold @@ -2915,6 +3009,7 @@ def _shoot_and_bounce(self, normals = tf.gather(normals, active_ind, axis=0) samples_tx_indices = tf.gather(samples_tx_indices, active_ind, axis=0) + k_tx = tf.gather(k_tx, active_ind, axis=0) if ris: radii_curv = tf.gather(radii_curv, active_ind, axis=0) dirs_curv = tf.gather(dirs_curv, active_ind, axis=0) diff --git a/sionna/rt/solver_paths.py b/sionna/rt/solver_paths.py index ea6772ce..3e731c34 100644 --- a/sionna/rt/solver_paths.py +++ b/sionna/rt/solver_paths.py @@ -425,9 +425,6 @@ def trace_paths(self, max_depth, method, num_samples, los, reflection, ris_paths : :class:`~sionna.rt.Paths` Computed paths involving RIS - ris_paths : :class:`~sionna.rt.Paths` - Computed paths involving RIS - spec_paths_tmp : PathsTmpData Additional data required to compute the EM fields of the specular paths @@ -4251,11 +4248,12 @@ def _compute_directions_distances_delays_angles(self, paths, paths_tmp, # triangles can be considered twice. # Note that this is rare, as intersections rarely occur on # edges. - # The similarity measure used to distinguish paths if the - # distance between the angles of arrivals and departures. - # [num_targets, num_sources, max_num_paths, 4] - sim = tf.stack([theta_t, phi_t, theta_r, phi_r], axis=3) - # [num_targets, num_sources, max_num_paths, max_num_paths, 4] + # Paths are considered different if they have different + # angles of departure, angles of arrival, and total length. + # [num_targets, num_sources, max_num_paths, 5] + sim = tf.stack([theta_t, phi_t, theta_r, phi_r, total_distance], + axis=3) + # [num_targets, num_sources, max_num_paths, max_num_paths, 5] sim = tf.expand_dims(sim, axis=2) - tf.expand_dims(sim, axis=3) # [num_targets, num_sources, max_num_paths, max_num_paths] sim = tf.reduce_sum(tf.square(sim), axis=4)