Skip to content

Engines

BaseIntegrationEngine

BaseIntegrationEngine(config: BaseEngineConfigDict | None)

Bases: ABC, EngineProtocol

All calculations are done in imperial units (feet and fps).

Parameters:

Name Type Description Default
config BaseEngineConfigDict | None

The configuration object.

required

Methods:

Name Description
get_calc_step

Get step size for integration.

find_max_range

Find the maximum range along shot_info.look_angle, and the launch angle to reach it.

find_apex

Find the apex of the trajectory.

find_zero_angle

Find the barrel elevation needed to hit sight line at a specific distance.

find_zero_point

Find a zero trajectory and return its solved angle and terminal point.

zero_angle

Find the barrel elevation needed to hit sight line at a specific distance.

zero_point

Return the zero angle and the trajectory point used to determine it.

integrate

Compute the trajectory for the given shot.

Source code in py_ballisticcalc/engines/base_engine.py
488
489
490
491
492
493
494
495
def __init__(self, config: BaseEngineConfigDict | None) -> None:
    """Initialize the class.

    Args:
        config: The configuration object.
    """
    self._config: BaseEngineConfig = create_base_engine_config(config)
    self.gravity_vector: Vector = Vector(0.0, self._config.cGravityConstant, 0.0)

get_calc_step

get_calc_step() -> float

Get step size for integration.

Source code in py_ballisticcalc/engines/base_engine.py
497
498
499
def get_calc_step(self) -> float:
    """Get step size for integration."""
    return self._config.cStepMultiplier

find_max_range

find_max_range(
    shot_info: Shot,
    angle_bracket_deg: tuple[float, float] = (0, 90),
) -> tuple[Distance, Angular]

Find the maximum range along shot_info.look_angle, and the launch angle to reach it.

Parameters:

Name Type Description Default
shot_info Shot

The shot information: gun, ammo, environment, look_angle.

required
angle_bracket_deg tuple[float, float]

The angle bracket in degrees to search for max range. Defaults to (0, 90).

(0, 90)

Returns:

Type Description
tuple[Distance, Angular]

The maximum slant-range and the launch angle to reach it.

Raises:

Type Description
ValueError

If the angle bracket excludes the look_angle.

Source code in py_ballisticcalc/engines/base_engine.py
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
def find_max_range(
    self, shot_info: Shot, angle_bracket_deg: tuple[float, float] = (0, 90)
) -> tuple[Distance, Angular]:
    """Find the maximum range along shot_info.look_angle, and the launch angle to reach it.

    Args:
        shot_info: The shot information: gun, ammo, environment, look_angle.
        angle_bracket_deg: The angle bracket in degrees to search for max range. Defaults to (0, 90).

    Returns:
        The maximum slant-range and the launch angle to reach it.

    Raises:
        ValueError: If the angle bracket excludes the look_angle.
    """
    """
    TODO: Make sure user hasn't restricted angle bracket to exclude the look_angle.
        ... and check for weird situations, like backward-bending trajectories,
        where the max range occurs with launch angle less than the look angle.
    """
    props = self._init_trajectory(shot_info)
    return self._find_max_range(props, angle_bracket_deg)

find_apex

find_apex(shot_info: Shot) -> TrajectoryData

Find the apex of the trajectory.

Apex is defined as the point where the vertical component of velocity goes from positive to negative.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required

Returns:

Name Type Description
TrajectoryData TrajectoryData

The trajectory data at the apex of the trajectory.

Raises:

Type Description
SolverRuntimeError

If no apex is found in the trajectory data.

ValueError

If barrel elevation is not > 0.

Source code in py_ballisticcalc/engines/base_engine.py
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
def find_apex(self, shot_info: Shot) -> TrajectoryData:
    """Find the apex of the trajectory.

    Apex is defined as the point where the vertical component of velocity goes from positive to negative.

    Args:
        shot_info: The shot information.

    Returns:
        TrajectoryData: The trajectory data at the apex of the trajectory.

    Raises:
        SolverRuntimeError: If no apex is found in the trajectory data.
        ValueError: If barrel elevation is not > 0.
    """
    props = self._init_trajectory(shot_info)
    return self._find_apex(props)

find_zero_angle

find_zero_angle(
    shot_info: Shot,
    distance: Distance,
    lofted: bool = False,
) -> Angular

Find the barrel elevation needed to hit sight line at a specific distance.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
distance Distance

Slant distance to the target.

required
lofted bool

If True, find the higher angle that hits the zero point.

False

Returns:

Type Description
Angular

Barrel elevation needed to hit the zero point.

Source code in py_ballisticcalc/engines/base_engine.py
677
678
679
680
681
682
683
684
685
686
687
688
689
def find_zero_angle(self, shot_info: Shot, distance: Distance, lofted: bool = False) -> Angular:
    """Find the barrel elevation needed to hit sight line at a specific distance.

    Args:
        shot_info: The shot information.
        distance: Slant distance to the target.
        lofted: If True, find the higher angle that hits the zero point.

    Returns:
        Barrel elevation needed to hit the zero point.
    """
    props = self._init_trajectory(shot_info)
    return self._find_zero_ridder(props, distance, lofted)[0]

find_zero_point

find_zero_point(
    shot_info: Shot,
    distance: Distance,
    lofted: bool = False,
) -> tuple[Angular, TrajectoryData]

Find a zero trajectory and return its solved angle and terminal point.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
distance Distance

Slant distance to the target.

required
lofted bool

If True, find the higher trajectory that hits the zero point.

False

Returns:

Type Description
Angular

The solved barrel elevation and the terminal RANGE point from the

TrajectoryData

successful zero-finding iteration.

Raises:

Type Description
SolverRuntimeError

If a zero-angle fast path did not integrate a trajectory and therefore has no trajectory point to return.

Source code in py_ballisticcalc/engines/base_engine.py
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
def find_zero_point(
    self, shot_info: Shot, distance: Distance, lofted: bool = False
) -> tuple[Angular, TrajectoryData]:
    """Find a zero trajectory and return its solved angle and terminal point.

    Args:
        shot_info: The shot information.
        distance: Slant distance to the target.
        lofted: If True, find the higher trajectory that hits the zero point.

    Returns:
        The solved barrel elevation and the terminal RANGE point from the
        successful zero-finding iteration.

    Raises:
        SolverRuntimeError: If a zero-angle fast path did not integrate a
            trajectory and therefore has no trajectory point to return.
    """
    props = self._init_trajectory(shot_info)
    angle, point = self._find_zero_ridder(props, distance, lofted)
    if point is None:
        raise SolverRuntimeError("Zero-angle fast path did not evaluate a trajectory point")
    return angle, point

zero_angle

zero_angle(shot_info: Shot, distance: Distance) -> Angular

Find the barrel elevation needed to hit sight line at a specific distance.

First tries the damped Newton solver; if that fails then falls back on _find_zero_ridder.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
distance Distance

The distance to the target.

required

Returns:

Type Description
Angular

Barrel elevation to hit height zero at zero distance along sight line.

Source code in py_ballisticcalc/engines/base_engine.py
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
def zero_angle(self, shot_info: Shot, distance: Distance) -> Angular:
    """Find the barrel elevation needed to hit sight line at a specific distance.

    First tries the damped Newton solver; if that fails then falls back on
    `_find_zero_ridder`.

    Args:
        shot_info: The shot information.
        distance: The distance to the target.

    Returns:
        Barrel elevation to hit height zero at zero distance along sight line.
    """
    props = self._init_trajectory(shot_info)
    try:
        return self._find_zero_newton(props, distance)[0]
    except ZeroFindingError as e:
        logger.warning(f"Failed to find zero angle using base iterative method: {e}")
        # Fallback to guaranteed method
        return self._find_zero_ridder(props, distance)[0]

zero_point

zero_point(
    shot_info: Shot, distance: Distance
) -> tuple[Angular, TrajectoryData]

Return the zero angle and the trajectory point used to determine it.

This is the terminal RANGE point from the successful zero-finding iteration. It performs no final re-integration after finding the angle, so it is useful to callers that need both the zero geometry and its ballistic state.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
distance Distance

Slant distance to the zero point.

required

Returns:

Type Description
Angular

A pair of the lower-arc barrel elevation and the trajectory point

TrajectoryData

from the successful zero-finding iteration.

Raises:

Type Description
SolverRuntimeError

If a zero-angle fast path did not integrate a trajectory and therefore has no trajectory point to return.

Source code in py_ballisticcalc/engines/base_engine.py
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
def zero_point(self, shot_info: Shot, distance: Distance) -> tuple[Angular, TrajectoryData]:
    """Return the zero angle and the trajectory point used to determine it.

    This is the terminal RANGE point from the successful zero-finding
    iteration.  It performs no final re-integration after finding the
    angle, so it is useful to callers that need both the zero geometry and
    its ballistic state.

    Args:
        shot_info: The shot information.
        distance: Slant distance to the zero point.

    Returns:
        A pair of the lower-arc barrel elevation and the trajectory point
        from the successful zero-finding iteration.

    Raises:
        SolverRuntimeError: If a zero-angle fast path did not integrate a
            trajectory and therefore has no trajectory point to return.
    """
    props = self._init_trajectory(shot_info)
    try:
        # Bypass optional-engine overrides while this API is limited to
        # the pure-Python solver path.
        angle, point = BaseIntegrationEngine._find_zero_newton(self, props, distance)
    except ZeroFindingError as error:
        logger.warning(f"Failed to find zero point using base iterative method: {error}")
        angle, point = BaseIntegrationEngine._find_zero_ridder(self, props, distance, False)
    if point is None:
        raise SolverRuntimeError("Zero-angle fast path did not evaluate a trajectory point")
    return angle, point

integrate

integrate(
    shot_info: Shot,
    max_range: Distance,
    dist_step: Distance | None = None,
    time_step: float = 0.0,
    filter_flags: TrajFlag | int = NONE,
    dense_output: bool = False,
    **kwargs,
) -> HitResult

Compute the trajectory for the given shot.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
max_range Distance

Maximum range of the trajectory (if float then treated as feet).

required
dist_step Distance | None

Distance step for recording RANGE TrajectoryData rows.

None
time_step float

Time step for recording trajectory data. Defaults to 0.0.

0.0
filter_flags TrajFlag | int

Flags to filter trajectory data. Defaults to TrajFlag.RANGE.

NONE
dense_output bool

If True, HitResult will save BaseTrajData for interpolating TrajectoryData.

False

Returns:

Type Description
HitResult

HitResult object for describing the trajectory.

Source code in py_ballisticcalc/engines/base_engine.py
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
def integrate(
    self,
    shot_info: Shot,
    max_range: Distance,
    dist_step: Distance | None = None,
    time_step: float = 0.0,
    filter_flags: TrajFlag | int = TrajFlag.NONE,
    dense_output: bool = False,
    **kwargs,
) -> HitResult:
    """Compute the trajectory for the given shot.

    Args:
        shot_info: The shot information.
        max_range: Maximum range of the trajectory (if float then treated as feet).
        dist_step: Distance step for recording RANGE TrajectoryData rows.
        time_step: Time step for recording trajectory data. Defaults to 0.0.
        filter_flags: Flags to filter trajectory data. Defaults to TrajFlag.RANGE.
        dense_output: If True, HitResult will save BaseTrajData for interpolating TrajectoryData.

    Returns:
        HitResult object for describing the trajectory.
    """
    props = self._init_trajectory(shot_info)
    props.filter_flags = filter_flags
    range_limit_ft = max_range >> Distance.Foot
    if dist_step is None:
        range_step_ft = range_limit_ft
    else:
        range_step_ft = dist_step >> Distance.Foot
    return self._integrate(props, range_limit_ft, range_step_ft, time_step, filter_flags, dense_output, **kwargs)

BaseEngineConfigDict

Bases: TypedDict

TypedDict for flexible engine configuration from dictionaries.

This TypedDict provides a flexible way to configure ballistic calculation engines using dictionary syntax. All fields are optional, allowing partial configuration where only specific parameters need to be overridden.

When used with create_base_engine_config(), any unspecified fields will use their default values from DEFAULT_BASE_ENGINE_CONFIG.

Note: All fields are Optional to support partial configuration.

Fields
  • cZeroFindingAccuracy: Maximum slant-error in feet for zero-finding precision.
  • cMaxIterations: Maximum iterations for convergence algorithms.
  • cMinimumAltitude: Minimum altitude in feet to continue calculation.
  • cMaximumDrop: Maximum drop in feet from muzzle to continue.
  • cMinimumVelocity: Minimum velocity in fps to continue calculation.
  • cGravityConstant: Gravitational acceleration in ft/s².
  • cStepMultiplier: Integration step size multiplier.

Examples:

>>> config_dict: BaseEngineConfigDict = {
...     'cMinimumVelocity': 100.0,
...     'cStepMultiplier': 0.8
... }
>>> config = create_base_engine_config(config_dict)
>>> # Using with Calculator
>>> from py_ballisticcalc import Calculator
>>> calc = Calculator(config=config_dict)
See Also
  • BaseEngineConfig: Type-safe dataclass version
  • create_base_engine_config: Factory function for BaseEngineConfig creation

cython+dopri selects CythonizedDormandPrinceIntegrationEngine when the optional compiled package is installed. relative_tolerance and absolute_tolerance default to 1e-6; get_step_stats() returns accepted and rejected steps. Its controller follows SciPy RK45 semantics, unlike compatibility-preserving Cash--Karp.

cython+tsitouras selects CythonizedTsitourasIntegrationEngine the same way. It is structurally identical to CythonizedDormandPrinceIntegrationEngine (same 7-stage FSAL shape, same SciPy-RK45-style controller, same relative_tolerance/ absolute_tolerance config) but uses the Tsitouras 5(4) tableau instead of Dormand-Prince's — measured to be in the same performance class as Dormand-Prince and Cash--Karp, not consistently faster, despite its smaller leading truncation-error coefficient; see benchmarks.

RK4IntegrationEngine

RK4IntegrationEngine(config: BaseEngineConfigDict | None)

Bases: BaseIntegrationEngine

Runge-Kutta 4th order integration engine for ballistic trajectory calculations.

Attributes:

Name Type Description
integration_step_count int

Number of integration steps performed.

Examples:

>>> config = BaseEngineConfigDict(cMinimumVelocity=0.0)
>>> engine = RK4IntegrationEngine(config)

Parameters:

Name Type Description Default
config BaseEngineConfigDict | None

Configuration dictionary containing engine parameters. See BaseEngineConfigDict for available options. Common settings include cStepMultiplier for accuracy control and cMinimumVelocity for termination conditions.

required

Examples:

>>> precise_config = BaseEngineConfigDict(
...     cStepMultiplier=0.5,  # Smaller steps
...     cMinimumVelocity=20.0  # Continue to lower velocities
... )
>>> precise_engine = RK4IntegrationEngine(precise_config)

Methods:

Name Description
get_calc_step

Get the calculation step size for RK4 integration.

Source code in py_ballisticcalc/engines/rk4.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def __init__(self, config: BaseEngineConfigDict | None) -> None:
    """Initialize the RK4 integration engine.

    Args:
        config: Configuration dictionary containing engine parameters.
               See [`BaseEngineConfigDict`](py_ballisticcalc.base_engine.BaseEngineConfigDict) for available options.
               Common settings include cStepMultiplier for accuracy control
               and cMinimumVelocity for termination conditions.

    Examples:
        >>> precise_config = BaseEngineConfigDict(
        ...     cStepMultiplier=0.5,  # Smaller steps
        ...     cMinimumVelocity=20.0  # Continue to lower velocities
        ... )
        >>> precise_engine = RK4IntegrationEngine(precise_config)
    """
    super().__init__(config)
    self.integration_step_count: int = 0
    self.trajectory_count = 0  # Number of trajectories calculated

get_calc_step

get_calc_step() -> float

Get the calculation step size for RK4 integration.

Returns:

Type Description
float

Time-step size (in seconds) for integration calculations.

Mathematical Context

The step size directly affects the accuracy and computational cost: - Smaller steps: Higher accuracy, more computation - Larger steps: Lower accuracy, faster computation - RK4's O(h⁵) error means accuracy improves rapidly with smaller h

Examples:

>>> config = BaseEngineConfigDict(cStepMultiplier=0.5)
>>> engine = RK4IntegrationEngine(config)
>>> engine.get_calc_step()
0.00125
Note

For RK4, the relationship between step size and accuracy is: - Halving the step size reduces error by ~32× (2⁵) - Default step size is sufficient to pass unit tests.

Source code in py_ballisticcalc/engines/rk4.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
@override
def get_calc_step(self) -> float:
    """Get the calculation step size for RK4 integration.

    Returns:
        Time-step size (in seconds) for integration calculations.

    Mathematical Context:
        The step size directly affects the accuracy and computational cost:
        - Smaller steps: Higher accuracy, more computation
        - Larger steps: Lower accuracy, faster computation
        - RK4's O(h⁵) error means accuracy improves rapidly with smaller h

    Examples:
        >>> config = BaseEngineConfigDict(cStepMultiplier=0.5)
        >>> engine = RK4IntegrationEngine(config)
        >>> engine.get_calc_step()
        0.00125

    Note:
        For RK4, the relationship between step size and accuracy is:
        - Halving the step size reduces error by ~32× (2⁵)
        - Default step size is sufficient to pass unit tests.
    """
    return super().get_calc_step() * self.DEFAULT_TIME_STEP

EulerIntegrationEngine

EulerIntegrationEngine(config: BaseEngineConfigDict | None)

Bases: BaseIntegrationEngine

Euler integration engine for ballistic trajectory calculations.

Attributes:

Name Type Description
DEFAULT_STEP

Default step size multiplier for integration (0.5).

integration_step_count int

Number of integration steps performed.

Examples:

>>> config = BaseEngineConfigDict(cMinimumVelocity=100.0)
>>> engine = EulerIntegrationEngine(config)

Parameters:

Name Type Description Default
config BaseEngineConfigDict | None

Configuration dictionary containing engine parameters. See [BaseEngineConfigDict][py_ballisticcalc.base_engine.BaseEngineConfigDict] for available options.

required

Methods:

Name Description
get_calc_step

Get the base calculation step size for Euler integration.

time_step

Calculate adaptive time step based on current projectile velocity.

Source code in py_ballisticcalc/engines/euler.py
66
67
68
69
70
71
72
73
74
75
def __init__(self, config: BaseEngineConfigDict | None) -> None:
    """Initialize the Euler integration engine.

    Args:
        config: Configuration dictionary containing engine parameters.
               See [`BaseEngineConfigDict`][py_ballisticcalc.base_engine.BaseEngineConfigDict] for available options.
    """
    super().__init__(config)
    self.integration_step_count: int = 0
    self.trajectory_count = 0  # Number of trajectories calculated

get_calc_step

get_calc_step() -> float

Get the base calculation step size for Euler integration.

Calculates the effective step size by combining the base engine step multiplier with the Euler-specific DEFAULT_STEP constant. The step size directly affects accuracy and performance trade-offs. This is a distance-like quantity that is subsequently scaled by velocity to produce a time-like integration step.

Returns:

Type Description
float

Base step size for integration calculations.

Note

The step size is calculated as: cStepMultiplier * DEFAULT_STEP. Smaller step sizes increase accuracy but require more computation. The DEFAULT_STEP is sufficient to pass all unit tests.

Source code in py_ballisticcalc/engines/euler.py
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
@override
def get_calc_step(self) -> float:
    """Get the base calculation step size for Euler integration.

    Calculates the effective step size by combining the base engine
    step multiplier with the Euler-specific DEFAULT_STEP constant.
    The step size directly affects accuracy and performance trade-offs.
    This is a distance-like quantity that is subsequently scaled by velocity
    to produce a time-like integration step.

    Returns:
        Base step size for integration calculations.

    Note:
        The step size is calculated as: `cStepMultiplier * DEFAULT_STEP`.
        Smaller step sizes increase accuracy but require more computation.
        The DEFAULT_STEP is sufficient to pass all unit tests.
    """
    return super().get_calc_step() * self.DEFAULT_TIME_STEP

time_step

time_step(base_step: float, velocity: float) -> float

Calculate adaptive time step based on current projectile velocity.

Implements adaptive time stepping where the time step is inversely related to projectile velocity. This helps maintain numerical stability and accuracy as the projectile slows down or speeds up.

Parameters:

Name Type Description Default
base_step float

Base step size from the integration engine.

required
velocity float

Current projectile velocity in fps.

required

Returns:

Type Description
float

Adaptive time step for the current integration step.

Formula

time_step = base_step / max(1.0, velocity)

Examples:

>>> config = BaseEngineConfigDict(cStepMultiplier=0.5)
>>> engine = EulerIntegrationEngine(config)
>>> engine.time_step(0.5, 2000.0)
0.00025
>>> engine.time_step(0.5, 100.0)
0.005
Note

The max(1.0, velocity) ensures that the time step never becomes excessively large, maintaining numerical stability even at very low velocities.

Source code in py_ballisticcalc/engines/euler.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def time_step(self, base_step: float, velocity: float) -> float:
    """Calculate adaptive time step based on current projectile velocity.

    Implements adaptive time stepping where the time step is inversely
    related to projectile velocity. This helps maintain numerical stability
    and accuracy as the projectile slows down or speeds up.

    Args:
        base_step: Base step size from the integration engine.
        velocity: Current projectile velocity in fps.

    Returns:
        Adaptive time step for the current integration step.

    Formula:
        time_step = base_step / max(1.0, velocity)

    Examples:
        >>> config = BaseEngineConfigDict(cStepMultiplier=0.5)
        >>> engine = EulerIntegrationEngine(config)
        >>> engine.time_step(0.5, 2000.0)
        0.00025
        >>> engine.time_step(0.5, 100.0)
        0.005

    Note:
        The max(1.0, velocity) ensures that the time step never becomes
        excessively large, maintaining numerical stability even at very
        low velocities.
    """
    return base_step / max(1.0, velocity)

VelocityVerletIntegrationEngine

VelocityVerletIntegrationEngine(
    config: BaseEngineConfigDict | None,
)

Bases: BaseIntegrationEngine

Velocity Verlet integration engine for ballistic trajectory calculations.

Algorithm Details

The method uses a two-stage approach: 1. Update position using current velocity and acceleration. 2. Update velocity using average of current and new acceleration. This ensures velocity and position remain properly synchronized and conserves the total energy of the system.

Attributes:

Name Type Description
DEFAULT_TIME_STEP

Default time step multiplier.

integration_step_count int

Number of integration steps performed.

See Also
  • RK4IntegrationEngine: Higher accuracy alternative
  • EulerIntegrationEngine: Simpler alternative
  • SciPyIntegrationEngine: Adaptive methods

Parameters:

Name Type Description Default
config BaseEngineConfigDict | None

Configuration dictionary containing engine parameters. See BaseEngineConfigDict for available options.

required

Examples:

>>> config = BaseEngineConfigDict(
...     cStepMultiplier=0.5,
...     cMinimumVelocity=10.0
... )
>>> engine = VelocityVerletIntegrationEngine(config)

Methods:

Name Description
get_calc_step

Get the calculation step size for Velocity Verlet integration.

Source code in py_ballisticcalc/engines/velocity_verlet.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def __init__(self, config: BaseEngineConfigDict | None) -> None:
    """Initialize the Velocity Verlet integration engine.

    Args:
        config: Configuration dictionary containing engine parameters.
               See [`BaseEngineConfigDict`](py_ballisticcalc.base_engine.BaseEngineConfigDict) for available options.

    Examples:
        >>> config = BaseEngineConfigDict(
        ...     cStepMultiplier=0.5,
        ...     cMinimumVelocity=10.0
        ... )
        >>> engine = VelocityVerletIntegrationEngine(config)
    """
    super().__init__(config)
    self.integration_step_count: int = 0

get_calc_step

get_calc_step() -> float

Get the calculation step size for Velocity Verlet integration.

Combines the base engine step multiplier with the Verlet-specific DEFAULT_TIME_STEP to determine the effective integration step size.

Returns:

Type Description
float

Effective step size for Velocity Verlet integration.

Formula

step_size = base_step_multiplier × DEFAULT_TIME_STEP

Note

The small DEFAULT_TIME_STEP value is chosen to ensure that this engine can pass all unit tests, despite most of them being highly dissipative rather than conservative of energy.

Source code in py_ballisticcalc/engines/velocity_verlet.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@override
def get_calc_step(self) -> float:
    """Get the calculation step size for Velocity Verlet integration.

    Combines the base engine step multiplier with the Verlet-specific
    DEFAULT_TIME_STEP to determine the effective integration step size.

    Returns:
        Effective step size for Velocity Verlet integration.

    Formula:
        step_size = base_step_multiplier × DEFAULT_TIME_STEP

    Note:
        The small DEFAULT_TIME_STEP value is chosen to ensure
        that this engine can pass all unit tests, despite most of them
        being highly dissipative rather than conservative of energy.
    """
    return super().get_calc_step() * self.DEFAULT_TIME_STEP

SciPyIntegrationEngine

SciPyIntegrationEngine(
    config: SciPyEngineConfigDict | None,
)

Bases: BaseIntegrationEngine

High-performance ballistic trajectory integration engine using SciPy's solve_ivp.

Examples:

>>> from py_ballisticcalc.engines.scipy_engine import SciPyIntegrationEngine, SciPyEngineConfigDict
>>>
>>> # High-precision configuration
>>> config = SciPyEngineConfigDict(
...     integration_method='DOP853',
...     relative_tolerance=1e-10,
...     absolute_tolerance=1e-12
... )
>>> engine = SciPyIntegrationEngine(config)
>>>
>>> # Using with Calculator
>>> from py_ballisticcalc import Calculator
>>> calc = Calculator(engine='scipy+dop853')
Note

Requires scipy and numpy packages. Install with: pip install py_ballisticcalc[scipy] or pip install scipy numpy

Sets up the engine with the provided configuration dictionary, initializing all necessary parameters for high-precision ballistic trajectory calculations. The configuration is converted to a structured format with appropriate defaults for any unspecified parameters.

Parameters:

Name Type Description Default
_config

Configuration dictionary containing engine parameters. Can include SciPy-specific options (integration_method, tolerances, max_time) as well as all standard BaseEngineConfigDict.

parameters (cMinimumVelocity, cStepMultiplier, etc.).

SciPy-specific parameters:
- integration_method: SciPy method ('RK45', 'DOP853', etc.)
- relative_tolerance: Relative error tolerance (rtol)
- absolute_tolerance: Absolute error tolerance (atol)
- max_time: Maximum simulation time in seconds

Standard ballistic parameters:
- cMinimumVelocity: Minimum velocity to continue calculation
- cStepMultiplier: Integration step size multiplier
- cGravityConstant: Gravitational acceleration
- And other BaseEngineConfigDict parameters
required

Raises:

Type Description
ImportError

If scipy or numpy packages are not available.

ValueError

If configuration contains invalid parameters.

Examples:

>>> config = SciPyEngineConfigDict(
...     integration_method='DOP853',
...     relative_tolerance=1e-10,
...     cMinimumVelocity=50.0
... )
>>> engine = SciPyIntegrationEngine(config)
Attributes Initialized
  • _config: Complete configuration with defaults applied
  • gravity_vector: Gravitational acceleration vector
  • integration_step_count: Counter for integration steps (debugging)
  • trajectory_count: Counter for calculated trajectories (debugging)
  • eval_points: list of evaluation points (debugging/analysis)
Note

The configuration is processed through create_scipy_engine_config() which applies defaults for any unspecified parameters. This ensures the engine always has a complete, valid configuration.

Methods:

Name Description
find_zero_angle

Find a zero angle with SciPy's brentq root solver.

find_zero_point

Find a zero point with SciPy's brentq root solver.

zero_angle

Find the lower zero angle, falling back from Newton to brentq.

zero_point

Return SciPy's zero solution and its already-evaluated terminal point.

Source code in py_ballisticcalc/engines/scipy_engine.py
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
@override
def __init__(self, config: SciPyEngineConfigDict | None) -> None:
    """Initialize the SciPy integration engine with configuration.

    Sets up the engine with the provided configuration dictionary, initializing
    all necessary parameters for high-precision ballistic trajectory calculations.
    The configuration is converted to a structured format with appropriate
    defaults for any unspecified parameters.

    Args:
        _config: Configuration dictionary containing engine parameters.
                Can include SciPy-specific options (integration_method,
                tolerances, max_time) as well as all standard [`BaseEngineConfigDict`](py_ballisticcalc.base_engine.BaseEngineConfigDict).

                parameters (cMinimumVelocity, cStepMultiplier, etc.).

                SciPy-specific parameters:
                - integration_method: SciPy method ('RK45', 'DOP853', etc.)
                - relative_tolerance: Relative error tolerance (rtol)
                - absolute_tolerance: Absolute error tolerance (atol)
                - max_time: Maximum simulation time in seconds

                Standard ballistic parameters:
                - cMinimumVelocity: Minimum velocity to continue calculation
                - cStepMultiplier: Integration step size multiplier
                - cGravityConstant: Gravitational acceleration
                - And other BaseEngineConfigDict parameters

    Raises:
        ImportError: If scipy or numpy packages are not available.
        ValueError: If configuration contains invalid parameters.

    Examples:
        >>> config = SciPyEngineConfigDict(
        ...     integration_method='DOP853',
        ...     relative_tolerance=1e-10,
        ...     cMinimumVelocity=50.0
        ... )
        >>> engine = SciPyIntegrationEngine(config)

    Attributes Initialized:
        - _config: Complete configuration with defaults applied
        - gravity_vector: Gravitational acceleration vector
        - integration_step_count: Counter for integration steps (debugging)
        - trajectory_count: Counter for calculated trajectories (debugging)
        - eval_points: list of evaluation points (debugging/analysis)

    Note:
        The configuration is processed through create_scipy_engine_config()
        which applies defaults for any unspecified parameters. This ensures
        the engine always has a complete, valid configuration.
    """

    # dependencies guard
    if not _HAS_NUMPY:
        raise ImportError("Numpy is required for SciPyIntegrationEngine.")
    if not _HAS_SCIPY:
        raise ImportError("SciPy is required for SciPyIntegrationEngine.")

    self._config: SciPyEngineConfig = create_scipy_engine_config(config)  # type: ignore
    self.gravity_vector: Vector = Vector(0.0, self._config.cGravityConstant, 0.0)
    self.integration_step_count = 0  # Number of evaluations of diff_eq during ._integrate()
    self.trajectory_count = 0  # Number of trajectories calculated
    self.eval_points: list[float] = []  # Points at which diff_eq is called

find_zero_angle

find_zero_angle(
    shot_info: Shot,
    distance: Distance,
    lofted: bool = False,
) -> Angular

Find a zero angle with SciPy's brentq root solver.

Source code in py_ballisticcalc/engines/scipy_engine.py
819
820
821
822
823
@override
def find_zero_angle(self, shot_info: Shot, distance: Distance, lofted: bool = False) -> Angular:
    """Find a zero angle with SciPy's ``brentq`` root solver."""
    props = self._init_trajectory(shot_info)
    return self._find_zero_brentq(props, distance, lofted)[0]

find_zero_point

find_zero_point(
    shot_info: Shot,
    distance: Distance,
    lofted: bool = False,
) -> tuple[Angular, TrajectoryData]

Find a zero point with SciPy's brentq root solver.

Source code in py_ballisticcalc/engines/scipy_engine.py
825
826
827
828
829
830
831
832
833
834
@override
def find_zero_point(
    self, shot_info: Shot, distance: Distance, lofted: bool = False
) -> tuple[Angular, TrajectoryData]:
    """Find a zero point with SciPy's ``brentq`` root solver."""
    props = self._init_trajectory(shot_info)
    angle, point = self._find_zero_brentq(props, distance, lofted)
    if point is None:
        raise SolverRuntimeError("Zero-angle fast path did not evaluate a trajectory point")
    return angle, point

zero_angle

zero_angle(shot_info: Shot, distance: Distance) -> Angular

Find the lower zero angle, falling back from Newton to brentq.

Source code in py_ballisticcalc/engines/scipy_engine.py
836
837
838
839
840
841
842
843
844
@override
def zero_angle(self, shot_info: Shot, distance: Distance) -> Angular:
    """Find the lower zero angle, falling back from Newton to ``brentq``."""
    props = self._init_trajectory(shot_info)
    try:
        return self._find_zero_newton(props, distance)[0]
    except ZeroFindingError as error:
        logger.warning(f"Failed to find zero angle using Newton method: {error}")
        return self._find_zero_brentq(props, distance)[0]

zero_point

zero_point(
    shot_info: Shot, distance: Distance
) -> tuple[Angular, TrajectoryData]

Return SciPy's zero solution and its already-evaluated terminal point.

Unlike the base implementation, this directly uses the root_scalar solver implemented by :meth:_find_zero_brentq.

Source code in py_ballisticcalc/engines/scipy_engine.py
846
847
848
849
850
851
852
853
854
855
856
857
@override
def zero_point(self, shot_info: Shot, distance: Distance) -> tuple[Angular, TrajectoryData]:
    """Return SciPy's zero solution and its already-evaluated terminal point.

    Unlike the base implementation, this directly uses the ``root_scalar``
    solver implemented by :meth:`_find_zero_brentq`.
    """
    props = self._init_trajectory(shot_info)
    angle, point = self._find_zero_brentq(props, distance)
    if point is None:
        raise SolverRuntimeError("Zero-angle fast path did not evaluate a trajectory point")
    return angle, point

SciPyEngineConfigDict

Bases: BaseEngineConfigDict

TypedDict for flexible SciPy integration engine configuration.

This TypedDict provides a flexible dictionary-based interface for configuring the SciPy integration engine. All fields are optional (total=False), allowing partial configuration with automatic fallback to default values.

Attributes:

Name Type Description
max_time float

Maximum simulation time in seconds before terminating integration. Prevents runaway calculations in edge cases. If not specified, uses DEFAULT_MAX_TIME (90.0 seconds).

relative_tolerance float

Relative tolerance for integration error control (rtol). Controls the relative accuracy of the numerical solution. Smaller values provide higher precision at computational cost. If not specified, uses DEFAULT_RELATIVE_TOLERANCE (1e-8).

absolute_tolerance float

Absolute tolerance for integration error control (atol). Controls absolute accuracy, particularly important near zero. Smaller values improve precision for small quantities. If not specified, uses DEFAULT_ABSOLUTE_TOLERANCE (1e-6).

integration_method INTEGRATION_METHOD

SciPy solve_ivp integration method selection. If not specified, uses DEFAULT_INTEGRATION_METHOD ('RK45').

Examples:

>>> # Minimal configuration - uses defaults for unspecified fields
>>> config: SciPyEngineConfigDict = {
...     'integration_method': 'DOP853'  # High precision
... }

SciPyIntegrationEngineFactory

SciPyIntegrationEngineFactory(
    method: INTEGRATION_METHOD,
) -> Callable[
    [SciPyEngineConfigDict | None], SciPyIntegrationEngine
]

Make an engine factory with the SciPy solve_ivp integration method preset.

The returned callable matches EngineFactoryProtocol; integration_method from the passed config is overridden by method.

Examples:

>>> calc = Calculator(engine=SciPyIntegrationEngineFactory("DOP853"))
Source code in py_ballisticcalc/engines/scipy_engine.py
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
def SciPyIntegrationEngineFactory(
    method: INTEGRATION_METHOD,
) -> Callable[[SciPyEngineConfigDict | None], SciPyIntegrationEngine]:
    """Make an engine factory with the SciPy `solve_ivp` integration method preset.

    The returned callable matches `EngineFactoryProtocol`; `integration_method` from the
    passed config is overridden by `method`.

    Examples:
        >>> calc = Calculator(engine=SciPyIntegrationEngineFactory("DOP853"))
    """

    @wraps(SciPyIntegrationEngine, updated=())  # keep __doc__/__module__/__wrapped__ (signature) of the engine
    def factory(config: SciPyEngineConfigDict | None = None) -> SciPyIntegrationEngine:
        return SciPyIntegrationEngine({**(config or {}), "integration_method": method})  # type: ignore[typeddict-item]

    factory.__name__ = factory.__qualname__ = f"SciPyIntegrationEngine[{method}]"
    return factory

CythonizedBaseIntegrationEngine

CythonizedBaseIntegrationEngine(
    config: BaseEngineConfigDict | None,
)

Bases: EngineProtocol

Base Cython wrapper for C/С++ based binary engine. Implements EngineProtocol

Parameters:

Name Type Description Default
config BaseEngineConfig

The engine configuration.

required
IMPORTANT

Avoid calling Python functions inside __init__! __init__ is called after __cinit__, so any memory allocated in __cinit__ that is not referenced in Python will be leaked if __init__ raises an exception.

Methods:

Name Description
find_max_range

Finds the maximum range along shot_info.look_angle,

find_zero_angle

Finds the barrel elevation needed to hit sight line at a specific distance,

find_zero_point

Find a zero trajectory and return its solved angle and terminal point.

find_apex

Finds the apex of the trajectory, where apex is defined as the point

zero_angle

Finds the barrel elevation needed to hit sight line at a specific distance.

zero_point

Return the zero angle and the trajectory point used to determine it.

integrate

Integrates the trajectory for the given shot.

integrate_raw_at

Integrates the trajectory until a specified attribute reaches a target value

Attributes:

Name Type Description
integration_step_count int

Gets the number of integration steps performed in the last integration.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
29
30
31
32
33
34
35
36
37
38
39
40
def __init__(self, config: BaseEngineConfigDict | None) -> None:
    """
    Initializes the engine with the given configuration.

    Args:
        config (BaseEngineConfig): The engine configuration.

    IMPORTANT:
        Avoid calling Python functions inside `__init__`!
        `__init__` is called after `__cinit__`, so any memory allocated in `__cinit__`
        that is not referenced in Python will be leaked if `__init__` raises an exception.
    """

integration_step_count property

integration_step_count: int

Gets the number of integration steps performed in the last integration.

Returns:

Name Type Description
int int

The number of integration steps.

find_max_range

find_max_range(
    shot_info: Shot,
    angle_bracket_deg: tuple[float, float] = (0, 90),
) -> tuple[Distance, Angular]

Finds the maximum range along shot_info.look_angle, and the launch angle to reach it.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
angle_bracket_deg tuple[float, float]

The angle bracket in degrees to search for max range. Defaults to (0, 90).

(0, 90)

Returns:

Type Description
tuple[Distance, Angular]

tuple[Distance, Angular]: The maximum slant range and the launch angle to reach it.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def find_max_range(
    self, shot_info: Shot, angle_bracket_deg: tuple[float, float] = (0, 90)
) -> tuple[Distance, Angular]:
    """
    Finds the maximum range along shot_info.look_angle,
    and the launch angle to reach it.

    Args:
        shot_info (Shot): The shot information.
        angle_bracket_deg (tuple[float, float], optional):
            The angle bracket in degrees to search for max range. Defaults to (0, 90).

    Returns:
        tuple[Distance, Angular]: The maximum slant range and the launch angle to reach it.
    """

find_zero_angle

find_zero_angle(
    shot_info: Shot,
    distance: Distance,
    lofted: bool = False,
) -> Angular

Finds the barrel elevation needed to hit sight line at a specific distance, using unimodal root-finding that is guaranteed to succeed if a solution exists.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
distance Distance

The distance to the target.

required
lofted bool

Whether the shot is lofted.

False

Returns:

Name Type Description
Angular Angular

The required barrel elevation angle.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
84
85
86
87
88
89
90
91
92
93
94
95
96
def find_zero_angle(self, shot_info: Shot, distance: Distance, lofted: bool = False) -> Angular:
    """
    Finds the barrel elevation needed to hit sight line at a specific distance,
    using unimodal root-finding that is guaranteed to succeed if a solution exists.

    Args:
        shot_info (Shot): The shot information.
        distance (Distance): The distance to the target.
        lofted (bool): Whether the shot is lofted.

    Returns:
        Angular: The required barrel elevation angle.
    """

find_zero_point

find_zero_point(
    shot_info: Shot,
    distance: Distance,
    lofted: bool = False,
) -> tuple[Angular, TrajectoryData]

Find a zero trajectory and return its solved angle and terminal point.

The returned point is the terminal RANGE point evaluated by the successful zero-finding iteration. No final trajectory integration is performed after the angle is found.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
distance Distance

Slant distance to the target.

required
lofted bool

If True, find the higher trajectory that hits the zero point.

False

Returns:

Type Description
Angular

The solved barrel elevation and the terminal RANGE point from the

TrajectoryData

successful zero-finding iteration.

Raises:

Type Description
SolverRuntimeError

If a zero-angle fast path did not integrate a trajectory and therefore has no trajectory point to return.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def find_zero_point(
    self, shot_info: Shot, distance: Distance, lofted: bool = False
) -> tuple[Angular, TrajectoryData]:
    """Find a zero trajectory and return its solved angle and terminal point.

    The returned point is the terminal ``RANGE`` point evaluated by the
    successful zero-finding iteration. No final trajectory integration is
    performed after the angle is found.

    Args:
        shot_info: The shot information.
        distance: Slant distance to the target.
        lofted: If True, find the higher trajectory that hits the zero point.

    Returns:
        The solved barrel elevation and the terminal RANGE point from the
        successful zero-finding iteration.

    Raises:
        SolverRuntimeError: If a zero-angle fast path did not integrate a
            trajectory and therefore has no trajectory point to return.
    """

find_apex

find_apex(shot_info: Shot) -> TrajectoryData

Finds the apex of the trajectory, where apex is defined as the point where the vertical component of velocity goes from positive to negative.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required

Returns:

Name Type Description
TrajectoryData TrajectoryData

The trajectory data at the apex.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
121
122
123
124
125
126
127
128
129
130
131
def find_apex(self, shot_info: Shot) -> TrajectoryData:
    """
    Finds the apex of the trajectory, where apex is defined as the point
    where the vertical component of velocity goes from positive to negative.

    Args:
        shot_info (Shot): The shot information.

    Returns:
        TrajectoryData: The trajectory data at the apex.
    """

zero_angle

zero_angle(shot_info: Shot, distance: Distance) -> Angular

Finds the barrel elevation needed to hit sight line at a specific distance. First tries iterative approach; if that fails falls back on _find_zero_angle.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
distance Distance

The distance to the target.

required

Returns:

Name Type Description
Angular Angular

Barrel elevation to hit height zero at zero distance along sight line

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
133
134
135
136
137
138
139
140
141
142
143
144
def zero_angle(self, shot_info: Shot, distance: Distance) -> Angular:
    """
    Finds the barrel elevation needed to hit sight line at a specific distance.
    First tries iterative approach; if that fails falls back on _find_zero_angle.

    Args:
        shot_info (Shot): The shot information.
        distance (Distance): The distance to the target.

    Returns:
        Angular: Barrel elevation to hit height zero at zero distance along sight line
    """

zero_point

zero_point(
    shot_info: Shot, distance: Distance
) -> tuple[Angular, TrajectoryData]

Return the zero angle and the trajectory point used to determine it.

This is the terminal RANGE point from the successful zero-finding iteration. It performs no final re-integration after finding the angle, so callers receive both the zero geometry and its ballistic state from one solve.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
distance Distance

Slant distance to the zero point.

required

Returns:

Type Description
Angular

A pair of the lower-arc barrel elevation and the trajectory point

TrajectoryData

from the successful zero-finding iteration.

Raises:

Type Description
SolverRuntimeError

If a zero-angle fast path did not integrate a trajectory and therefore has no trajectory point to return.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def zero_point(self, shot_info: Shot, distance: Distance) -> tuple[Angular, TrajectoryData]:
    """Return the zero angle and the trajectory point used to determine it.

    This is the terminal ``RANGE`` point from the successful zero-finding
    iteration. It performs no final re-integration after finding the
    angle, so callers receive both the zero geometry and its ballistic
    state from one solve.

    Args:
        shot_info: The shot information.
        distance: Slant distance to the zero point.

    Returns:
        A pair of the lower-arc barrel elevation and the trajectory point
        from the successful zero-finding iteration.

    Raises:
        SolverRuntimeError: If a zero-angle fast path did not integrate a
            trajectory and therefore has no trajectory point to return.
    """

integrate

integrate(
    shot_info: Shot,
    max_range: Distance,
    dist_step: Distance | None = None,
    time_step: float = 0.0,
    filter_flags: TrajFlag | int = 0,
    dense_output: bool = False,
    **kwargs: Any,
) -> HitResult

Integrates the trajectory for the given shot.

Parameters:

Name Type Description Default
shot_info Shot

The shot information.

required
max_range Distance

Maximum range of the trajectory (if float then treated as feet).

required
dist_step Distance | None

Distance step for recording RANGE TrajectoryData rows.

None
time_step float

Time step for recording trajectory data. Defaults to 0.0.

0.0
filter_flags TrajFlag | int

Flags to filter trajectory data. Defaults to TrajFlag.RANGE.

0
dense_output bool

If True, HitResult will save BaseTrajData for interpolating TrajectoryData.

False

Returns:

Name Type Description
HitResult HitResult

Object for describing the trajectory.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def integrate(
    self,
    shot_info: Shot,
    max_range: Distance,
    dist_step: Distance | None = None,
    time_step: float = 0.0,
    filter_flags: TrajFlag | int = 0,
    dense_output: bool = False,
    **kwargs: Any,
) -> HitResult:
    """
    Integrates the trajectory for the given shot.

    Args:
        shot_info (Shot): The shot information.
        max_range (Distance):
            Maximum range of the trajectory (if float then treated as feet).
        dist_step (Distance | None):
            Distance step for recording RANGE TrajectoryData rows.
        time_step (float, optional):
            Time step for recording trajectory data. Defaults to 0.0.
        filter_flags (TrajFlag | int, optional):
            Flags to filter trajectory data. Defaults to TrajFlag.RANGE.
        dense_output (bool, optional):
            If True, HitResult will save BaseTrajData for interpolating TrajectoryData.

    Returns:
        HitResult: Object for describing the trajectory.
    """

integrate_raw_at

integrate_raw_at(
    shot_info: Shot, key_attribute: str, target_value: float
) -> tuple[CythonizedBaseTrajData, TrajectoryData]

Integrates the trajectory until a specified attribute reaches a target value and returns the interpolated data point.

This method initializes the trajectory using the provided shot information, performs integration using the underlying C++ engine's 'integrate_at' function, and handles the conversion of C++ results back to Python objects.

Parameters:

Name Type Description Default
shot_info object

Information required to initialize the trajectory (e.g., muzzle velocity, drag model).

required
key_attribute str

The name of the attribute to track, such as 'time', 'mach', or a vector component like 'position.z'.

required
target_value float

The value the 'key_attribute' must reach for the integration to stop and interpolation to occur.

required

Returns:

Type Description
tuple[CythonizedBaseTrajData, TrajectoryData]

tuple[CythonizedBaseTrajData, TrajectoryData]: A tuple containing: - CythonizedBaseTrajData: The interpolated raw data point. - TrajectoryData: The fully processed trajectory data point.

Raises:

Type Description
InterceptionError

If the underlying C++ integration fails to find the target point (e.g., due to insufficient range or data issues).

SolverRuntimeError

If some other internal error occured

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def integrate_raw_at(
    self, shot_info: Shot, key_attribute: str, target_value: float
) -> tuple[CythonizedBaseTrajData, TrajectoryData]:
    """
    Integrates the trajectory until a specified attribute reaches a target value
    and returns the interpolated data point.

    This method initializes the trajectory using the provided shot information,
    performs integration using the underlying C++ engine's 'integrate_at' function,
    and handles the conversion of C++ results back to Python objects.

    Args:
        shot_info (object): Information required to initialize the trajectory
            (e.g., muzzle velocity, drag model).
        key_attribute (str): The name of the attribute to track, such as
            'time', 'mach', or a vector component like 'position.z'.
        target_value (float): The value the 'key_attribute' must reach for
            the integration to stop and interpolation to occur.

    Returns:
        tuple[CythonizedBaseTrajData, TrajectoryData]:
            A tuple containing:
            - CythonizedBaseTrajData: The interpolated raw data point.
            - TrajectoryData: The fully processed trajectory data point.

    Raises:
        InterceptionError: If the underlying C++ integration fails to find
            the target point (e.g., due to insufficient range or data issues).
        SolverRuntimeError: If some other internal error occured
    """

CythonizedRK4IntegrationEngine

CythonizedRK4IntegrationEngine(
    config: BaseEngineConfigDict | None,
)

Bases: CythonizedBaseIntegrationEngine[BaseEngineConfigDict]

Cythonized RK4 (Runge-Kutta 4th order) integration engine for ballistic calculations.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
29
30
31
32
33
34
35
36
37
38
39
40
def __init__(self, config: BaseEngineConfigDict | None) -> None:
    """
    Initializes the engine with the given configuration.

    Args:
        config (BaseEngineConfig): The engine configuration.

    IMPORTANT:
        Avoid calling Python functions inside `__init__`!
        `__init__` is called after `__cinit__`, so any memory allocated in `__cinit__`
        that is not referenced in Python will be leaked if `__init__` raises an exception.
    """

CythonizedEulerIntegrationEngine

CythonizedEulerIntegrationEngine(
    config: BaseEngineConfigDict | None,
)

Bases: CythonizedBaseIntegrationEngine[BaseEngineConfigDict]

Cythonized Euler integration engine for ballistic calculations.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
29
30
31
32
33
34
35
36
37
38
39
40
def __init__(self, config: BaseEngineConfigDict | None) -> None:
    """
    Initializes the engine with the given configuration.

    Args:
        config (BaseEngineConfig): The engine configuration.

    IMPORTANT:
        Avoid calling Python functions inside `__init__`!
        `__init__` is called after `__cinit__`, so any memory allocated in `__cinit__`
        that is not referenced in Python will be leaked if `__init__` raises an exception.
    """

CythonizedVelocityVerletIntegrationEngine

CythonizedVelocityVerletIntegrationEngine(
    config: BaseEngineConfigDict | None,
)

Bases: CythonizedBaseIntegrationEngine[BaseEngineConfigDict]

Cythonized Velocity Verlet integration engine for ballistic calculations.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/base_engine.pyi
29
30
31
32
33
34
35
36
37
38
39
40
def __init__(self, config: BaseEngineConfigDict | None) -> None:
    """
    Initializes the engine with the given configuration.

    Args:
        config (BaseEngineConfig): The engine configuration.

    IMPORTANT:
        Avoid calling Python functions inside `__init__`!
        `__init__` is called after `__cinit__`, so any memory allocated in `__cinit__`
        that is not referenced in Python will be leaked if `__init__` raises an exception.
    """

CythonizedCashKarpIntegrationEngine

CythonizedCashKarpIntegrationEngine(
    config: CashKarpEngineConfig
    | BaseEngineConfigDict
    | None = None,
)

Bases: CythonizedBaseIntegrationEngine[BaseEngineConfigDict]

Cythonized Cash-Karp (embedded adaptive RK45) integration engine for ballistic calculations.

The scalar relative_tolerance and absolute_tolerance config keys control the embedded local-error estimate with solve_ivp semantics. Both default to 1e-6.

Methods:

Name Description
get_step_stats

Returns (accepted, rejected) step counts from the most recent integration call.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/engines.pyi
76
77
def __init__(self, config: CashKarpEngineConfig | BaseEngineConfigDict | None = None) -> None:
    """Initialize with standard options and optional SciPy-style tolerances."""

get_step_stats

get_step_stats() -> tuple[int, int]

Returns (accepted, rejected) step counts from the most recent integration call.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/engines.pyi
79
80
def get_step_stats(self) -> tuple[int, int]:
    """Returns (accepted, rejected) step counts from the most recent integration call."""

CythonizedDormandPrinceIntegrationEngine

CythonizedDormandPrinceIntegrationEngine(
    config: DormandPrinceEngineConfig
    | BaseEngineConfigDict
    | None = None,
)

Bases: CythonizedBaseIntegrationEngine[BaseEngineConfigDict]

Cythonized Dormand-Prince 5(4) integration engine with solve_ivp-style tolerances.

Methods:

Name Description
get_step_stats

Returns (accepted, rejected) step counts from the most recent integration call.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/engines.pyi
101
def __init__(self, config: DormandPrinceEngineConfig | BaseEngineConfigDict | None = None) -> None: ...

get_step_stats

get_step_stats() -> tuple[int, int]

Returns (accepted, rejected) step counts from the most recent integration call.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/engines.pyi
102
103
def get_step_stats(self) -> tuple[int, int]:
    """Returns (accepted, rejected) step counts from the most recent integration call."""

CythonizedTsitourasIntegrationEngine

CythonizedTsitourasIntegrationEngine(
    config: TsitourasEngineConfig
    | BaseEngineConfigDict
    | None = None,
)

Bases: CythonizedBaseIntegrationEngine[BaseEngineConfigDict]

Cythonized Tsitouras 5(4) integration engine with solve_ivp-style tolerances.

Methods:

Name Description
get_step_stats

Returns (accepted, rejected) step counts from the most recent integration call.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/engines.pyi
124
def __init__(self, config: TsitourasEngineConfig | BaseEngineConfigDict | None = None) -> None: ...

get_step_stats

get_step_stats() -> tuple[int, int]

Returns (accepted, rejected) step counts from the most recent integration call.

Source code in py_ballisticcalc.exts/py_ballisticcalc_exts/engines.pyi
125
126
def get_step_stats(self) -> tuple[int, int]:
    """Returns (accepted, rejected) step counts from the most recent integration call."""