> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/aerial/aodt/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/aerial/aodt/_mcp/server.

# EM Solver API

## EM Solver API

The EM engine is developed directly by NVIDIA and embedded modularly in the Aerial Omniverse Digital Twin through a specific interface. This makes it possible to integrate different EM engines when needed. This section gives an overview of the key mechanics of that interface.

NVIDIA's EM engine API provides functions to

* manage the device memory,
* perform EM calculations,
* and copy results to host memory.

All classes, member functions, and variables are defined in the `aerial_emsolver_api.h` header and use the C++/CUDA primitive data types.

### Data types

* **d\_complex**

  ```cpp
  typedef cuda::std::complex<float> d_complex
  ```

  Complex-valued data type used in both host code and device code.

* **d\_complex4**
  ```cpp
  typedef struct d_complex4 {
      d_complex m[4]{};
  } d_complex4
  ```
  An array of four `d_complex` elements.

* **CIRResult**
  ```cpp
  struct CIRResult {
      d_complex *cir_values{};
      float *cir_delays{};
      float2 *angles_of_departure{};
      float2 *angles_of_arrival{};

      int num_rx{};
      int num_samples{};
      int num_tx_horz{};
      int num_tx_vert{};
      int num_tx_pol{};
      int num_rx_horz{};
      int num_rx_vert{};
      int num_rx_pol{};
      int num_taps{};

      __host__ __device__ CIRResult(d_complex *cir_values, float *cir_delays,
                                    float2 *angles_of_departure,
                                    float2 *angles_of_arrival, int num_rx,
                                    int num_samples, int num_tx_horz,
                                    int num_tx_vert, int num_tx_pol,
                                    int num_rx_horz, int num_rx_vert,
                                    int num_rx_pol, int num_taps);

      __host__ __device__ int cir_value_index(int rx, int sample, int rx_h,
                                              int rx_v, int rx_p, int tx_h,
                                              int tx_v, int tx_p, int tap) const;
      __host__ __device__ int cir_delay_index(int rx, int sample, int rx_h,
                                              int rx_v, int tx_h, int tx_v,
                                              int tap) const;
      __host__ __device__ int angle_of_departure_index(int rx, int sample,
                                                       int rx_h, int rx_v,
                                                       int tx_h, int tx_v,
                                                       int tap) const;
      __host__ __device__ int angle_of_arrival_index(int rx, int sample,
                                                     int rx_h, int rx_v,
                                                     int tx_h, int tx_v,
                                                     int tap) const;

      size_t cir_value_size() const;
      size_t cir_delay_size() const;
      size_t angle_of_departure_size() const;
      size_t angle_of_arrival_size() const;

      __host__ __device__ d_complex get_cir_value(int rx, int sample, int rx_h,
                                                  int rx_v, int rx_p, int tx_h,
                                                  int tx_v, int tx_p,
                                                  int tap) const;
      __host__ __device__ void set_cir_value(int rx, int sample, int rx_h,
                                             int rx_v, int rx_p, int tx_h,
                                             int tx_v, int tx_p, int tap,
                                             d_complex value);

      __host__ __device__ float get_cir_delay(int rx, int sample, int rx_h,
                                              int rx_v, int tx_h, int tx_v,
                                              int tap) const;
      __host__ __device__ void set_cir_delay(int rx, int sample, int rx_h,
                                             int rx_v, int tx_h, int tx_v,
                                             int tap, float value);

      __host__ __device__ float2 get_angle_of_departure(int rx, int sample,
                                                        int rx_h, int rx_v,
                                                        int tx_h, int tx_v,
                                                        int tap) const;
      __host__ __device__ void set_angle_of_departure(int rx, int sample,
                                                      int rx_h, int rx_v,
                                                      int tx_h, int tx_v,
                                                      int tap, float2 value);

      __host__ __device__ float2 get_angle_of_arrival(int rx, int sample,
                                                      int rx_h, int rx_v,
                                                      int tx_h, int tx_v,
                                                      int tap) const;
      __host__ __device__ void set_angle_of_arrival(int rx, int sample,
                                                    int rx_h, int rx_v,
                                                    int tx_h, int tx_v,
                                                    int tap, float2 value);
  }
  ```
  Struct contains:
  * `cir_values`: the device buffer storing the CIR values; the size of the `cir_values` per TX is `num_rx * num_samples * num_rx_horz * num_rx_vert * num_rx_pol * num_tx_horz * num_tx_vert * num_tx_pol * num_taps`
  * `cir_delays`: the device buffer storing the CIR delay; the size of the `cir_delays` per TX is `num_rx * num_samples * num_rx_horz * num_rx_vert * num_tx_horz * num_tx_vert * num_taps`
  * `angles_of_departure`: the device buffer storing angle of departure values as `float2` `(azimuth, zenith)` tuples, in radians, with the same indexing and element count as `cir_delays`; the angles are defined in the TX panel's local coordinate system
  * `angles_of_arrival`: the device buffer storing angle of arrival values as `float2` `(azimuth, zenith)` tuples, in radians, with the same indexing and element count as `cir_delays`; the angles are defined in the RX panel's local coordinate system
  * `num_samples` is either 1 or 14, the number of realizations per slot
  * methods to get index, size, get, and set the CIR values, delays, angles of departure, and angles of arrival for a specific TX/RX antenna pair and tap

* **MultiTimeStepCIRResult**

  ```cpp
  struct MultiTimeStepCIRResult {
      d_complex *d_cir_values_buffer{};
      float *d_cir_delays_buffer{};
      float2 *d_angles_of_departure_buffer{};
      float2 *d_angles_of_arrival_buffer{};

      std::vector<std::vector<CIRResult>> cir_results;

      std::vector<size_t> values_time_step_offsets;
      std::vector<size_t> delays_time_step_offsets;
      std::vector<size_t> angles_of_departure_time_step_offsets;
      std::vector<size_t> angles_of_arrival_time_step_offsets;

      std::vector<std::vector<size_t>> values_ru_offsets;
      std::vector<std::vector<size_t>> delays_ru_offsets;
      std::vector<std::vector<size_t>> angles_of_departure_ru_offsets;
      std::vector<std::vector<size_t>> angles_of_arrival_ru_offsets;

      size_t total_values_bytes{};
      size_t total_delays_bytes{};
      size_t total_angles_of_departure_bytes{};
      size_t total_angles_of_arrival_bytes{};
      size_t total_values_elements{};
      size_t total_delays_elements{};
      size_t total_angles_of_departure_elements{};
      size_t total_angles_of_arrival_elements{};
      int num_time_steps{};
      bool use_async_alloc{true};

      std::vector<std::vector<uint32_t>> tx_indices_per_ts;
      std::vector<std::vector<std::vector<uint32_t>>> rx_indices_per_ts;

      int get_num_rus(int time_step) const;
      std::vector<CIRResult>& get(int time_step);
      const std::vector<CIRResult>& get(int time_step) const;
      CIRResult& get(int time_step, int ru_pos);
      const CIRResult& get(int time_step, int ru_pos) const;

      size_t get_values_offset(int time_step, int ru_pos) const;
      size_t get_delays_offset(int time_step, int ru_pos) const;
      size_t get_angles_of_departure_offset(int time_step, int ru_pos) const;
      size_t get_angles_of_arrival_offset(int time_step, int ru_pos) const;
  }
  ```

  A struct storing batched CIR results for multiple time steps.

  | Member                                                                                                                                         | Description                                                                                                                    |
  | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
  | d\_cir\_values\_buffer                                                                                                                         | a single contiguous device buffer storing CIR values for all time steps and RUs                                                |
  | d\_cir\_delays\_buffer                                                                                                                         | a single contiguous device buffer storing CIR delays for all time steps and RUs                                                |
  | d\_angles\_of\_departure\_buffer                                                                                                               | a single contiguous device buffer storing angle of departure values for all time steps and RUs                                 |
  | d\_angles\_of\_arrival\_buffer                                                                                                                 | a single contiguous device buffer storing angle of arrival values for all time steps and RUs                                   |
  | cir\_results                                                                                                                                   | per-time-step, per-RU `CIRResult` views into the contiguous buffers; indexed as `cir_results[time_step][ru_pos]`               |
  | values\_time\_step\_offsets, delays\_time\_step\_offsets, angles\_of\_departure\_time\_step\_offsets, angles\_of\_arrival\_time\_step\_offsets | cumulative element offsets for each time step from the corresponding buffer start                                              |
  | values\_ru\_offsets, delays\_ru\_offsets, angles\_of\_departure\_ru\_offsets, angles\_of\_arrival\_ru\_offsets                                 | per-RU element offsets within each time step, relative to that time step's start                                               |
  | total\_values\_bytes, total\_delays\_bytes, total\_angles\_of\_departure\_bytes, total\_angles\_of\_arrival\_bytes                             | total allocated byte counts for each contiguous buffer                                                                         |
  | total\_values\_elements, total\_delays\_elements, total\_angles\_of\_departure\_elements, total\_angles\_of\_arrival\_elements                 | total allocated element counts for each contiguous buffer                                                                      |
  | num\_time\_steps                                                                                                                               | number of time steps in the batch                                                                                              |
  | use\_async\_alloc                                                                                                                              | `true` when buffers were allocated with `cudaMallocAsync`; `false` when allocated with `cudaMalloc` for CUDA IPC compatibility |
  | tx\_indices\_per\_ts                                                                                                                           | RU indices used for each time step, indexed as `[num_time_steps][num_rus]`                                                     |
  | rx\_indices\_per\_ts                                                                                                                           | UE indices used for each time step and RU, indexed as `[num_time_steps][num_rus][num_ues]`                                     |

  All offsets are element offsets, not byte offsets. The `values` offsets index `d_complex` elements, the `delays` offsets index `float` elements, and the angle offsets index `float2` elements. The angle buffers have the same logical shape and element count as the delay buffer for the corresponding `CIRResult`.

  Each contiguous buffer is laid out by concatenating all time steps, then all RUs within each time step:

  ```text
  pointer_for(time_step, ru_pos) =
      buffer_base
      + time_step_offsets[time_step]
      + ru_offsets[time_step][ru_pos]
  ```

  Use `values_*` offsets with `d_cir_values_buffer`, `delays_*` offsets with `d_cir_delays_buffer`, `angles_of_departure_*` offsets with `d_angles_of_departure_buffer`, and `angles_of_arrival_*` offsets with `d_angles_of_arrival_buffer`.

  Within each `CIRResult` view, use the `CIRResult` index helpers for the per-RU tensor layout: `cir_value_index()` for `cir_values`, `cir_delay_index()` for `cir_delays`, and `angle_of_departure_index()` / `angle_of_arrival_index()` for the angle buffers. The angle index helpers use the same layout as `cir_delay_index()`. Each angle element is a `float2` tuple `(azimuth, zenith)` in radians. `angles_of_departure` is defined in the TX panel's local coordinate system, and `angles_of_arrival` is defined in the RX panel's local coordinate system.

* **Matrix4x4**
  ```cpp
  typedef struct Matrix4x4 {
      float m[4][4]{};

      [[nodiscard]] bool operator==(const Matrix4x4 &other) const;
  } Matrix4x4
  ```
  A $4\times4$ matrix of `float` elements.

* **EMMaterial**

  ```cpp
  struct EMMaterial {
      float4 abcd{};
      float roughness_rms{};
      float k_xpol{};
      float scattering_coeff{};
      int exponent_alpha_R{};
      int exponent_alpha_I{};
      float lambda_R{};
      float thickness_m{};

      [[nodiscard]] bool operator==(const EMMaterial &other) const;
  }
  ```

  A struct storing EM material parameters.

  | Member             | Description                                                                                                                          |
  | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
  | abcd               | a `float4` storing ITU-R P2040 a, b, c, and d parameters for calculating the relative permittivity \[^2]                             |
  | roughness\_rms     | the root mean square of the surface roughness (type `float`), in meters                                                              |
  | k\_xpol            | scattering cross-polarization/col-polarization power ratio (type `float`)                                                            |
  | scattering\_coeff  | scattering coefficient in the effective roughness (ER) model \[^3], \[^4] (type `float`)                                             |
  | exponent\_alpha\_R | integer exponent of the forward scattering lobe (in the specular reflection direction) in the Directional diffuse model (type `int`) |
  | exponent\_alpha\_I | integer exponent of the backward scattering lobe (in the incidence direction) in the Directional diffuse model (type `int`)          |
  | lambda\_R          | ratio between the forward scattering power and the total scattering power in the Directional diffuse model (type `float`)            |
  | thickness\_m       | thickness of the single-layer material (type `float`), in meters                                                                     |

The diffuse model can be either Lambertian or Directional, and there are two ways to tune the diffuse scattering pattern:

* using a fixed positive `scattering_coeff` (the ER model), which does not depend on the incidence angle of the impinging wave. This coefficient is used to calculate the reflection reduction factor and the fraction of power used for diffuse scattering. In this case, the `roughness_rms` parameter is ignored, as the sets of `(abcd, scattering_coeff, k_xpol)` and `(abcd, scattering_coeff, k_xpol, exponent_alpha_R, exponent_alpha_I, lambda_R, thickness_m)` are sufficient for Lambertian and Directional diffuse models, respectively.
* using `roughness_rms` to characterize the Rayleigh reflection reduction factor \[^5], \[^6] and the fraction of power for diffuse scattering. In this case, the `scattering_coeff` parameter is ignored, as the sets of `(abcd, roughness_rms, k_xpol)` and `(abcd, roughness_rms, k_xpol, exponent_alpha_R, exponent_alpha_I, lambda_R, thickness_m)` are sufficient for Lambertian and Directional diffuse models, respectively.

By default, for a material with a positive `scattering_coeff`, the first method is used. Otherwise, if `scattering_coeff = 0.0`, then the second method is used. For the Directional diffuse model, the formulation \[^4] is reciprocal. When `lambda_R = 1.0`, the Directional diffuse model becomes a single-lobe diffuse (SLD) model, and the forward lobe contains the total diffuse scattering power. Otherwise, the model uses two lobes.

For reflection and transmission coefficients, the single-layer slab model with a single thickness in ITU Recommendation P.2040-3 is used.

* **EMVegetationMaterial\_ITUR**

  ```cpp
  struct EMVegetationMaterial_ITUR {
      float A{};
      float B{};
      float C{};
      float E{};
      float G{};
      float scattering_power_factor{};
      float k_xpol{};

      [[nodiscard]] bool operator==(const EMVegetationMaterial_ITUR &other) const;
  }
  ```

  A struct storing EM material parameters for vegetation, where `A`, `B`, `C`, `E`, `G` are the empirical parameters (type `float`) used in modeling the attenuation loss in Equation (3) of ITU-R 388-10 Recommendation \[^7]:

  $$
  L(\text{dB}) = Af^{B}d^{C}(θ + E)^{G}
  $$

  where $f$ is the carrier frequency (in MHz), $d$ is the vegetation depth (in meters), and $\theta$ is the path's elevation angle (in degrees).

  Additionally, the following parameters are used to model diffuse scattering power from vegetation and polarization effects.

  | Member                    | Description                                                                                                                     |
  | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
  | scattering\_power\_factor | scattering power factor (type `float`), the fraction of the total power loss by vegetation that is diffused over all directions |
  | k\_xpol                   | scattering cross-polarization/co-polarization power ratio (type `float`)                                                        |

The current diffuse scattering model assumes isotropic scattering pattern, i.e., same power in all directions over the unit sphere. The following figure illustrates the diffusion (a) and transmission (b) interactions modeling for vegetation.

* **EM\_INTERACT\_TYPE**
  ```cpp
  enum EM_INTERACT_TYPE : unsigned int {
      Emission = 0,
      Reflection = 1,
      Diffraction = 2,
      Diffuse = 3,
      Reception = 4,
      Transmission = 5,
      Diffuse_Vegetation = 6,
      Transmission_Vegetation_In = 7,
      Transmission_Vegetation_Out = 8,
      Reserved,
  }
  ```
  An enumeration of EM interaction types per ray.

* **EM\_DIFFUSE\_TYPE**
  ```cpp
  enum EM_DIFFUSE_TYPE : unsigned int {
      Lambertian = 0,
      Directional = 1
  }
  ```
  An enumeration of EM diffuse type.

* **RayPath**

  ```cpp
  struct RayPath {
      int tx_id{};
      int rx_id{};

      int tx_ij[2]{};
      int rx_ij[2]{};

      int rx_index{};

      EM_INTERACT_TYPE point_types[MAX_NUM_INTERACTIONS_WITH_TRANSMISSION+2]{};
      float3 points[MAX_NUM_INTERACTIONS_WITH_TRANSMISSION+2]{};

      int object_ids[MAX_NUM_INTERACTIONS_WITH_TRANSMISSION + 2]{};
      int prim_ids[MAX_NUM_INTERACTIONS_WITH_TRANSMISSION+2]{};

      float3 normals[MAX_NUM_INTERACTIONS_WITH_TRANSMISSION+2]{};
      float vegetation_depths[MAX_NUM_INTERACTIONS_WITH_TRANSMISSION + 2]{};

      int num_points{};

      d_complex cir_ampl[4]{};
      float cir_delay{};

      __host__ __device__ RayPath() {}
      __host__ __device__ RayPath(int *tx_ij, int *rx_ij, float3 *points,
                            EM_INTERACT_TYPE *point_types, int *object_ids,
                            int *prim_ids, float3 *normals, int tx_id,
                            int rx_id, int rx_index, int num_points);

      [[nodiscard]] bool operator==(const RayPath &other) const;
  }
  ```

  A struct storing geometry and EM data of a propagation path.

  | Member             | Description                                                                                                                                                                                                                                                                                                       |
  | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | tx\_id             | ID of the RU (type `int`)                                                                                                                                                                                                                                                                                         |
  | rx\_id             | ID of the UE (type `int`)                                                                                                                                                                                                                                                                                         |
  | tx\_ij             | two-element array of indices (type `int`, `i` for horizontal index and `j` for vertical index) of the antenna element within the RU panel                                                                                                                                                                         |
  | rx\_ij             | two-element array of indices (type `int`, `i` for horizontal index and `j` for vertical index) of the antenna element within the UE panel                                                                                                                                                                         |
  | rx\_index          | index of the UE (type `int`)                                                                                                                                                                                                                                                                                      |
  | point\_types       | an array of `EM_INTERACT_TYPE` storing the EM interaction types for points along the path                                                                                                                                                                                                                         |
  | points             | an array of `float3` storing the (x, y, z) coordinates of interaction points, in the same unit as USD map\*                                                                                                                                                                                                       |
  | object\_ids        | an array of `int` storing the indices of the geometry object at the interaction points. The building object, if present, is indexed by 0, followed by the vehicular objects starting from 1. If the building is not present, the first vehicular object is indexed by 0 instead                                   |
  | prim\_ids          | an array of `int` storing the indices of the geometry primitive at the interaction points: the hit triangle index for reflection, diffusion, or transmission; the hit edge index for diffraction; and `-1` otherwise. The triangle index is local to the building mesh or the base (pre-transform) scatterer mesh |
  | normals            | an array of `float3` storing the normals at the interaction points                                                                                                                                                                                                                                                |
  | vegetation\_depths | an array of `float` storing the vegetation depth at the interaction points\*\*                                                                                                                                                                                                                                    |
  | num\_points        | number of interaction points from the RU to UE (type `int`)                                                                                                                                                                                                                                                       |
  | cir\_ampl          | an array of four complex-valued elements storing the path CIR amplitude for four UE-RU polarization combinations\*\*\*                                                                                                                                                                                            |
  | cir\_delay         | propagation delay of the path (type `float`), in seconds                                                                                                                                                                                                                                                          |

  \*The map unit is determined by `Meters Per Unit` in the USD file. For example, a map is in meters if `Meters Per Unit = 1` and in centimeters if `Meters Per Unit = 0.01`.

  \*\*For the transmission paths that go through each vegetation object, the `vegetation depth` is zero at `Transmission_Vegetation_In` point, and is equal to propagation path length between `Transmission_Vegetation_In` and `Transmission_Vegetation_Out` points at the `Transmission_Vegetation_Out` point. For the diffuse scattering paths from each vegetation object, the `vegetation depth` is stored at the `Diffuse_Vegetation` point, the first contact point of the ray from the TX on the vegetation object.

  \*\*\*`cir_ampl[i*2 + j]` is for the UE's $i$-th polarization and RU's $j$-th polarization, for $i \in \left[0, 1\right]$ and $j \in \left[0, 1\right]$.

* **ANTENNA\_TYPE**

  ```cpp
  enum ANTENNA_TYPE : unsigned int {
      Isotropic = 0,
      Infinitesimal_dipole = 1,
      Halfwave_dipole = 2,
      Rec_microstrip_patch = 3,
      ThreeGPP_38901 = 4,
      Polarized_Isotropic = 5
  }
  ```

  An enumeration for the built-in antenna types currently supported by the EM solver.

* **AntennaPattern**

  ```cpp
  struct AntennaPattern {
      int pattern_type{};
      std::vector<std::vector<d_complex>> ampls_theta{};
      std::vector<std::vector<d_complex>> ampls_phi{};

      [[nodiscard]] bool operator==(const AntennaPattern &other) const;
  }
  ```

  A struct storing a radiation pattern for a given antenna element, for either a built-in or a custom antenna type.  The current version only supports patterns at a single frequency.

  | Member        | Description                                                                                                                                                                                      |
  | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | pattern\_type | an integer indicating the radiation pattern type as defined in the `ANTENNA_TYPE` enum (type `int`)                                                                                              |
  | ampls\_theta  | a two-dimensional vector storing complex-valued amplitudes (type `d_complex`) of the antenna radiated field along the theta direction, each inner vector stores the amplitudes for one frequency |
  | ampls\_phi    | a two-dimensional vector storing complex-valued amplitudes (type `d_complex`) of the antenna radiated field along the phi direction, each inner vector stores the amplitudes for one frequency   |

  For built-in antenna types, the radiation fields are analytically calculated by the EM engine, and the `ampls_theta` and `ampls_phi` member variables are ignored.  For example, for an `Infinitesimal_dipole` pattern type, the `AntennaPattern` object is:

  ```cpp
  AntennaPattern pattern = {.pattern_type = 1, .ampls_theta = {}, .ampls_phi = {}};
  ```

* **AntennaPanel**

  ```cpp
  struct AntennaPanel {
      std::string panel_name{};
      std::vector<std::string> antenna_names{};
      std::vector<int> antenna_pattern_indices{};
      std::vector<float> frequencies{};
      std::vector<float> thetas{};
      std::vector<float> phis{};
      double reference_freq{};
      bool dual_polarized{};
      int num_loc_antenna_horz{};
      int num_loc_antenna_vert{};
      double antenna_spacing_horz{};
      double antenna_spacing_vert{};
      double antenna_roll_angle_first_polz{};
      double antenna_roll_angle_second_polz{};

      [[nodiscard]] bool operator==(const AntennaPanel &other) const;
  }
  ```

  A struct storing information for a given antenna panel. The number of antenna elements in a panel is `num_loc_antenna_horz*num_loc_antenna_vert*num_polarizations`.

  | Member                             | Description                                                                                                                   |
  | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
  | panel\_name                        | name of the panel (type `string`)                                                                                             |
  | antenna\_names                     | a vector of names (type `string`) of all antenna elements in the panel                                                        |
  | antenna\_pattern\_indices          | a vector of indices (type `int`) to the `patterns` vector for the antenna elements in the panel                               |
  | frequencies                        | a vector of frequencies (type `float`) for the antenna radiation patterns of the antenna elements in the panel, in Hertz      |
  | thetas                             | a vector storing elevation angles (type `float`) of the radiation pattern for all antenna elements in the panel, in radians\* |
  | phis                               | a vector storing azimuth angles (type `float`) of the radiation pattern for all antenna elements in the panel, in radians\*   |
  | reference\_freq                    | center frequency (type `double`) of the panel, in Hertz                                                                       |
  | dual\_polarized                    | a `bool` variable to indicate if the panel antennas are dual- (true) or single- polarized (false)                             |
  | num\_loc\_antenna\_horz            | number of antenna element locations (type `int`) in the planar array along a row                                              |
  | num\_loc\_antenna\_vert            | number of antenna element locations (type `int`) in the planar array along a column                                           |
  | antenna\_spacing\_horz             | horizontal antenna element spacing (type `double`), in centimeters                                                            |
  | antenna\_spacing\_vert             | vertical antenna element spacing (type `double`), in centimeters                                                              |
  | antenna\_roll\_angle\_first\_polz  | angular displacement of the antenna element realizing the first polarization (type `double`), in radians                      |
  | antenna\_roll\_angle\_second\_polz | angular displacement of the element realizing the second polarization (type `double`), in radians                             |

  \*For custom antenna patterns, the ranges of thetas and phis need to follow the standard conventions: $[0, \pi]$ for thetas and $[0, 2\pi]$ for phis.

* **AntennaInfo**

  ```cpp
  struct AntennaInfo {
      std::vector<AntennaPanel> panels{};
      std::vector<AntennaPattern> patterns{};

      [[nodiscard]] bool operator==(const AntennaInfo &other) const;
  }
  ```

  A struct encapsulating information of all antenna panels and antenna radiation patterns.

  | Member   | Description                                |
  | -------- | ------------------------------------------ |
  | panels   | vector of panels (type `AntennaPanel`)     |
  | patterns | vector of patterns (type `AntennaPattern`) |

* **TXInfo**

  ```cpp
  struct TXInfo {
      int tx_ID{};
      float3 tx_center{};
      Matrix4x4 Ttx{};

      std::vector<int> panel_id{};
      float height{};
      float mech_azimuth_deg{};
      float mech_tilt_deg{};

      float carrier_freq{};
      float carrier_bandwidth{};
      float subcarrier_spacing{};
      int fft_size{};
      float radiated_power{};
      std::vector<std::string> antenna_names{};
      std::vector<int> antenna_pattern_indices{};
      bool dual_polarized_antenna{};
      std::vector<float3> antenna_rotation_angles{};
      int num_loc_antenna_horz{};
      int num_loc_antenna_vert{};
      std::vector<float3> loc_antenna{};
      std::vector<std::pair<int, int>> ij_antenna{};

      float3 velocity_3d{};

      [[nodiscard]] bool operator==(const TXInfo &other) const;
  }
  ```

  A struct storing RU information.

  | Member                    | Description                                                                                                                                                                                                              |
  | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | tx\_ID                    | ID of the RU (type `int`)                                                                                                                                                                                                |
  | tx\_center                | (x , y, z) coordinates of the RU center (type `float3`), in the same unit as USD map                                                                                                                                     |
  | Ttx                       | a `Matrix4x4` transformation matrix for the RU combining translation and rotation, in the same unit as USD map                                                                                                           |
  | panel\_id                 | a vector of indices (type `int`) identifying the panels used by the RU; currently only size 1 is supported                                                                                                               |
  | height                    | height (type `float`) calculated from RU base to the RU center, in centimeters                                                                                                                                           |
  | mech\_azimuth\_deg        | mechanical azimuth (type `float`) of the RU, in degrees                                                                                                                                                                  |
  | mech\_tilt\_deg           | mechanical tilt (type `float`) of the RU, in degrees                                                                                                                                                                     |
  | carrier\_freq             | carrier frequency (type `float`) of the RU, in Hertz                                                                                                                                                                     |
  | subcarrier\_spacing       | sub-carrier spacing (type `float`), in Hertz                                                                                                                                                                             |
  | fft\_size                 | FFT size (type `int`) used for wideband CFR calculation                                                                                                                                                                  |
  | radiated\_power           | radiated power (type `float`) of the RU, in Watts                                                                                                                                                                        |
  | antenna\_names            | a vector of names (type `string`) of all antenna elements in the RU panel                                                                                                                                                |
  | antenna\_pattern\_indices | a vector of indices (type `int`) to the `patterns` vector for the antenna elements in the RU panel                                                                                                                       |
  | dual\_polarized\_antenna  | a `bool` variable to indicate if the RU antenna panel is composed by dual- (true) or single- polarized (false) elements                                                                                                  |
  | antenna\_rotation\_angles | a vector of triplets storing rotation angles (type `float3`) of the antennas: the first triplet is for the first polarization, and in case of dual-polarized antennas, the second triplet is for the second polarization |
  | num\_loc\_antenna\_horz   | number of antenna element locations (type `int`) in the horizontal direction within the RU antenna panel                                                                                                                 |
  | num\_loc\_antenna\_vert   | number of antenna element locations (type `int`) in the vertical direction within the RU antenna panel                                                                                                                   |
  | loc\_antenna              | vector of (x, y, z) of antenna positions within the RU antenna panel (type `float3`), in the same unit as USD map\*                                                                                                      |
  | ij\_antenna               | a vector of pairs of indices (type `int`) storing horizontal and vertical indices of the antenna elements in the RU antenna panel                                                                                        |
  | velocity\_3d              | three-dimensional velocity (in meter per second) of the RU (type `float3`)                                                                                                                                               |

  **Note:** The antenna locations and the `(i, j)` antenna indices in the `TXInfo` are order-consistent: e.g. the element at location indexed by `(i, j)` with polarization `p`, is `antenna_names[i*num_loc_antenna_vert*num_polarizations + j*num_polarizations + p - 1]`, where `num_polarizations` is 1 for single-polarized panels and 2 for dual-polarized panels. In addition, `(i, j) = (0, 0)`  indicates the bottom-left antenna location in the antenna array. These conventions are also used for `RXInfo` struct below. It is worth noticing that, in the graphical interface, the top and the bottom of the array are inverted, i.e., what is at the top in the graphical interface is considered at the bottom in EM solver.

* **RXInfo**

  ```cpp
  struct RXInfo {
      int rx_ID{};
      float3 rx_center{};
      Matrix4x4 Trx{};

      std::vector<int> panel_id{};
      float radiated_power{};
      std::vector<std::string> antenna_names{};
      std::vector<int> antenna_pattern_indices{};
      bool dual_polarized_antenna{};
      std::vector<float3> antenna_rotation_angles{};
      int num_loc_antenna_horz{};
      int num_loc_antenna_vert{};
      std::vector<float3> loc_antenna{};
      std::vector<std::pair<int, int>> ij_antenna{};

      float3 velocity_3d{};

      [[nodiscard]] bool operator==(const RXInfo &other) const;
  }
  ```

  A struct storing UE information.

  | Member                    | Description                                                                                                                                                                                                              |
  | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | rx\_ID                    | ID of the UE (type `int`)                                                                                                                                                                                                |
  | rx\_center                | (x , y, z) coordinates of the UE center (type `float3`), in the same unit as USD map                                                                                                                                     |
  | Trx                       | a `Matrix4x4` transformation matrix for the UE combining translation and rotation, in the same unit as USD map                                                                                                           |
  | panel\_id                 | a vector of indices (type `int`) identifying the panels used by the UE; currently only size 1 is supported                                                                                                               |
  | radiated\_power           | radiated power (type `float`) of the UE, in Watts                                                                                                                                                                        |
  | antenna\_names            | a vector of names (type `string`) of all antenna elements in the UE panel                                                                                                                                                |
  | antenna\_pattern\_indices | a vector of indices (type `int`) to the `patterns` vector for the antenna elements in the UE panel                                                                                                                       |
  | dual\_polarized\_antenna  | a `bool` variable to indicate if the UE antenna panel is composed by dual- (true) or single- polarized (false) elements                                                                                                  |
  | antenna\_rotation\_angles | a vector of triplets storing rotation angles (type `float3`) of the antennas: the first triplet is for the first polarization, and in case of dual-polarized antennas, the second triplet is for the second polarization |
  | num\_loc\_antenna\_horz   | number of antenna element locations (type `int`) in the horizontal direction within the UE antenna panel                                                                                                                 |
  | num\_loc\_antenna\_vert   | number of antenna element locations (type `int`) in the vertical direction within the UE antenna panel                                                                                                                   |
  | loc\_antenna              | vector of (x, y, z) of antenna positions within the RU antenna panel (type `float3`), in the same unit as USD map                                                                                                        |
  | ij\_antenna               | a vector of pairs of indices (type `int`) storing horizontal and vertical indices of the antenna elements in the UE antenna panel                                                                                        |
  | velocity\_3d              | three-dimensional velocity (in meter per second) of the UE (type `float3`)                                                                                                                                               |

* **Mesh**

  ```cpp
  struct Mesh {
      std::vector<float3> mesh_vertices{};
      std::vector<int> triangle_material_ids{};
      bool is_diffusion{};
      bool is_diffraction{};
      bool is_transmission{};
      float diffuse_dS_sqm = 1.0;

      [[nodiscard]] bool operator==(const Mesh &other) const;
  };
  ```

  A struct storing information of a triangular mesh in the scene.

  | Member                  | Description                                                                                         |
  | ----------------------- | --------------------------------------------------------------------------------------------------- |
  | mesh\_vertices          | a vector of vertices (type `float3`) of the triangular mesh\*, in the same unit as USD map          |
  | triangle\_material\_ids | a vector of material indices (type `int`) of the mesh's triangles                                   |
  | is\_diffusion           | flag (type `boolean`) to indicate if the mesh is enabled for diffuse (`True`) or not (`False`)      |
  | is\_diffraction         | flag (type `boolean`) to indicate if the mesh is enabled for diffraction (`True`) or not (`False`)  |
  | is\_transmission        | flag (type `boolean`) to indicate if the mesh is enabled for transmission (`True`) or not (`False`) |
  | diffuse\_dS\_sqm        | (type `float`) the area of the diffuse surface element in square meter                              |

  **Notes**: The vertices are grouped in tuples of 3 elements for the building triangles, e.g., `vertices {[0], [1], [2]}` for the first triangle, `vertices {[3], [4], [5]}` for the second triangles and so on. For each triangle, the vertex winding order is counter-clockwise so that its front face normal points outward for the building meshes and upward for the ground meshes.

* **BuildingEdge**

  ```cpp
  struct BuildingEdge {
      float3 p1{};
      float3 p2{};
      float3 e{};
      float3 e1{};
      float3 e2{};
      float3 n1{};
      float3 n2{};
      int triangle_id1{};
      int triangle_id2{};
      int material_id1{};
      int material_id2{};
      int diffuse_attr_1{};
      int diffuse_attr_2{};
  };
  ```

  A struct storing information about a building edge formed by two connected building faces (triangles).

  | Member           | Description                                                                          |
  | ---------------- | ------------------------------------------------------------------------------------ |
  | p1               | first endpoint of the edge (type `float3`)                                           |
  | p2               | second endpoint of the edge (type `float3`)                                          |
  | e                | unit vector pointing from `p1` to `p2` (type `float3`)                               |
  | e1               | unit vector tangent to the first face (type `float3`)                                |
  | e2               | unit vector tangent to the second face (type `float3`)                               |
  | n1               | unit vector normal to the first face (type `float3`)                                 |
  | n2               | unit vector normal to the second face (type `float3`)                                |
  | triangle\_id1    | index of the first triangle (type `int`)                                             |
  | triangle\_id2    | index of the second triangle (type `int`)                                            |
  | material\_id1    | material index of the first triangle (type `int`)                                    |
  | material\_id2    | material index of the second triangle (type `int`)                                   |
  | diffuse\_attr\_1 | `1` if the first face is enabled for diffuse scattering, `0` otherwise (type `int`)  |
  | diffuse\_attr\_2 | `1` if the second face is enabled for diffuse scattering, `0` otherwise (type `int`) |

* **SCATTERER\_TYPE**
  ```cpp
  enum SCATTERER_TYPE : int { Vehicle = 0 };
  ```
  An enumeration for the scatterer type.

* **ScattererInfo**

  ```cpp
  struct ScattererInfo {
  int id{};
  SCATTERER_TYPE type{};
  int mesh_ind{};
  Matrix4x4 transform{};
  float3 velocity_3d{};

  [[nodiscard]] bool operator==(const ScattererInfo &other) const;
  };
  ```

  A struct storing information of a scatterer in the scene.

  | Member          | Description                                                                                                                         |
  | --------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
  | id              | index of the scatterer (type `int`)                                                                                                 |
  | SCATTERER\_TYPE | type of the scatterer (type `SCATTERER_TYPE`)                                                                                       |
  | mesh\_ind       | index of the base (pre-transform) mesh in the array `geometry_info.scatterer_mesh` used for the scatterer (type `int`)              |
  | transform       | transformation matrix for the scatterer combining scaling, translation and rotation, in the same unit as USD map (type `Matrix4x4`) |
  | velocity\_3d    | three-dimensional velocity (in meter per second) of the scatterer (type `float3`)                                                   |

* **VEGETATION\_TYPE**
  ```cpp
  enum VEGETATION_TYPE : int {
      Street_Tree = 0,
      // Add more here
  };
  ```
  An enumeration for the vegetation type.

* **VegetationInfo**

  ```cpp
  struct VegetationInfo {
      int id{};
      VEGETATION_TYPE type{};
      int vegetation_material_id{};
      int mesh_ind{}; // index to the array geometry_info.vegetation_mesh
      Matrix4x4 transform{};

      [[nodiscard]] bool operator==(const VegetationInfo &other) const;
  };
  ```

  A struct storing information of a vegetation in the scene.

  | Member                   | Description                                                                                                                         |
  | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
  | id                       | index of the vegetation (type `int`)                                                                                                |
  | VEGETATION\_TYPE         | type of the vegetation (type `EM_VEGETATION_MODEL`)                                                                                 |
  | vegetation\_material\_id | material index of the vegetation (type `int`)                                                                                       |
  | mesh\_ind                | index of the base (pre-transform) mesh in the array `geometry_info.vegetation_mesh` used for the vegetation (type `int`)            |
  | transform                | transformation matrix for the scatterer combining scaling, translation and rotation, in the same unit as USD map (type `Matrix4x4`) |

* **GeometryInfo**

  ```cpp
  struct GeometryInfo {
      std::vector<Mesh> building_mesh{};
      std::vector<Mesh> terrain_mesh{};
      std::vector<Mesh> scatterer_mesh{};
      std::vector<ScattererInfo> scatterer_info{};
      std::vector<Mesh> vegetation_mesh{};
      std::vector<VegetationInfo> vegetation_info{};
      std::unordered_map<std::string, std::pair<int,EMMaterial>> material_dict{};
      std::unordered_map<std::string, std::pair<int, EMVegetationMaterial_ITUR>>
          vegetation_material_ITUR_dict{};
      float meters_per_unit = 0.01f;

      [[nodiscard]] bool operator==(const GeometryInfo &other) const;
  };
  ```

  A struct storing information for the geometries in the scene.

  | Member                           | Description                                                                                                                                                                                    |
  | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | building\_mesh                   | a vector of building meshes (type `Mesh`) in the scene                                                                                                                                         |
  | terrain\_mesh                    | a vector of terrain meshes (type `Mesh`) in the scene                                                                                                                                          |
  | scatterer\_mesh                  | a vector of scatterer meshes (type `Mesh`), each holds the base (pre-transform) mesh and related metadata for each type of scatterer in the scene                                              |
  | scatterer\_info                  | a vector of `ScattererInfo` structs storing the information of the scatterers in the scene                                                                                                     |
  | vegetation\_mesh                 | a vector of vegetation meshes (type `Mesh`), each holds the base (pre-transform) mesh and related metadata for each type of vegetation in the scene                                            |
  | vegetation\_info                 | a vector of `VegetationInfo` structs storing the information of the vegetation in the scene                                                                                                    |
  | material\_dict                   | an unordered map for the material dictionary storing all materials in the scene: key is the material name (type `string`) and value is a pair of `<int, EMMaterial>`                           |
  | vegetation\_material\_ITUR\_dict | an unordered map for the vegetation material dictionary storing all materials in the scene: key is the material name (type `string`) and value is a pair of `<int, EMVegetationMaterial_ITUR>` |
  | meters\_per\_unit                | a constant value of the amount of meters in the spatial units of the meshes (type `float`), default value is 0.01 `0.01`                                                                       |

* **RTConfig**

  ```cpp
  struct RTConfig {
      int num_rays_in_thousands{};
      int max_num_bounces{};
      float rx_sphere_radius_cm{};
      EM_DIFFUSE_TYPE em_diffuse_type{};
      bool use_only_first_antenna_pair{};
      bool write_cfr_results{};
      bool write_tau_mins{};
      bool simulate_ran{};
      int max_num_paths_per_ant_pair{};
      EM_VEGETATION_MODEL em_vegetation_model{};
      bool write_cir_results{};
      bool fast_mode{};
      bool copy_ray_paths_to_host{};

      [[nodiscard]] bool operator==(const RTConfig &other) const;
  }
  ```

  A struct storing the configuration of the raytracing parameters.

  | Member                          | Description                                                                                                                                                                                                                |
  | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | num\_rays\_in\_thousands        | number of emitted rays in thousands (type `int`)                                                                                                                                                                           |
  | max\_num\_bounces               | maximum number of scattering events for each emitted ray (type `int`)                                                                                                                                                      |
  | rx\_sphere\_radius\_cm          | reception sphere radius (type `float`), in centimeters                                                                                                                                                                     |
  | em\_diffuse\_type               | diffuse type to indicate the diffuse scattering model (type `EM_DIFFUSE_TYPE`): Lambertian = 0, Directional = 1                                                                                                            |
  | use\_only\_first\_antenna\_pair | a `bool` variable, when set to `true` only the results for the first RU-UE antenna pair are returned from `runEMSolver()`                                                                                                  |
  | write\_cfr\_results             | a `bool` variable, when set to `true`, `runEMSolver()` returns the channel frequency response results stored on device buffers `d_all_cfr_results`                                                                         |
  | write\_tau\_mins                | a `bool` variable, when set to `true`, `runEMSolver()` returns the minimum propagation delays stored on device buffers `d_all_tau_mins`                                                                                    |
  | simulate\_ran                   | a `bool` variable, when set to `true` the full RAN simulation is enabled                                                                                                                                                   |
  | max\_num\_paths\_per\_ant\_pair | the maximum number of strongest paths retained per RU-UE antenna pair (type `int`)                                                                                                                                         |
  | em\_vegetation\_model           | enumeration for the vegetation attenuation model (type `EM_VEGETATION_MODEL`). Currently only ITU\_R 388-10 model \[^7] is supported                                                                                       |
  | write\_cir\_results             | a `bool` variable, when set to `true`, `runEMSolver()` returns the CIR values, delays, angles of departure, and angles of arrival stored on device buffers in `CIRResult` structs                                          |
  | fast\_mode                      | a `bool` variable, when set to `true`, the EM solver executes in fast mode with two approximations: plane-wave approximation for MIMO antenna panels, and Doppler phase-shift approximation for 14-symbol slot simulations |
  | copy\_ray\_paths\_to\_host      | a `bool` variable, when set to `true`, all propagation results from all selected RUs to their associated UEs are stored in `all_ray_path_results` after the `runEMSolver()` call returns                                   |

### Compute mutual coupling patterns (temporarily not supported)

* **compute\_mutual\_coupling\_patterns()**
  ```cpp
  std::vector<emsolver::AntennaPattern> compute_mutual_coupling_patterns(const emsolver::AntennaPanel &panel);
  ```
  Compute antenna patterns including the effect of mutual coupling.

### Supported CUDA architectures

* **supported\_cuda\_architectures()**
  ```cpp
  std::vector<uint32_t> supported_cuda_architectures();
  ```
  Returns list of supported cuda architectures based on emsolver library compilation, e.g. \[80, 86, 89, 90].

### Class AerialEMSolver

* **AerialEMSolver()**

  ```cpp
  AerialEMSolver(const std::vector<TXInfo>& tx_info,
                 const std::vector<RXInfo>& rx_info,
                 const AntennaInfo& antenna_info,
                 const GeometryInfo& geometry_info,
                 const RTConfig& rt_cfg,
                 cudaStream_t ext_stream);
  ```

  Constructor for the AerialEMSolver object.

  | In/out | Parameter      | Description                                                                                       |
  | ------ | -------------- | ------------------------------------------------------------------------------------------------- |
  | \[in]  | tx\_info       | a vector of `TXInfo` structs storing the information of the RUs                                   |
  | \[in]  | rx\_info       | a vector of `RXInfo` structs storing the information of the UEs                                   |
  | \[in]  | antenna\_info  | `AntennaInfo` struct storing the information of all antenna panels and antenna radiation patterns |
  | \[in]  | geometry\_info | `GeometryInfo` struct storing the information of the scene geometry and materials                 |
  | \[in]  | rt\_cfg        | `RTConfig` struct storing the ray tracing configurations                                          |
  | \[in]  | ext\_stream    | CUDA stream index (type `cudaStream_t`)                                                           |

* **\~AerialEMSolver()**
  ```cpp
  ~AerialEMSolver()
  ```
  Destructor for the AerialEMSolver object.

* **allocateDeviceMemForResults()**

  ```cpp
  int32_t allocateDeviceMemForResults(const std::vector<TXInfo>& tx_info,
                                      const std::vector<RXInfo>& rx_info,
                                      const std::vector<uint32_t>& tx_indices,
                                      const std::vector<std::vector<uint32_t>>& rx_indices,
                                      const RTConfig& rt_cfg,
                                      const int symbols_per_slot,
                                      std::vector<d_complex*>& d_all_cfr_results,
                                      std::vector<float*>& d_all_tau_mins,
                                      std::vector<CIRResult> &d_all_cir_results);

  int32_t allocateDeviceMemForResults(const std::vector<TXInfo>& tx_info,
                                      const std::vector<RXInfo>& rx_info,
                                      const std::vector<std::vector<uint32_t>>& tx_indices_per_ts,
                                      const std::vector<std::vector<std::vector<uint32_t>>>& rx_indices_per_ts,
                                      const RTConfig& rt_cfg,
                                      const int symbols_per_slot,
                                      const int num_time_steps,
                                      std::vector<d_complex*>& d_all_cfr_results,
                                      std::vector<float*>& d_all_tau_mins,
                                      MultiTimeStepCIRResult& cir_result,
                                      bool use_async_alloc = true);

  int32_t allocateDeviceMemForResults(const std::vector<TXInfo>& tx_info,
                                      const std::vector<RXInfo>& rx_info,
                                      const std::vector<uint32_t>& tx_indices,
                                      const std::vector<std::vector<uint32_t>>& rx_indices,
                                      const RTConfig& rt_cfg,
                                      const int symbols_per_slot,
                                      const int num_time_steps,
                                      std::vector<d_complex*>& d_all_cfr_results,
                                      std::vector<float*>& d_all_tau_mins,
                                      MultiTimeStepCIRResult& cir_result,
                                      bool use_async_alloc = true);
  ```

  Allocation of device (GPU) memory to store the results of the EM engine.

  The first overload is the legacy single-time-step API and returns a vector of per-RU `CIRResult` views. The second overload accepts a per-time-step RU/UE configuration and allocates a `MultiTimeStepCIRResult`. The third overload broadcasts the same RU/UE configuration to `num_time_steps` time steps and delegates to the per-time-step overload.

  Multi-time-step allocation is supported for CIR results. CFR allocation is supported only for a single time step; when `num_time_steps > 1`, `write_cfr_results` must be `false`.

  | In/out | Parameter            | Description                                                                                                                                                                                                                                                                                          |
  | ------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | \[in]  | tx\_info             | a vector of `TXInfo` structs storing the information of all the RUs                                                                                                                                                                                                                                  |
  | \[in]  | rx\_info             | a vector of `RXInfo` structs storing the information of all the UEs                                                                                                                                                                                                                                  |
  | \[in]  | tx\_indices\_per\_ts | a vector of vectors of RU indices (type `uint32_t`) for each time step, indexed as `[num_time_steps][num_rus]`                                                                                                                                                                                       |
  | \[in]  | rx\_indices\_per\_ts | a vector of vectors of selected UE indices (type `uint32_t`) for each time step and RU, indexed as `[num_time_steps][num_rus][num_ues]`                                                                                                                                                              |
  | \[in]  | tx\_indices          | a vector of indices (type `uint32_t`) for the RUs whose results need to be computed; used by the broadcast and legacy overloads                                                                                                                                                                      |
  | \[in]  | rx\_indices          | a vector of vectors of indices (type `uint32_t`) of selected UEs for each RU; used by the broadcast and legacy overloads                                                                                                                                                                             |
  | \[in]  | rt\_cfg              | `RTConfig` struct storing the raytracing configuration                                                                                                                                                                                                                                               |
  | \[in]  | symbols\_per\_slot   | number of symbols (type `int`) in one slot (either 1 or 14)                                                                                                                                                                                                                                          |
  | \[in]  | num\_time\_steps     | number of time steps to allocate for the multi-time-step overloads                                                                                                                                                                                                                                   |
  | \[out] | d\_all\_cfr\_results | a vector of device pointers (type `d_complex`), each pointing to memory address holding the CFRs for the UEs associated to a given RU. The content of the vector follows the content of `tx_indices`. The allocation is realized when `write_cfr_results` in the `rt_config` is `true`               |
  | \[out] | d\_all\_tau\_mins    | a vector of device pointers (type `float`), each pointing to memory address holding the minimum propagation delay for the UEs associated to a given RU. The content of the vector follows the content of `tx_indices`. The allocation is realized when `write_tau_mins` in the `rt_config` is `true` |
  | \[out] | cir\_result          | `MultiTimeStepCIRResult` storing the contiguous CIR values, delays, angles of departure, angles of arrival, and per-time-step/per-RU views into those buffers. The allocation is realized only when `write_cir_results` in the `rt_config` is `true`                                                 |
  | \[out] | d\_all\_cir\_results | a vector of `CIRResult` structs storing the CIR results. The content of the vector follows the content of `tx_indices`. The allocation is realized only when `write_cir_results` in the `rt_config` is `true`                                                                                        |
  | \[in]  | use\_async\_alloc    | when `true`, allocate CIR buffers with `cudaMallocAsync`; when `false`, allocate them with `cudaMalloc` for CUDA IPC compatibility                                                                                                                                                                   |

  All CFR results for the $i$-th RU, i.e., `d_all_cfr_results_i = d_all_cfr_results[i]`, are stored in the device memory as a flattened representation of multidimensional array whose indices, in order, are `<ue_idx>, <symbol_idx>, <freq_idx>, <ue_ant_idx>, <ue_ant_pol_idx>, <ru_ant_idx>, <ru_ant_pol_idx>`.

  For example, the first 6 elements of `d_all_cfr_results_i`, with the $i$-the RU being equipped with dual-polarized antennas and all associated UEs also having dual-polarized antennas, are:

  ![CFRs\_mem\_arrangement](https://files.buildwithfern.com/aerial.docs.buildwithfern.com/aerial/aodt/7cf78974d1f6727c72eb00cca7dc423d88e7d51da99b9b471a38d3cc49d04a9e/docs/assets/em_physics/CFRs_mem_arrangement.png)

  For the minimum delay results, there is one minimum delay for all RU-UE antenna pairs (i.e., one value for one RU-UE pair, and `d_all_tau_mins[i]` holds the minimum delays from $i$-th RU to its associated UEs).

  Similar device memory arrangements are done for `cir_values`, `cir_delays`, `angles_of_departure`, and `angles_of_arrival` in each `CIRResult`. In the multi-time-step API, these `CIRResult` objects are stored in `cir_result.cir_results[time_step][ru_pos]` and point into the contiguous buffers owned by `MultiTimeStepCIRResult`.

* **runEMSolver()**

  ```cpp
  int32_t runEMSolver(const unsigned int time_idx,
                      const std::vector<TXInfo>& tx_info,
                      const std::vector<RXInfo>& rx_info,
                      const AntennaInfo& antenna_info,
                      const GeometryInfo &geometry_info,
                      const std::vector<uint32_t>& tx_indices,
                      std::vector<std::vector<uint32_t>>& rx_indices,
                      const RTConfig& rt_cfg,
                      const int symbol_idx,
                      const int symbols_per_slot,
                      std::vector<RayPath>& all_ray_path_results,
                      std::vector<d_complex*>& d_all_cfr_results,
                      std::vector<float*>& d_all_tau_mins,
                      std::vector<CIRResult> &d_all_cir_results);
  ```

  Launch the EM engine.

  | In/out | Parameter               | Description                                                                                                                                                                                                                                                                                       |
  | ------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | \[in]  | time\_idx               | time index (type `unsigned int`) in the simulation                                                                                                                                                                                                                                                |
  | \[in]  | tx\_info                | a vector of `TXInfo` structs storing the information of all the RUs                                                                                                                                                                                                                               |
  | \[in]  | rx\_info                | a vector of `RXInfo` structs storing the information of all the UEs                                                                                                                                                                                                                               |
  | \[in]  | antenna\_info           | `AntennaInfo` struct storing the information of all antenna panels and antenna radiation patterns                                                                                                                                                                                                 |
  | \[in]  | geometry\_info          | `GeometryInfo` struct storing the information of the scene geometry and materials                                                                                                                                                                                                                 |
  | \[in]  | tx\_indices             | a vector of indices (type `uint32_t`) for the RUs whose results need to be computed                                                                                                                                                                                                               |
  | \[in]  | rx\_indices             | a vector of vectors of indices (type `uint32_t`) of selected UEs for each RUs whose results need to be computed                                                                                                                                                                                   |
  | \[in]  | rt\_cfg                 | `RTConfig` struct storing the ray tracing configurations                                                                                                                                                                                                                                          |
  | \[in]  | symbol\_idx             | symbol index (type `int`) within a slot                                                                                                                                                                                                                                                           |
  | \[in]  | symbols\_per\_slot      | number of symbols (type `int`) in one slot (either 1 or 14)                                                                                                                                                                                                                                       |
  | \[in]  | all\_ray\_path\_results | a vector of `RayPath` structs storing all propagation results from all selected RUs to their associated UEs                                                                                                                                                                                       |
  | \[out] | d\_all\_cfr\_results    | a vector of device pointers (type `d_complex`), each pointing to memory address holding the CFRs for the UEs associated to a given RU. The content of the vector follows the content of `tx_indices`. The storing is realized when `write_cfr_results` in the `rt_config` is `true`               |
  | \[out] | d\_all\_tau\_mins       | a vector of device pointers (type `float`), each pointing to memory address holding the minimum propagation delay for the UEs associated to a given RU. The content of the vector follows the content of `tx_indices`. The storing is realized when `write_tau_mins` in the `rt_config` is `true` |
  | \[out] | d\_all\_cir\_results    | a vector of `CIRResult` structs storing the CIR results. The content of the vector follows the content of `tx_indices`. The storing is realized only when `write_cir_results` in the `rt_config` is `true`                                                                                        |

* **copyResultsFromDeviceToHost()**

  ```cpp
  int32_t copyResultsFromDeviceToHost(const std::vector<uint32_t>& tx_indices,
                                      const std::vector<std::vector<uint32_t>>& rx_indices,
                                      const RTConfig& rt_cfg,
                                      const int symbols_per_slot,
                                      const std::vector<d_complex*>& d_all_cfr_results,
                                      std::vector<std::vector<d_complex>>* all_cfr_results);
  ```

  Copy the results of the EM engine from device to host.

  | In/out | Parameter            | Description                                                                                                                                                                                          |
  | ------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | \[in]  | tx\_indices          | a vector of indices (type `uint32_t`) for the RUs whose results need to be computed                                                                                                                  |
  | \[in]  | rx\_indices          | a vector of vectors of indices (type `uint32_t`) of selected UEs for each RUs whose results need to be computed                                                                                      |
  | \[in]  | rt\_cfg              | `RTConfig` struct storing the ray tracing configurations                                                                                                                                             |
  | \[in]  | symbols\_per\_slot   | number of symbols (type `int`) in one slot (either 1 or 14)                                                                                                                                          |
  | \[in]  | d\_all\_cfr\_results | a vector of device pointers (type `d_complex`), each pointing to memory address holding the CFRs for the UEs associated to a given RU. The content of the vector follows the content of `tx_indices` |
  | \[out] | all\_cfr\_results    | a pointer to a host-side vector of vectors for the CFR results, with the inner vector holding the CFRs from one RU to its associated UEs and the outer vector following `tx_indices`                 |

* **deAllocateDeviceMemForResults()**

  ```cpp
  int32_t deAllocateDeviceMemForResults(const RTConfig& rt_cfg,
                                        std::vector<d_complex*>& d_all_cfr_results,
                                        std::vector<float*>& d_all_tau_mins,
                                        std::vector<CIRResult>& d_all_cir_results);

  int32_t deAllocateDeviceMemForResults(const RTConfig& rt_cfg,
                                        std::vector<d_complex*>& d_all_cfr_results,
                                        std::vector<float*>& d_all_tau_mins,
                                        MultiTimeStepCIRResult& cir_result);
  ```

  Deallocate device memory previously used for the EM engine results.

  The first overload is the legacy single-time-step API. The second overload deallocates the contiguous CIR buffers owned by `MultiTimeStepCIRResult`.

  | In/out | Parameter            | Description                                                                                                                                                                                                                            |
  | ------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | \[in]  | rt\_cfg              | `RTConfig` struct storing the ray tracing configurations                                                                                                                                                                               |
  | \[in]  | d\_all\_cfr\_results | a vector of device pointers (type `d_complex`): each of them pointing to a device memory that holds complex-valued amplitude of the CFRs from one RU to its associated UEs. The size of the vector is equal to size of the tx\_indices |
  | \[in]  | d\_all\_tau\_mins    | a vector of device pointers (type `float`), each pointing to memory address holding the minimum propagation delay for the UEs associated to a given RU. The content of the vector follows the content of `tx_indices`                  |
  | \[in]  | cir\_result          | `MultiTimeStepCIRResult` whose contiguous CIR values, delays, angles of departure, and angles of arrival buffers are deallocated                                                                                                       |
  | \[in]  | d\_all\_cir\_results | a vector of `CIRResult` structs storing the CIR results. The content of the vector follows the content of `tx_indices`.                                                                                                                |

* **getBuildingEdges()**

  ```cpp
  const std::vector<BuildingEdge>& getBuildingEdges() const noexcept;
  ```

  Return all building edges of the scene.

### Error handling

The EM engine has built-in error handling. The function where the error or invalid condition occurs is recorded and error messages are propagated to both the local console and the **Console tab** in the graphical interface.

* **EMLogLevel**

  ```cpp
  enum class EMLogLevel {ERROR=0, NOTIFY=1, WARNING=2, INFO=3, DEBUG=4, VERBOSE=5}
  ```

  An enumeration for the level of logging.

* **EMLogCallback**
  ```cpp
  EMLogCallback = std::function<void(EMLogLevel, const std::string&)>;
  ```
  Callback function prototype.

* **registerLogCallback()**

  ```cpp
  int32_t registerLogCallback(EMLogCallback func);
  ```

  Function to register a callback function (type `EMLogCallback`) for error handling.

* **deregisterLogCallback()**

  ```cpp
  int32_t deregisterLogCallback();
  ```

  Function to deregister the currently registered callback function.