@mytec: 3.8.0 start, stable w/0 ref+

This commit is contained in:
2026-02-03 23:24:12 +02:00
parent 6cd9d869cc
commit 6dcc5a19b9
3 changed files with 215 additions and 9 deletions

View File

@@ -62,6 +62,9 @@ from app.services.parallel_coverage_service import (
calculate_coverage_parallel, get_cpu_count, get_parallel_backend,
CancellationToken,
)
# NOTE: gpu_manager and gpu_service are imported INSIDE functions that need them,
# NOT at module level. This prevents worker processes from initializing CuPy/CUDA
# which causes cudaErrorInsufficientDriver errors in child processes.
# ── New propagation models (Phase 3.0) ──
from app.propagation.base import PropagationModel, PropagationInput, PropagationOutput
@@ -546,8 +549,11 @@ class CoverageService:
from app.services.gpu_service import gpu_service
t_gpu = time.time()
grid_lats = np.array([lat for lat, lon in grid])
grid_lons = np.array([lon for lat, lon in grid])
# Import GPU modules here (main process only) to avoid CUDA context issues in workers
from app.services.gpu_backend import gpu_manager
xp = gpu_manager.get_array_module()
grid_lats = xp.array([lat for lat, lon in grid], dtype=xp.float64)
grid_lons = xp.array([lon for lat, lon in grid], dtype=xp.float64)
pre_distances = gpu_service.precompute_distances(
grid_lats, grid_lons, site.lat, site.lon
@@ -556,6 +562,9 @@ class CoverageService:
pre_distances, site.frequency, site.height,
environment=getattr(settings, 'environment', 'urban'),
)
gpu_time = time.time() - t_gpu
backend_name = "GPU (CUDA)" if gpu_manager.gpu_available else "CPU (NumPy)"
_clog(f"Precomputed {len(grid)} distances+path_loss on {backend_name} in {gpu_time:.2f}s")
# Build lookup dict for point loop
precomputed = {}
@@ -918,9 +927,12 @@ class CoverageService:
await asyncio.sleep(0)
from app.services.gpu_service import gpu_service
from app.services.gpu_backend import gpu_manager
grid_lats = np.array([lat for lat, _lon in tile_grid])
grid_lons = np.array([_lon for _lat, _lon in tile_grid])
t_gpu = time.time()
xp = gpu_manager.get_array_module()
grid_lats = xp.array([lat for lat, _lon in tile_grid], dtype=xp.float64)
grid_lons = xp.array([_lon for _lat, _lon in tile_grid], dtype=xp.float64)
pre_distances = gpu_service.precompute_distances(
grid_lats, grid_lons, site.lat, site.lon,
@@ -929,6 +941,9 @@ class CoverageService:
pre_distances, site.frequency, site.height,
environment=getattr(settings, 'environment', 'urban'),
)
gpu_time = time.time() - t_gpu
backend_name = "GPU (CUDA)" if gpu_manager.gpu_available else "CPU (NumPy)"
_clog(f"Tile {tile_idx+1}: precomputed {len(tile_grid)} pts on {backend_name} in {gpu_time:.2f}s")
precomputed = {}
for i, (lat, lon) in enumerate(tile_grid):
@@ -1405,14 +1420,18 @@ class CoverageService:
lat2: float, lon2: float
) -> float:
"""Calculate bearing from point 1 to point 2 (degrees)"""
lat1, lon1, lat2, lon2 = map(np.radians, [lat1, lon1, lat2, lon2])
# Use math for scalar operations (faster than numpy/cupy for single values)
lat1_r = math.radians(lat1)
lon1_r = math.radians(lon1)
lat2_r = math.radians(lat2)
lon2_r = math.radians(lon2)
dlon = lon2 - lon1
dlon = lon2_r - lon1_r
x = np.sin(dlon) * np.cos(lat2)
y = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(dlon)
x = math.sin(dlon) * math.cos(lat2_r)
y = math.cos(lat1_r) * math.sin(lat2_r) - math.sin(lat1_r) * math.cos(lat2_r) * math.cos(dlon)
bearing = np.degrees(np.arctan2(x, y))
bearing = math.degrees(math.atan2(x, y))
return (bearing + 360) % 360