API Reference#

This reference intentionally focuses on the high-level public API used in the official samples and notebooks.

Core Public API#

Simulator#

Primary entry point for replay and forward simulation.

class biwipy.core.Simulator(grib, behavior: CyclistBehavior | None = None, CdA: float = 0.5, Cr: float = 0.004, m: float = 75.0, g: float = 9.80665, clip_wind: float = 40.0, use_yaw_cdA: bool = True, ratio_wind: float = 0.25, yaw_k: float = 0.02, v_max: float = 25.0, use_dynamic: bool = True, limit_speed_in_corners: bool = True, rho_forced: float | None = None)#

Bases: object

Stable public interface for cycling simulations.

  • simulate_future(): forward simulation (forecast) -> SimulationResult

  • simulate_replay(): replay of a completed ride (post-ride) -> SimulationResult

  • P0_from_v0(): convert a target flat speed to a reference power

  • v0_from_P0(): convert a reference power to an equilibrium flat speed

  • print_power_model(): display the adaptive power table by slope

P0_from_v0(v0: float) float#

Compute P0 (W) from a reference speed v0 (m/s) on flat ground and no wind.

Wrapper around estimate_P0_from_v0 using the Simulator parameters (CdA, Cr, m, g, rho_forced).

print_power_model(P0: float) None#

Print the adaptive power model table by slope.

Wrapper around print_power_model_info with the configured Simulator behavior.

simulate_future(segments_in: List[Dict], t_start, v0: float | None = None, P0: float | None = None, passes: int = 2, velocity_smooth_window: int = 0) SimulationResult#

Prospective simulation (future forecast).

Parameters:#

segments_inList[Dict]

Route segment data

t_startdatetime

Start timestamp

v0Optional[float]

Initial speed (m/s)

P0Optional[float]

Reference power for simulation (Watts) - REQUIRED for forecast

passesint

Number of simulation passes (default: 2)

velocity_smooth_windowint

Window size for velocity smoothing (default: 0, no smoothing). Typically not needed for prospective simulations (use for replay only).

Returns:#

SimulationResult

Complete structured output with all statistics. The t_start field preserves the provided timezone (typically UTC from GPX files).

simulate_replay(segments_in: List[Dict], t_start=None, passes: int = 2, velocity_smooth_window: int = 7) SimulationResult#

Replay simulation (post-ride analysis).

Uses GPS timestamps to calculate power from observed speeds. P0 is automatically calibrated from the ride data (always enabled).

Parameters:#

segments_inList[Dict]

GPS segment data with timestamps and speeds

t_startdatetime, optional

Start timestamp. If None, will be auto-extracted from first segment’s gpxtime_start. GPX segments must contain timestamps (gpxtime_start, gpxtime_end).

passesint

Number of simulation passes (default: 2)

velocity_smooth_windowint

Window size for velocity smoothing to eliminate GPS timestamp noise. Default is 7 segments. Set to 0 to disable smoothing.

Returns:#

SimulationResult

Complete structured output with all statistics and power data. The t_start field will contain the auto-extracted start time (in UTC timezone). The P0 field contains the calibrated reference power matching observed performance.

v0_from_P0(P0: float) float#

Compute equilibrium speed v0 (m/s) from power P0 (W) on flat ground and no wind.

Uses solve_speed_for_power with zero slope and zero wind.

Cyclist behavior#

Behavior profiles for climbing, descending and cornering.

class biwipy.core.CyclistBehavior(uphill: str = 'realistic', downhill: str = 'realistic', corner: str = 'realistic')#

Bases: object

Centralized cyclist behavior parameters.

Groups: - Slope thresholds (uphill/downhill) - Turn thresholds - Uphill behavior factors (3 modes: realistic, conservative, aggressive) - Downhill behavior factors (3 modes) - Turn speed limits (3 modes)

Usage:#

# Create mixed profile >>> params = CyclistBehavior(uphill=’realistic’, downhill=’conservative’, corner=’aggressive’)

# Customize specific parameter >>> params.corner_speed_slight = 20.0 # m/s

# Save >>> params.save(‘/path/to/configs’, ‘remco_profile.json’)

# Load >>> params = CyclistBehavior.load(‘/path/to/configs’, ‘remco_profile.json’)

CORNER_PRESETS = {'aggressive': {'hairpin': 22.0, 'moderate': 22.0, 'sharp': 22.0, 'slight': 22.0, 'straight': 22.0}, 'conservative': {'hairpin': 4.0, 'moderate': 12.0, 'sharp': 6.0, 'slight': 16.0, 'straight': 20.0}, 'realistic': {'hairpin': 4.5, 'moderate': 14.0, 'sharp': 7.0, 'slight': 18.0, 'straight': 22.0}}#
DOWNHILL_PRESETS = {'aggressive': {'corner_downhill_safety_factor': 0.97, 'facteur_forte': 5.0, 'facteur_legere': 5.0, 'puissance_min': 10, 'vitesse_max_absolue': 22.0, 'vitesse_reduction_cap': 0.1, 'vitesse_reduction_factor': 1.0}, 'conservative': {'corner_downhill_safety_factor': 0.85, 'facteur_forte': 3.0, 'facteur_legere': 3.0, 'puissance_min': 10, 'vitesse_max_absolue': 18.0, 'vitesse_reduction_cap': 0.7, 'vitesse_reduction_factor': 10.0}, 'realistic': {'corner_downhill_safety_factor': 0.9, 'facteur_forte': 20.0, 'facteur_legere': 6.0, 'puissance_min': 10, 'vitesse_max_absolue': 22.0, 'vitesse_reduction_cap': 0.4, 'vitesse_reduction_factor': 2.5}}#
SEUIL_CORNER_MODERATE = 45#
SEUIL_CORNER_SHARP = 90#
SEUIL_CORNER_SLIGHT = 20#
SEUIL_CORNER_STRAIGHT = 5#
SEUIL_DESCENTE_LEGERE = -0.05#
SEUIL_MONTEE_FORTE = 0.08#
SEUIL_MONTEE_LEGERE = 0.01#
SEUIL_MONTEE_MODEREE = 0.03#
SEUIL_PLAT = -0.02#
UPHILL_PRESETS = {'aggressive': {'facteur_forte': 4.0, 'facteur_legere': 4.0, 'facteur_moderee': 4.0}, 'conservative': {'facteur_forte': 2.0, 'facteur_legere': 2.0, 'facteur_moderee': 2.0}, 'realistic': {'facteur_forte': 3.5, 'facteur_legere': 1.5, 'facteur_moderee': 2.5}}#
display(verbose: bool = True)#

Display current configuration with explanations.

Parameters:#
verbosebool

If True, display full details. If False, display summary only.

get_corner_speed_limit(bearing_change: float) float#

Return maximum speed for given turn.

Parameters:#
bearing_changefloat

Direction change in degrees (0-180)

Returns:#

float : Maximum speed in m/s

get_uphill_factor(slope: float) float#

Return appropriate power factor for given slope.

Parameters:#
slopefloat

Slope (ratio, e.g., 0.05 = 5%)

Returns:#

float : Power multiplier factor

classmethod load(dirpath: str, filename: str) CyclistBehavior#

Load configuration from a JSON file.

Parameters:#
dirpathstr

Path to the directory containing the file

filenamestr

File name to load

Returns:#

CyclistBehavior : Instance with loaded configuration

Example:#
>>> params = CyclistBehavior.load('/home/user/configs', 'my_profile.json')
save(dirpath: str, filename: str)#

Save configuration to a JSON file.

Parameters:#
dirpathstr

Path to the directory to save into

filenamestr

File name (e.g. ‘remco_profile.json’)

Example:#
>>> params.save('/home/user/configs', 'my_profile.json')
to_dict() Dict[str, Any]#

Convert configuration to dictionary for save.

Returns:#

dict : Complete configuration

biwipy.core.bike_physics.estimate_P0_from_v0(v0, CdA=0.5, Cr=0.004, m=75.0, rho=1.225, g=9.80665)#

Route analysis#

High-level GPX preprocessing and segment generation.

class biwipy.analysis.RouteAnalyzer(smoothing_window: int = 11, smoothing_method: str = 'mediane', merge_min_distance: float = 50.0, merge_max_bearing_diff: float = 20.0, merge_max_slope_diff: float = 0.1, merge_max_slope: float = 0.15)#

Bases: object

Stable public interface for GPX preprocessing and route analysis.

Individual methods (for flexibility): - load_gpx(): load points - segments_from_gpx(): build segments (with smoothing) - merge_segments(): merge short segments - moving_average_kmh(): moving average speed - filter_stops(): remove stops (v < threshold)

Full workflow (convenience): - process_gpx(): wraps the full GPX cleaning pipeline

filter_stops(segments: List[Dict], speed_threshold: float = 1.0, verbose: bool = True) List[Dict]#
load_gpx(gpx_file: str) List[Dict[str, Any]]#
merge_segments(segments: List[Dict], verbose: bool = True) Tuple[List[Dict], int]#
moving_average_kmh(segments: List[Dict], speed_threshold: float = 1.0) float#
process_gpx(gpx_file: str, smooth: bool = True, detect_noise: bool = True, remove_noise: bool = True, merge_segments_flag: bool = True, filter_stops_flag: bool = False, debug_elevation_pipeline: bool = False, max_dist_noise: float = 5.0, min_slope_threshold_noise: float = 0.1, normal_slope_threshold_noise: float = 0.05, log_file: str | None = None, verbose: bool = True) GPXProcessingResult#

Run the full GPX cleanup pipeline and return a structured result.

The returned object provides: - segments: List of processed segments - stats: Dictionary with processing statistics - distance_km: Total distance property - cut(p1_km, p2_km): Method to extract a portion of the route

Example:#

>>> analyzer = RouteAnalyzer()
>>> result = analyzer.process_gpx('route.gpx')
>>> print(f"Total: {result.distance_km:.1f} km")
>>> cut_result = result.cut(p1_km=5.0, p2_km=15.0)
>>> print(f"Cut: {cut_result.distance_km:.1f} km")

Backward-compatible tuple unpacking is preserved: >>> segments, stats = analyzer.process_gpx(‘route.gpx’)

segments_from_gpx(points: List[Dict[str, Any]], smooth: bool = True) List[Dict]#

Weather provider#

High-level weather model wrapper for GRIB-backed simulations.

class biwipy.weather.WeatherProvider(lfile: List[str], bcache: bool = True, model: str = 'GFS', resolution: float = 0.25, grib_limit: tuple = (0.0, 359.75, -90.0, 90.0))#

Bases: object

Stable public interface for weather access (GRIB).

  • get_wind(): fetch tws/twd/gust at a point

  • wind_impact(): project wind onto a segment

classmethod from_grib(grib: Grib) WeatherProvider#

Wrap an existing Grib instance without reloading files.

get_wind(tp, lat: float, lon: float)#
purge_before(dt)#

Remove all timesteps strictly before the given datetime.

purge_between(dt1, dt2)#

Remove all timesteps within the closed interval [dt1, dt2] inclusive.

wind_impact(tp, lat: float, lon: float, bearing: float, ratio_wind: float = 0.1, rugosite: float | None = None)#

GRIB file planning helper#

Plan and retrieve the GRIB files required for interpolation over a ride window.

biwipy.weather.grib_finder.build_grib_list(hdir, date_cible: datetime, pas: Literal[1, 3] = 1, intervalle_h: int = 6, model: str = 'GFS', resolution: float = 0.25, grib_limit: tuple = (0.0, 359.75, -90.0, 90.0)) List[str]#

Build the GRIB file list required for interpolation over a ride time window.

Parameters:
  • hdir (str) – Base directory containing GRIB files (for example /path/to/grib/data/).

  • date_cible (datetime) – Target departure datetime. If not UTC, it will be converted to UTC internally.

  • pas (Literal[1, 3], default=1) – Time-step granularity for file selection (1 = hourly, 3 = every 3 hours).

  • intervalle_h (int, default=6) – Estimated ride duration in hours. An automatic margin is added so interpolation remains valid for the full window.

  • model (str, default="GFS") – Weather model name (currently GFS or IFS).

  • resolution (float, default=0.25) – Target grid resolution in degrees.

  • grib_limit (tuple, default=(0.0, 359.75, -90.0, 90.0)) – Optional geographic bounds (lon_min, lon_max, lat_min, lat_max).

Returns:

  • List[str] – Absolute file paths to GRIB files covering the interpolation period.

  • Example – # For a 2-hour bike ride starting at 10:00 local time departure = datetime(2026, 2, 18, 10, 0, tzinfo=ZoneInfo(“Europe/Paris”)) gribs = build_grib_list(“/data/grib/”, departure, pas=3, intervalle_h=2) # Returns files covering before, during, and after the ride for interpolation

Visualization helpers#

biwipy.analysis.anareswind.print_summary_statistics(data, label: str = 'Simulation', terrain_smoothing_window_m: float = 100.0)#

Display summary statistics for simulation results.

Parameters:#

dataSimulationResult | List[Dict]

Structured result (recommended) or list of segments (legacy)

labelstr, optional

Label to identify the simulation

terrain_smoothing_window_mfloat, optional

Smoothing window (in metres) for terrain, virtual, and effective slope extremes. Name kept for compatibility. Must be between 50 m and 2000 m.

biwipy.analysis.anareswind.compare_scenarios(segments_list: List[List[Dict]], labels: List[str], attribute: str, x_axis: str = 'distance', figsize: tuple = (14, 6), title: str | None = None, distance_from_finish: bool = False)#

Compare multiple scenarios (e.g., with/without wind) for a given attribute.

Parameters:#

segments_listList[List[Dict]]

List of segment lists (one per scenario)

labelsList[str]

Labels for each scenario

attributestr

Attribute to compare

x_axisstr, optional

‘distance’ or ‘time’

figsizetuple, optional

Figure size

titlestr, optional

Chart title

distance_from_finishbool, optional

If True and x_axis=’distance’, the x-axis represents the remaining distance to the finish.

Returns:#

figmatplotlib.figure.Figure

The created figure

axmatplotlib.axes.Axes

The created axes

biwipy.analysis.anareswind.plot_segments_evolution(segments: List[Dict], attributes: List[str], x_axis: str = 'distance', figsize: tuple = (14, 10), title: str | None = None, distance_from_finish: bool = False)#

Plot the evolution of segment attributes against distance or time.

Parameters:#

segmentsList[Dict]

List of segments from simulate_with_weather

attributesList[str]

List of attributes to plot (e.g., [‘tws’, ‘twd’, ‘wind_along’, ‘speed_m_s’]) Available attributes: - ‘tws’: wind speed (m/s) - ‘twd’: wind direction (degrees) - ‘wind_along’: wind along the trajectory (m/s) - ‘gust’: gusts (m/s) - ‘headwind’: headwind (m/s) - ‘gust_along’: gusts along the trajectory (m/s) - ‘crosswind’: crosswind (m/s) - ‘is_headwind’: headwind indicator (boolean) - ‘speed_m_s’: cyclist speed (m/s) - ‘slope’: slope (ratio) - ‘slope_terrain’: terrain slope (ratio) - ‘slope_wind’: virtual slope due to wind (ratio) - ‘slope_effective’: effective slope (terrain + wind) (ratio) - ‘elevation_virtual_m’: virtual elevation gain in metres

x_axisstr, optional

‘distance’ to plot against kilometres (default) ‘time’ to plot against time (minutes)

figsizetuple, optional

Figure size (width, height)

titlestr, optional

Main chart title

distance_from_finishbool, optional

If True and x_axis=’distance’, the x-axis represents the remaining distance to the finish.

Returns:#

figmatplotlib.figure.Figure

The created figure

axeslist

List of created axes

biwipy.analysis.anareswind.plot_elevation_profile(segments: List[Dict], figsize: tuple = (14, 6), title: str | None = None, show_virtual: bool = True, distance_from_finish: bool = False)#

Plot the real and virtual elevation profile (including wind effect).

Parameters:#

segmentsList[Dict]

List of segments from simulate_with_weather

figsizetuple, optional

Figure size (width, height)

titlestr, optional

Main chart title

show_virtualbool, optional

If True, also show the virtual elevation profile (default: True)

distance_from_finishbool, optional

If True, the x-axis represents the remaining distance to the finish. The start appears at the total route distance, the finish at 0 km.

Returns:#

figmatplotlib.figure.Figure

The created figure

axmatplotlib.axes.Axes

The created axes

biwipy.analysis.anareswind.plot_wind_rose(segments: List[Dict], figsize: tuple = (10, 10), title: str | None = None)#

Plot a wind rose based on the segments.

Parameters:#

segmentsList[Dict]

List of result segments

figsizetuple, optional

Figure size

titlestr, optional

Chart title

Returns:#

figmatplotlib.figure.Figure

The created figure

axmatplotlib.axes.Axes

The created axes

biwipy.visualization.interactive_map.create_interactive_map(segments: List[Dict], output_file: str, title: str | None = None, enable_animation: bool = True, distance_from_finish: bool = False) str#

Create an interactive HTML map with a colorized trace and elevation profile.

Features: - Map/profile sync: click the profile -> marker on the map - Time animation with a slider to replay the route

Parameters:#

segmentsList[Dict]

List of segments from simulate_with_weather

output_filestr

Output HTML file path

titlestr, optional

Page title

enable_animationbool, optional

Enable the time animation slider (default: True)

distance_from_finishbool, optional

If True, the interactive elevation profile uses remaining distance to the finish on the x-axis.

Returns:#

str : Generated file path

Notes#

Lower-level/internal modules remain available in the codebase but are not part of this condensed public API reference.