Skip to content

solar

solar #

Angular separation between two solar positions on the celestial sphere.

Measures the angular distance between two solar positions using the Haversine formula. Used to ensure captures are distributed across different sun angles (morning, noon, evening, golden hour, etc.).

SolarDistanceFunction #

Bases: BatchedDistanceFunction[SolarPosition]

Haversine angular distance between solar positions, normalized by sigma.

Source code in capturegraph-lib/capturegraph/scheduling/distance/solar.py
class SolarDistanceFunction(BatchedDistanceFunction[SolarPosition]):
    """Haversine angular distance between solar positions, normalized by sigma."""

    def __init__(self, sigma_deg: float = 1.0) -> None:
        """Normalize the Haversine angular distance by ``sigma_deg``."""
        self.sigma_deg = sigma_deg

    def __call__(self, a: SolarPosition, b: SolarPosition) -> float:
        """Haversine angular distance between two solar positions, normalized."""
        return solar_distance_deg(a, b) / self.sigma_deg

    def extract(self, items: cg.Array[Any]) -> np.ndarray:
        """Extract altitude/azimuth (in radians) as a two-column feature array."""
        alts = np.radians(np.array(items.altitude, dtype=np.float64))
        azs = np.radians(np.array(items.azimuth, dtype=np.float64))
        return np.column_stack([alts, azs])

    def pairwise(self, features_a: np.ndarray, features_b: np.ndarray) -> np.ndarray:
        """Compute pairwise Haversine distances from extracted solar features."""
        alt1, az1 = features_a[:, 0], features_a[:, 1]
        alt2, az2 = features_b[:, 0], features_b[:, 1]

        return _haversine_deg_vectorized(alt1, az1, alt2, az2) / self.sigma_deg

__call__(a, b) #

Haversine angular distance between two solar positions, normalized.

Source code in capturegraph-lib/capturegraph/scheduling/distance/solar.py
def __call__(self, a: SolarPosition, b: SolarPosition) -> float:
    """Haversine angular distance between two solar positions, normalized."""
    return solar_distance_deg(a, b) / self.sigma_deg

__init__(sigma_deg=1.0) #

Normalize the Haversine angular distance by sigma_deg.

Source code in capturegraph-lib/capturegraph/scheduling/distance/solar.py
def __init__(self, sigma_deg: float = 1.0) -> None:
    """Normalize the Haversine angular distance by ``sigma_deg``."""
    self.sigma_deg = sigma_deg

extract(items) #

Extract altitude/azimuth (in radians) as a two-column feature array.

Source code in capturegraph-lib/capturegraph/scheduling/distance/solar.py
def extract(self, items: cg.Array[Any]) -> np.ndarray:
    """Extract altitude/azimuth (in radians) as a two-column feature array."""
    alts = np.radians(np.array(items.altitude, dtype=np.float64))
    azs = np.radians(np.array(items.azimuth, dtype=np.float64))
    return np.column_stack([alts, azs])

pairwise(features_a, features_b) #

Compute pairwise Haversine distances from extracted solar features.

Source code in capturegraph-lib/capturegraph/scheduling/distance/solar.py
def pairwise(self, features_a: np.ndarray, features_b: np.ndarray) -> np.ndarray:
    """Compute pairwise Haversine distances from extracted solar features."""
    alt1, az1 = features_a[:, 0], features_a[:, 1]
    alt2, az2 = features_b[:, 0], features_b[:, 1]

    return _haversine_deg_vectorized(alt1, az1, alt2, az2) / self.sigma_deg

solar(sigma_deg=1.0) #

Create a distance function based on solar angular separation.

Uses the Haversine formula to compute angular distance between solar positions on the celestial sphere.

Parameters:

Name Type Description Default
sigma_deg float

Normalization factor in degrees. The returned distance is angle_deg / sigma_deg, so sessions within sigma_deg angular separation have distance < 1.0.

1.0

Returns:

Type Description
SolarDistanceFunction

A distance function with batch support: (session_a, session_b) -> float

Source code in capturegraph-lib/capturegraph/scheduling/distance/solar.py
def solar(sigma_deg: float = 1.0) -> SolarDistanceFunction:
    """Create a distance function based on solar angular separation.

    Uses the Haversine formula to compute angular distance between solar
    positions on the celestial sphere.

    Args:
        sigma_deg: Normalization factor in degrees. The returned distance is
            `angle_deg / sigma_deg`, so sessions within sigma_deg angular
            separation have distance < 1.0.

    Returns:
        A distance function with batch support: `(session_a, session_b) -> float`
    """
    return SolarDistanceFunction(sigma_deg)

solar_distance_deg(angle_a, angle_b) #

Compute angular distance using the Haversine formula.

The Haversine formula is numerically stable for small angles, unlike the spherical cosine formula which loses precision near 0°.

Parameters:

Name Type Description Default
angle_a SolarPosition

First solar position (with altitude, azimuth in degrees).

required
angle_b SolarPosition

Second solar position (with altitude, azimuth in degrees).

required

Returns:

Type Description
float

Angular distance in degrees.

Source code in capturegraph-lib/capturegraph/scheduling/distance/solar.py
def solar_distance_deg(angle_a: SolarPosition, angle_b: SolarPosition) -> float:
    """Compute angular distance using the Haversine formula.

    The Haversine formula is numerically stable for small angles, unlike
    the spherical cosine formula which loses precision near 0°.

    Args:
        angle_a: First solar position (with altitude, azimuth in degrees).
        angle_b: Second solar position (with altitude, azimuth in degrees).

    Returns:
        Angular distance in degrees.
    """
    # Convert to radians
    alt1, az1 = np.radians(angle_a.altitude), np.radians(angle_a.azimuth)
    alt2, az2 = np.radians(angle_b.altitude), np.radians(angle_b.azimuth)

    # Haversine formula
    d_alt = alt2 - alt1
    d_az = az2 - az1

    a = np.square(np.sin(d_alt / 2)) + np.cos(alt1) * np.cos(alt2) * np.square(np.sin(d_az / 2))

    c = 2 * np.arcsin(np.sqrt(np.clip(a, 0, 1)))

    return np.degrees(c)