Skip to content

Commit 13a89de

Browse files
committed
Merge branch 'main' into joss_paper
2 parents 34af3b2 + d0060ea commit 13a89de

166 files changed

Lines changed: 8850 additions & 2946 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ venv.bak/
132132
.spyderproject
133133
.spyproject
134134

135+
# VS Code project settings
136+
.vscode/settings.json
137+
135138
# Rope project settings
136139
.ropeproject
137140

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
fail_fast: false
22
repos:
33
- repo: https://github.com/pre-commit/mirrors-mypy
4-
rev: v1.17.0 # Use the sha / tag you want to point at
4+
rev: v1.17.1 # Use the sha / tag you want to point at
55
hooks:
66
- id: mypy
77
- repo: https://github.com/pre-commit/pre-commit-hooks
8-
rev: v5.0.0
8+
rev: v6.0.0
99
hooks:
1010
- id: trailing-whitespace
1111
- id: end-of-file-fixer
1212
- id: check-yaml
1313
- id: check-added-large-files
1414
- repo: https://github.com/astral-sh/ruff-pre-commit
15-
rev: v0.12.4
15+
rev: v0.12.11
1616
hooks:
1717
- id: ruff
1818
args: [ --fix ]

artist/core/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
HeliostatRayTracer,
66
RestrictedDistributedSampler,
77
)
8-
from artist.core.kinematic_optimizer import KinematicOptimizer
9-
from artist.scene.rays import Rays
8+
from artist.core.kinematic_calibrator import KinematicCalibrator
9+
from artist.core.motor_position_optimizer import MotorPositionsOptimizer
10+
from artist.core.surface_reconstructor import SurfaceReconstructor
1011

1112
__all__ = [
1213
"HeliostatRayTracer",
1314
"DistortionsDataset",
1415
"RestrictedDistributedSampler",
15-
"Rays",
16-
"KinematicOptimizer",
16+
"KinematicCalibrator",
17+
"SurfaceReconstructor",
18+
"MotorPositionsOptimizer",
1719
]

artist/core/core_utils.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import torch
2+
3+
from artist.util.environment_setup import get_device
4+
5+
6+
def per_heliostat_reduction(
7+
per_sample_values: torch.Tensor,
8+
active_heliostats_mask: torch.Tensor,
9+
device: torch.device | None = None,
10+
) -> torch.Tensor:
11+
"""
12+
Compute mean losses for each heliostat with multiple samples.
13+
14+
If the active heliostats of one group have different amounts of samples to train on, i.e.
15+
one heliostat is trained with more samples than another, this function makes sure that
16+
each heliostat still contributes equally to the overall loss of the group. This function
17+
computes the mean loss for each heliostat.
18+
19+
Parameters
20+
----------
21+
per_sample_values : torch.Tensor
22+
The per sample values to be reduced.
23+
Tensor of shape [number_of_samples]
24+
active_heliostats_mask : torch.Tensor
25+
A mask defining which heliostats are activated.
26+
Tensor of shape [number_of_heliostats].
27+
device : torch.device | None
28+
The device on which to perform computations or load tensors and models (default is None).
29+
If None, ARTIST will automatically select the most appropriate
30+
device (CUDA or CPU) based on availability and OS.
31+
32+
Returns
33+
-------
34+
torch.Tensor
35+
The mean loss per heliostat.
36+
Tensor of shape [number_of_heliostats].
37+
"""
38+
device = get_device(device=device)
39+
40+
# A sample to heliostat index mapping.
41+
heliostat_ids = torch.repeat_interleave(
42+
torch.arange(len(active_heliostats_mask), device=device),
43+
active_heliostats_mask,
44+
)
45+
46+
loss_sum_per_heliostat = torch.zeros(len(active_heliostats_mask), device=device)
47+
loss_sum_per_heliostat = loss_sum_per_heliostat.index_add(
48+
0, heliostat_ids, per_sample_values
49+
)
50+
51+
# Compute mean MSE per heliostat on each rank.
52+
number_of_samples_per_heliostat = torch.zeros(
53+
len(active_heliostats_mask), device=device
54+
)
55+
number_of_samples_per_heliostat.index_add_(
56+
0, heliostat_ids, torch.ones_like(per_sample_values, device=device)
57+
)
58+
59+
counts_clamped = number_of_samples_per_heliostat.clamp_min(1.0)
60+
mean_loss_per_heliostat = loss_sum_per_heliostat / counts_clamped
61+
mean_loss_per_heliostat = mean_loss_per_heliostat * (
62+
number_of_samples_per_heliostat > 0
63+
)
64+
65+
return mean_loss_per_heliostat
66+
67+
68+
def scale_loss(
69+
loss: torch.Tensor, reference: torch.Tensor, weight: float
70+
) -> torch.Tensor:
71+
"""
72+
Scale one loss so that its weighted contribution is a ratio of the reference loss.
73+
74+
Parameters
75+
----------
76+
loss : torch.Tensor
77+
The loss to be scaled.
78+
Tensor of shape [1].
79+
reference_loss : torch.Tensor
80+
The reference loss.
81+
Tensor of shape [1].
82+
weight : float
83+
The weight or ratio used for the scaling.
84+
85+
Returns
86+
-------
87+
torch.Tensor
88+
The scaled loss.
89+
Tensor of shape [1].
90+
"""
91+
epsilon = 1e-12
92+
scale = (reference * weight) / (loss + epsilon)
93+
return loss * scale

artist/core/heliostat_ray_tracer.py

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,9 @@ class HeliostatRayTracer:
194194
The distortion sampler.
195195
distortions_loader : DataLoader
196196
The dataloader that loads the distortions.
197-
bitmap_resolution_e : int
198-
The resolution of the bitmap in the east dimension.
199-
bitmap_resolution_u : int
200-
The resolution of the bitmap in the up dimension.
197+
bitmap_resolution : int
198+
The resolution of the bitmap in both directions.
199+
Tensor of shape [2].
201200
202201
Methods
203202
-------
@@ -219,8 +218,7 @@ def __init__(
219218
rank: int = 0,
220219
batch_size: int = 1,
221220
random_seed: int = 7,
222-
bitmap_resolution_e: int = 256,
223-
bitmap_resolution_u: int = 256,
221+
bitmap_resolution: torch.Tensor = torch.tensor([256, 256]),
224222
) -> None:
225223
"""
226224
Initialize the heliostat ray tracer.
@@ -245,10 +243,9 @@ def __init__(
245243
The amount of samples (Heliostats) processed parallel within a single rank (default is 1).
246244
random_seed : int
247245
The random seed used for generating the distortions (default is 7).
248-
bitmap_resolution_e : int
249-
The resolution of the bitmap in the east dimension (default is 256).
250-
bitmap_resolution_u : int
251-
The resolution of the bitmap in the up dimension (default is 256).
246+
bitmap_resolution : torch.Tensor
247+
The resolution of the bitmap in both directions. (default is torch.tensor([256,256])).
248+
Tensor of shape [2].
252249
"""
253250
self.scenario = scenario
254251
self.heliostat_group = heliostat_group
@@ -282,8 +279,7 @@ def __init__(
282279
sampler=self.distortions_sampler,
283280
)
284281

285-
self.bitmap_resolution_e = bitmap_resolution_e
286-
self.bitmap_resolution_u = bitmap_resolution_u
282+
self.bitmap_resolution = bitmap_resolution
287283

288284
def trace_rays(
289285
self,
@@ -296,19 +292,22 @@ def trace_rays(
296292
Perform heliostat ray tracing.
297293
298294
Scatter the rays according to the distortions, calculate the intersections with the target planes,
299-
and sample the resulting bitmaps on the target areas. The bitmaps are generated seperatly for each
295+
and sample the resulting bitmaps on the target areas. The bitmaps are generated separately for each
300296
active heliostat and can be accessed individually or they can be combined to get the total flux
301297
density distribution for all heliostats on all target areas.
302298
303299
Parameters
304300
----------
305301
incident_ray_directions : torch.Tensor
306302
The direction of the incident rays as seen from the heliostats.
303+
Tensor of shape [number_of_active_heliostats, 4].
307304
active_heliostats_mask : torch.Tensor
308305
A mask where 0 indicates a deactivated heliostat and 1 an activated one.
309306
An integer greater than 1 indicates that this heliostat is regarded multiple times.
307+
Tensor of shape [number_of_heliostats].
310308
target_area_mask : torch.Tensor
311309
The indices of the target areas for each active heliostat.
310+
Tensor of shape [number_of_active_heliostats].
312311
device : torch.device | None
313312
The device on which to perform computations or load tensors and models (default is None).
314313
If None, ARTIST will automatically select the most appropriate
@@ -323,6 +322,7 @@ def trace_rays(
323322
-------
324323
torch.Tensor
325324
The resulting bitmaps per heliostat.
325+
Tensor of shape [number_of_active_heliostats, bitmap_resolution_e, bitmap_resolution_u].
326326
"""
327327
device = get_device(device=device)
328328

@@ -333,8 +333,8 @@ def trace_rays(
333333
flux_distributions = torch.zeros(
334334
(
335335
self.heliostat_group.number_of_active_heliostats,
336-
self.bitmap_resolution_u,
337-
self.bitmap_resolution_e,
336+
self.bitmap_resolution[1],
337+
self.bitmap_resolution[0],
338338
),
339339
device=device,
340340
)
@@ -405,10 +405,13 @@ def scatter_rays(
405405
----------
406406
distortion_u : torch.Tensor
407407
The distortions in up direction (angles for scattering).
408+
Tensor of shape [number_of_active_heliostats, number_of_rays, number_of_combined_surface_normals_all_facets].
408409
distortion_e : torch.Tensor
409410
The distortions in east direction (angles for scattering).
411+
Tensor of shape [number_of_active_heliostats, number_of_rays, number_of_combined_surface_normals_all_facets].
410412
original_ray_direction : torch.Tensor
411413
The ray direction around which to scatter.
414+
Tensor of shape [number_of_active_heliostats, number_of_combined_surface_normals_all_facets, 4].
412415
device : torch.device | None
413416
The device on which to perform computations or load tensors and models (default is None).
414417
If None, ARTIST will automatically select the most appropriate
@@ -451,12 +454,16 @@ def sample_bitmaps(
451454
----------
452455
intersections : torch.Tensor
453456
The intersections of rays on the target area planes for each heliostat.
457+
Tensor of shape [number_of_active_heliostats, number_of_rays, number_of_combined_surface_points_all_facets, 4].
454458
absolute_intensities : torch.Tensor
455459
The absolute intensities of the rays hitting the target planes for each heliostat.
460+
Tensor of shape [number_of_active_heliostats, number_of_rays, number_of_combined_surface_points_all_facets].
456461
active_heliostats_mask : torch.Tensor
457462
Used to map bitmaps per heliostat to correct index.
463+
Tensor of shape [number_of_heliostats].
458464
target_area_mask : torch.Tensor
459-
The indices of target areas on which each heliostat should be raytraced.
465+
The indices of target areas on which each heliostat is raytraced.
466+
Tensor of shape [number_of_active_heliostats].
460467
device : torch.device | None
461468
The device on which to perform computations or load tensors and models (default is None).
462469
If None, ARTIST will automatically select the most appropriate
@@ -466,6 +473,7 @@ def sample_bitmaps(
466473
-------
467474
torch.Tensor
468475
The flux density distributions of the reflected rays on the target areas for each active heliostat.
476+
Tensor of shape [number_of_active_heliostats, bitmap_resolution_e, bitmap_resolution_u].
469477
"""
470478
device = get_device(device=device)
471479

@@ -512,10 +520,10 @@ def sample_bitmaps(
512520
# x_intersections and y_intersections contain those intersection coordinates scaled to a range from 0 to bitmap_resolution_e/_u.
513521
# Additionally a mask is applied, only the intersections where intersection_indices == True are kept, the tensors are flattened.
514522
x_intersections = (
515-
dx_intersections / plane_widths * self.bitmap_resolution_e
523+
dx_intersections / plane_widths * self.bitmap_resolution[0]
516524
).reshape(-1, total_intersections)
517525
y_intersections = (
518-
dy_intersections / plane_heights * self.bitmap_resolution_u
526+
dy_intersections / plane_heights * self.bitmap_resolution[1]
519527
).reshape(-1, total_intersections)
520528

521529
# We assume a continuously positioned value in-between four
@@ -614,9 +622,9 @@ def sample_bitmaps(
614622
# in the bitmap (i.e. we prevent out-of-bounds access).
615623
intersection_indices_2 = (
616624
(0 <= x_indices)
617-
& (x_indices < self.bitmap_resolution_e)
625+
& (x_indices < self.bitmap_resolution[0])
618626
& (0 <= y_indices)
619-
& (y_indices < self.bitmap_resolution_u)
627+
& (y_indices < self.bitmap_resolution[1])
620628
)
621629

622630
final_intersection_indices = (
@@ -636,8 +644,8 @@ def sample_bitmaps(
636644
bitmaps_per_heliostat = torch.zeros(
637645
(
638646
self.heliostat_group.number_of_active_heliostats,
639-
self.bitmap_resolution_u,
640-
self.bitmap_resolution_e,
647+
self.bitmap_resolution[1],
648+
self.bitmap_resolution[0],
641649
),
642650
dtype=dx_intersections.dtype,
643651
device=device,
@@ -647,8 +655,8 @@ def sample_bitmaps(
647655
bitmaps_per_heliostat.index_put_(
648656
(
649657
heliostat_indices[mask],
650-
self.bitmap_resolution_u - 1 - y_indices[final_intersection_indices],
651-
self.bitmap_resolution_e - 1 - x_indices[final_intersection_indices],
658+
self.bitmap_resolution[1] - 1 - y_indices[final_intersection_indices],
659+
self.bitmap_resolution[0] - 1 - x_indices[final_intersection_indices],
652660
),
653661
intensities[final_intersection_indices],
654662
accumulate=True,
@@ -669,8 +677,10 @@ def get_bitmaps_per_target(
669677
----------
670678
bitmaps_per_heliostat : torch.Tensor
671679
Bitmaps per heliostat.
680+
Tensor of shape [number_of_active_heliostats, bitmap_resolution_e, bitmap_resolution_u].
672681
target_area_mask : torch.Tensor
673682
The mapping from heliostat to target area.
683+
Tensor of shape [number_of_active_heliostats].
674684
device : torch.device | None
675685
The device on which to perform computations or load tensors and models (default is None).
676686
If None, ARTIST will automatically select the most appropriate
@@ -680,14 +690,15 @@ def get_bitmaps_per_target(
680690
-------
681691
torch.Tensor
682692
Bitmaps per target area.
693+
Tensor of shape [number_of_target_areas, bitmap_resolution_e, bitmap_resolution_u].
683694
"""
684695
device = get_device(device=device)
685696

686697
group_bitmaps_per_target = torch.zeros(
687698
(
688699
self.scenario.target_areas.number_of_target_areas,
689-
self.bitmap_resolution_e,
690-
self.bitmap_resolution_u,
700+
self.bitmap_resolution[0],
701+
self.bitmap_resolution[1],
691702
),
692703
device=device,
693704
)

0 commit comments

Comments
 (0)