Loading...
Searching...
No Matches
geometry_generator.cuh
1/*
2 * Copyright (c) 2023, NVIDIA CORPORATION.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <cuspatial_test/random.cuh>
20#include <cuspatial_test/vector_factories.cuh>
21
22#include <cuspatial/cuda_utils.hpp>
23#include <cuspatial/error.hpp>
26
27#include <rmm/cuda_stream_view.hpp>
28#include <rmm/device_uvector.hpp>
29#include <rmm/exec_policy.hpp>
30
31#include <ranger/ranger.hpp>
32
33#include <thrust/sequence.h>
34#include <thrust/tabulate.h>
35
36namespace cuspatial {
37namespace test {
38
39namespace detail {
40
41template <typename T, typename index_t>
43 vec_2d<T> __device__ operator()(index_t i)
44 {
45 return vec_2d<T>{cos(static_cast<T>(i)), sin(static_cast<T>(i))};
46 }
47};
48
49template <typename T>
51 T segment_length;
52
53 vec_2d<T> __device__ operator()(vec_2d<T> prev, vec_2d<T> rad)
54 {
55 return prev + segment_length * rad;
56 }
57};
58
59} // namespace detail
60
66template <typename T>
68 using element_t = T;
69
70 std::size_t num_multipolygons;
71 std::size_t num_polygons_per_multipolygon;
72 std::size_t num_holes_per_polygon;
73 std::size_t num_edges_per_ring;
74 vec_2d<T> centroid;
75 T radius;
76
77 CUSPATIAL_HOST_DEVICE std::size_t num_polygons()
78 {
79 return num_multipolygons * num_polygons_per_multipolygon;
80 }
81 CUSPATIAL_HOST_DEVICE std::size_t num_rings() { return num_polygons() * num_rings_per_polygon(); }
82 CUSPATIAL_HOST_DEVICE std::size_t num_coords() { return num_rings() * num_vertices_per_ring(); }
83 CUSPATIAL_HOST_DEVICE std::size_t num_vertices_per_ring() { return num_edges_per_ring + 1; }
84 CUSPATIAL_HOST_DEVICE std::size_t num_rings_per_polygon() { return num_holes_per_polygon + 1; }
85 CUSPATIAL_HOST_DEVICE T hole_radius() { return radius / (num_holes_per_polygon + 1); }
86};
87
101template <typename T>
102vec_2d<T> __device__ generate_ring_coordinate(std::size_t point_local_idx,
103 std::size_t num_edges,
104 vec_2d<T> centroid,
105 T radius)
106{
107 // Overrides last coordinate to make sure ring is closed.
108 if (point_local_idx == num_edges) return vec_2d<T>{centroid.x + radius, centroid.y};
109
110 T angle = (2.0 * M_PI * point_local_idx) / num_edges;
111
112 return vec_2d<T>{centroid.x + radius * cos(angle), centroid.y + radius * sin(angle)};
113}
114
127template <typename T>
128vec_2d<T> __device__ polygon_centroid_displacement(vec_2d<T> centroid,
129 std::size_t part_local_idx,
130 T radius)
131{
132 return centroid + vec_2d<T>{part_local_idx * radius * T{3.0}, T{0.0}};
133}
134
168template <typename T>
169vec_2d<T> __device__
170ring_centroid_displacement(vec_2d<T> centroid, std::size_t ring_local_idx, T radius, T hole_radius)
171{
172 // This is a shell
173 if (ring_local_idx == 0) { return centroid; }
174
175 // This is a hole
176 ring_local_idx -= 1; // offset hole indices to be 0-based
177 T max_hole_displacement = radius - hole_radius;
178 T displacement_x = -max_hole_displacement + ring_local_idx * hole_radius * 2;
179 T displacement_y = 0.0;
180 return centroid + vec_2d<T>{displacement_x, displacement_y};
181}
182
194template <typename T, typename MultipolygonRange>
195void __global__ generate_multipolygon_array_coordinates(MultipolygonRange multipolygons,
196 multipolygon_generator_parameter<T> params)
197{
198 for (auto idx : ranger::grid_stride_range(multipolygons.num_points())) {
199 auto ring_idx = multipolygons.ring_idx_from_point_idx(idx);
200 auto part_idx = multipolygons.part_idx_from_ring_idx(ring_idx);
201 auto geometry_idx = multipolygons.geometry_idx_from_part_idx(part_idx);
202
203 auto point_local_idx = idx - params.num_vertices_per_ring() * ring_idx;
204 auto ring_local_idx = ring_idx - params.num_rings_per_polygon() * part_idx;
205 auto part_local_idx = part_idx - params.num_polygons_per_multipolygon * geometry_idx;
206
207 auto centroid = ring_centroid_displacement(
208 polygon_centroid_displacement(params.centroid, part_local_idx, params.radius),
209 ring_local_idx,
210 params.radius,
211 params.hole_radius());
212
213 if (ring_local_idx == 0) // Generate coordinate for shell
214 multipolygons.point_begin()[idx] = generate_ring_coordinate(
215 point_local_idx, params.num_edges_per_ring, centroid, params.radius);
216 else // Generate coordinate for holes
217 multipolygons.point_begin()[idx] = generate_ring_coordinate(
218 point_local_idx, params.num_edges_per_ring, centroid, params.hole_radius());
219 }
220}
221
230template <typename T>
231auto generate_multipolygon_array(multipolygon_generator_parameter<T> params,
232 rmm::cuda_stream_view stream)
233{
234 rmm::device_uvector<std::size_t> geometry_offsets(params.num_multipolygons + 1, stream);
235 rmm::device_uvector<std::size_t> part_offsets(params.num_polygons() + 1, stream);
236 rmm::device_uvector<std::size_t> ring_offsets(params.num_rings() + 1, stream);
237 rmm::device_uvector<vec_2d<T>> coordinates(params.num_coords(), stream);
238
239 thrust::sequence(rmm::exec_policy(stream),
240 ring_offsets.begin(),
241 ring_offsets.end(),
242 std::size_t{0},
243 params.num_vertices_per_ring());
244
245 thrust::sequence(rmm::exec_policy(stream),
246 part_offsets.begin(),
247 part_offsets.end(),
248 std::size_t{0},
249 params.num_rings_per_polygon());
250
251 thrust::sequence(rmm::exec_policy(stream),
252 geometry_offsets.begin(),
253 geometry_offsets.end(),
254 std::size_t{0},
255 params.num_polygons_per_multipolygon);
256
257 auto multipolygons = multipolygon_range(geometry_offsets.begin(),
258 geometry_offsets.end(),
259 part_offsets.begin(),
260 part_offsets.end(),
261 ring_offsets.begin(),
262 ring_offsets.end(),
263 coordinates.begin(),
264 coordinates.end());
265
266 auto [tpb, nblocks] = grid_1d(multipolygons.num_points());
267
268 generate_multipolygon_array_coordinates<T><<<nblocks, tpb, 0, stream>>>(multipolygons, params);
269
270 CUSPATIAL_CHECK_CUDA(stream.value());
271
272 return make_multipolygon_array<std::size_t, vec_2d<T>>(std::move(geometry_offsets),
273 std::move(part_offsets),
274 std::move(ring_offsets),
275 std::move(coordinates));
276}
277
283template <typename T>
285 std::size_t num_multilinestrings;
286 std::size_t num_linestrings_per_multilinestring;
287 std::size_t num_segments_per_linestring;
288 T segment_length;
289 vec_2d<T> origin;
290
291 std::size_t num_linestrings()
292 {
293 return num_multilinestrings * num_linestrings_per_multilinestring;
294 }
295
296 std::size_t num_points_per_linestring() { return num_segments_per_linestring + 1; }
297
298 std::size_t num_segments() { return num_linestrings() * num_segments_per_linestring; }
299 std::size_t num_points() { return num_linestrings() * num_points_per_linestring(); }
300};
301
325template <typename T>
326auto generate_multilinestring_array(multilinestring_generator_parameter<T> params,
327 rmm::cuda_stream_view stream)
328{
329 rmm::device_uvector<std::size_t> geometry_offset(params.num_multilinestrings + 1, stream);
330 rmm::device_uvector<std::size_t> part_offset(params.num_linestrings() + 1, stream);
331 rmm::device_uvector<vec_2d<T>> points(params.num_points(), stream);
332
333 thrust::sequence(rmm::exec_policy(stream),
334 geometry_offset.begin(),
335 geometry_offset.end(),
336 static_cast<std::size_t>(0),
337 params.num_linestrings_per_multilinestring);
338
339 thrust::sequence(rmm::exec_policy(stream),
340 part_offset.begin(),
341 part_offset.end(),
342 static_cast<std::size_t>(0),
343 params.num_segments_per_linestring + 1);
344
345 thrust::tabulate(rmm::exec_policy(stream),
346 points.begin(),
347 points.end(),
349
350 thrust::exclusive_scan(rmm::exec_policy(stream),
351 points.begin(),
352 points.end(),
353 points.begin(),
354 params.origin,
355 detail::random_walk_functor<T>{params.segment_length});
356
357 return make_multilinestring_array<std::size_t, T>(
358 std::move(geometry_offset), std::move(part_offset), std::move(points));
359}
360
366template <typename T>
368 using element_t = T;
369
370 std::size_t num_multipoints;
371 std::size_t num_points_per_multipoints;
372 vec_2d<T> lower_left;
373 vec_2d<T> upper_right;
374
375 CUSPATIAL_HOST_DEVICE std::size_t num_points()
376 {
377 return num_multipoints * num_points_per_multipoints;
378 }
379};
380
389template <typename T>
390auto generate_multipoint_array(multipoint_generator_parameter<T> params,
391 rmm::cuda_stream_view stream)
392{
393 rmm::device_uvector<vec_2d<T>> coordinates(params.num_points(), stream);
394 rmm::device_uvector<std::size_t> offsets(params.num_multipoints + 1, stream);
395
396 thrust::sequence(rmm::exec_policy(stream),
397 offsets.begin(),
398 offsets.end(),
399 std::size_t{0},
400 params.num_points_per_multipoints);
401
402 auto engine_x = deterministic_engine(params.num_points());
403 auto engine_y = deterministic_engine(2 * params.num_points());
404
405 auto x_dist = make_uniform_dist(params.lower_left.x, params.upper_right.x);
406 auto y_dist = make_uniform_dist(params.lower_left.y, params.upper_right.y);
407
408 auto point_gen =
409 point_generator(params.lower_left, params.upper_right, engine_x, engine_y, x_dist, y_dist);
410
411 thrust::tabulate(rmm::exec_policy(stream), coordinates.begin(), coordinates.end(), point_gen);
412
413 return make_multipoint_array(std::move(offsets), std::move(coordinates));
414}
415
416} // namespace test
417} // namespace cuspatial
#define CUSPATIAL_CHECK_CUDA(stream)
Debug macro to check for CUDA errors.
Definition error.hpp:166
Struct to store the parameters of the multilinestring generator.
Struct to store the parameters of the multipoint aray.
Struct to store the parameters of the multipolygon array generator.