artist.raytracing.blocking ========================== .. py:module:: artist.raytracing.blocking Attributes ---------- .. autoapisummary:: artist.raytracing.blocking.log Functions --------- .. autoapisummary:: artist.raytracing.blocking.create_blocking_primitives_rectangle artist.raytracing.blocking.create_blocking_primitives_rectangles_by_index artist.raytracing.blocking.soft_ray_blocking_mask artist.raytracing.blocking.expand_bits artist.raytracing.blocking.morton_codes artist.raytracing.blocking.longest_common_prefix artist.raytracing.blocking.build_linear_bounding_volume_hierarchies artist.raytracing.blocking.ray_aabb_intersect artist.raytracing.blocking.compute_lbvh_max_depth artist.raytracing.blocking.lbvh_filter_blocking_planes Module Contents --------------- .. py:data:: log A logger for blocking. .. py:function:: create_blocking_primitives_rectangle(blocking_heliostats_surface_points: torch.Tensor, blocking_heliostats_active_surface_points: torch.Tensor, device: torch.device | None = None) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor] Construct a rectangular blocking plane representation for heliostats by interpolating their corner points. Instead of keeping many surface samples, each heliostat is reduced to its blocking plane via: - its four corner points - two spanning vectors (rectangle axes) - the plane normal The corner points are indexed clockwise. The lower left corner point of a heliostat is indexed by 0, and so on. Overview of corner points and their indices:: 1 | 2 ----- 0 | 3 Assumptions: - The heliostat is rectangular. - The heliostat is oriented to the south if it is not aligned. Parameters ---------- blocking_heliostats_surface_points : torch.Tensor The unaligned surface points of all heliostats that might block other heliostats. Shape is ``[number_of_heliostats, number_of_combined_surface_points_all_facets, 4]``. blocking_heliostats_active_surface_points : torch.Tensor The aligned surface points of all heliostats that might block other heliostats. Shape is ``[number_of_heliostats, number_of_combined_surface_points_all_facets, 4]``. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor The blocking plane corners. Shape is ``[number_of_heliostats, 4, 4]``. torch.Tensor The blocking plane spans in u and v direction. Shape is ``[number_of_heliostats, 2, 4]``. torch.Tensor The blocking plane normals. Shape is ``[number_of_heliostats, 4]``. .. py:function:: create_blocking_primitives_rectangles_by_index(blocking_heliostats_active_surface_points: torch.Tensor, device: torch.device | None = None) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor] Construct a rectangular blocking plane representation for heliostats by the known indices of their corner points. The blocking plane for rectangular heliostats is represented by its four corner points, and its normal vector. The corner points are indexed clockwise. The lower left corner point of a heliostat is indexed by 0, and so on. Overview of corner points and their indices:: 1 | 2 ----- 0 | 3 Assumptions: - The heliostat is rectangular, each facet is also rectangular. - There are four facets ordered in two columns and two rows. - Each facet has the same number of surface points -> ``number_of_surface_points / 4`` - Each facet has the same number of points along its width and height -> ``math.sqrt(number_of_surface_points / 4)`` - Surface points are arranged in a structured, grid-like order and indexed in row-major fashion, analogous to a 2D tensor, ensuring consistent traversal. Parameters ---------- blocking_heliostats_active_surface_points : torch.Tensor The aligned surface points of all heliostats that might block other heliostats. Shape is ``[number_of_heliostats, number_of_combined_surface_points_all_facets, 4]``. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor The blocking plane corners. Shape is ``[number_of_heliostats, 4, 4]``. torch.Tensor The blocking plane spans in u and v direction. Shape is ``[number_of_heliostats, 2, 4]``. torch.Tensor The blocking plane normals. Shape is ``[number_of_heliostats, 4]``. .. py:function:: soft_ray_blocking_mask(ray_origins: torch.Tensor, ray_directions: torch.Tensor, blocking_primitives_corners: torch.Tensor, blocking_primitives_spans: torch.Tensor, blocking_primitives_normals: torch.Tensor, epsilon: float = 1e-12, softness: float = 1000.0, alpha: float = 100.0, ray_origin_offset: float = 0.05) -> torch.Tensor Compute a mask indicating which rays are blocked, using a soft differentiable approach. Calculate ray-plane intersections and the distances of the intersection from the ray origin. Depending on the intersections and the distances, rays are blocked if they cannot reach the target. The blocking is made differentiable by using sigmoid functions to approximate binary transitions with soft boundaries. For each ray and each blocking plane, the intersection point and distance is computed by solving the plane equation: .. math:: (\mathbf{p} - \mathbf{p_0}) \cdot \mathbf{n} = 0 \mathbf{p} = \mathbf{l_0} + \mathbf{l} d ((\mathbf{l_0} + \mathbf{l} d) - \mathbf{p_0}) \cdot \mathbf{n} = 0 d = \frac{(\mathbf{p_0}-\mathbf{l_0})\cdot \mathbf{n}}{\mathbf{l}\cdot \mathbf{n}} \mathbf{p_intersection} = \mathbf{l_0} + \mathbf{l}d where :math:`\mathbf{p}` are the points on the plane (`ray_origins`), :math:`\mathbf{p_0}` is a single point on the plane (`corner_0`), :math:`\mathbf{n}` is the normal vector of the plane (`blocking_planes_normals`), :math:`\mathbf{l}` is the unit vector describing the direction of the line (`ray_directions`), :math:`\mathbf{l_0}` is a point on the line (`ray_origins`), and :math:`d` is the distance from the ray origin to the point of intersection. In the final output of this method, values near 0 mean no blocking and values near 1 mean full blocking (there is at least one blocking primitive in front of the heliostat). Parameters ---------- ray_origins : torch.Tensor The origin points of the rays, i.e., the surface points. Shape is ``[number_of_heliostats, number_of_combined_surface_points_all_facets, 4]``. ray_directions : torch.Tensor The ray directions. Shape is ``[number_of_heliostats, number_of_rays, number_of_combined_surface_normals_all_facets, 4]``. blocking_primitives_corners : torch.Tensor The blocking primitives corner points. Shape is ``[number_of_blocking_primitives, 4, 4]``. blocking_primitives_spans: torch.Tensor The blocking primitives spans in u and v direction. Shape is ``[number_of_blocking_primitives, 2, 4]``. blocking_primitives_normals : torch.Tensor The blocking primitives normals. Shape is ``[number_of_blocking_primitives, 4]``. epsilon : float A small value used to avoid division by zero in plane-ray intersection (default is 1e-12). softness : float Controls how sharply the sigmoid approximates the hard blocking boundary (default is 1000.0). Higher values produce a steeper, more binary transition. alpha : float Optical depth scale factor for Beer–Lambert accumulation across blocking primitives (default is 100.0). ray_origin_offset : float Shift the ray origins a slight distance away from the heliostat planes to avoid self intersections (default is 0.05). The distance is measured in meters, so the default offset pushes the ray origins 5 cm along the ray direction. Returns ------- torch.Tensor A soft blocking mask, where values near 0 indicate no blocking and values near 1 indicate full blocking. Shape is ``[number_of_blocking_primitives, number_of_rays, number_of_combined_surface_points_all_facets]``. .. py:function:: expand_bits(integers: torch.Tensor) -> torch.Tensor Expand the lower ten bits of an integer into 30 bits by inserting two zero bits between each original bit. This method is safe and conceptualized for scenarios with up to 2^30 blocking planes represented by 30-bit Morton codes using torch.int32. Parameters ---------- integers : torch.Tensor Integer coordinates with values in ``[0, 1023]`` (10 bits). Shape is ``[number_of_blocking_planes]``. Returns ------- torch.Tensor Integer coordinates expanded from 10 bits to 30 bits. Shape is ``[number_of_blocking_planes]``. .. py:function:: morton_codes(coordinates: torch.Tensor, epsilon: float = 1e-06) -> torch.Tensor Map 3D points to a single integer value corresponding to its Morton Code. Spatially nearby points have similar Morton codes. Morton codes are also sometimes referred to as Z-order curve codes. They are computed by bit-interleaving the binary representations of the 3D x, y, z coordinates. The bits are interleaved such that y bits are given the highest priority, then x, then z. This is derived from heliostat field layouts, where heliostats commonly share a similar up component and blocking is determined by the east and north component. This method is safe and conceptualized for scenarios with up to 2^30 blocking planes represented by 30-bit Morton codes using torch.int32. Reference: Morton, G.M. (1966) A Computer Oriented Geodetic Data Base and a New Technique in File Sequencing. IBM Ltd., Ottawa. Parameters ---------- coordinates : torch.Tensor The coordinates to transform into Morton codes. Shape is ``[number_of_blocking_planes, 3]``. epsilon : float A small epsilon value to avoid division by zero (default is 1e-6). Returns ------- torch.Tensor The converted integers in Morton code. Shape is ``[number_of_blocking_planes]``. .. py:function:: longest_common_prefix(codes: torch.Tensor, i: torch.Tensor, j: torch.Tensor, device: torch.device | None = None) -> torch.Tensor Compute the longest common prefix (LCP) between pairs of Morton codes. The longest common prefix (LCP) indicates how similar two Morton codes are and therefore also indicates how close (spatially) two blocking objects are. The LCP is the number of highest-order bits that are identical in two Morton codes. This method is safe and conceptualized for scenarios with up to 2^30 blocking planes represented by 30-bit Morton codes using torch.int32. Parameters ---------- codes : torch.Tensor Sorted Morton codes as int64. Shape is ``[number_of_blocking_planes]``. i : torch.Tensor Lower indices selecting the first Morton codes for the comparison. Shape is ``[number_of_blocking_planes]``. j : torch.Tensor Upper indices selecting the second Morton codes for the comparison. Shape is ``[number_of_blocking_planes]``. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor The longest common prefixes in the range from 0 to ``total_bits``. Shape is ``[number_of_blocking_planes]``. .. py:function:: build_linear_bounding_volume_hierarchies(blocking_primitives_corners: torch.Tensor, device: torch.device | None = None) -> dict[str, torch.Tensor] Build linear bounding volume hierarchies (LBVHs). This method is safe and conceptualized for scenarios with up to 2^30 blocking planes represented by 30-bit Morton codes using ``torch.int32``. Reference: Tero Karras. Maximizing Parallelism in the Construction of BVHs, Octrees, and k‑d Trees. In Proceedings of the Fourth ACM SIGGRAPH / Eurographics Symposium on High‑Performance Graphics (HPG 2012) Parameters ---------- blocking_primitives_corners : torch.Tensor Corner points of each blocking primitive. Shape is ``[number_of_blocking_primitives, 4, 4]``. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- dict[str, torch.Tensor] - ``left``, ``right``: Indices of the left and right child of each LBVH node (-1 if leaf). - ``aabb_min``, ``aabb_max``: Axis-aligned bounding boxes. - ``is_leaf``: Boolean, indicating whether a node is a leaf node. - ``primitive_index``: Indicates which primitives are contained. .. py:function:: ray_aabb_intersect(ray_origins: torch.Tensor, inverse_ray_directions: torch.Tensor, aabb_min: torch.Tensor, aabb_max: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor] Compute intersection distances between rays and axis-aligned bounding boxes (AABBs). This method uses the slab method and the inverse ray direction for more efficient computation. Parameters ---------- ray_origins : torch.Tensor Ray origins. Shape is ``[total_number_of_rays, 3]``. inverse_ray_directions : torch.Tensor Precomputed inverse ray directions. Shape is ``[total_number_of_rays, 3]``. aabb_min : torch.Tensor Minimum corner points of the AABBs. Shape is ``[total_number_of_rays, 3]``. aabb_max : torch.Tensor Maximum corner points of the AABBs. Shape is ``[total_number_of_rays, 3]``. Returns ------- entry_distance_to_aabb : torch.Tensor Entry distance along each ray to the AABBs. Shape is ``[total_number_of_rays]``. exit_distance_to_aabb : torch.Tensor Exit distance along each ray to the AABBs. Shape is ``[total_number_of_rays]``. .. py:function:: compute_lbvh_max_depth(left: torch.Tensor, right: torch.Tensor) -> int Compute the maximum depth of the LBVH tree. Parameters ---------- left : torch.Tensor Left child indices. right : torch.Tensor Right child indices. Returns ------- int Maximum depth of the tree. .. py:function:: lbvh_filter_blocking_planes(points_at_ray_origins: torch.Tensor, ray_directions: torch.Tensor, blocking_primitives_corners: torch.Tensor, ray_to_heliostat_mapping: torch.Tensor, intersection_distances_target: torch.Tensor, device: torch.device | None = None) -> torch.Tensor Apply the LBVH filter to filter out blocking planes that are not hit. Parameters ---------- points_at_ray_origins : torch.Tensor Origin points of the rays, i.e., the surface points, expanded in the ray dimension. Shape is ``[number_of_heliostats, number_of_rays, number_of_combined_surface_normals_all_facets, 4]``. ray_directions : torch.Tensor Ray directions. Shape is ``[number_of_heliostats, number_of_rays, number_of_combined_surface_normals_all_facets, 4]``. blocking_primitives_corners : torch.Tensor Blocking primitives corner points. Shape is ``[number_of_blocking_planes, 4, 4]``. ray_to_heliostat_mapping : torch.Tensor Mapping indicating which ray is reflected by which heliostat. Shape is ``[total_number_of_rays]``. intersection_distances_target : torch.Tensor Distances from ray origins to the target. Shape is ``[number_of_heliostats, number_of_rays, number_of_combined_surface_normals_all_facets]``. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor Indices of the blocking primitives that are hit. Shape is ``[number_of_hit_blocking_planes]``.