Source code for archeryutils.handicaps.handicap_scheme_agb

"""Code for handicap calculations using Archery GB handicap schemes.

Extended Summary
----------------
Code to calculate information using a number of schemes:

- New Archery GB (J Atkinson, 2023)
- Old Archery GB (D Lane)

Routine Listings
----------------
- HandicapAGB Class
- HandicapAGBold Class

References
----------
- New AGB: Atkinson, J
- Old AGB: The construction of the graduated handicap tables for target
  archery
  Lane, D (2013)
  https://www.jackatkinson.net/files/Handicap_Tables_2013.pdf

"""

import numpy as np
import numpy.typing as npt

from .handicap_scheme import HandicapScheme, _cast_float_array


[docs] class HandicapAGB(HandicapScheme): """ Class to represent the Archery GB handicap scheme. This class represents the 2023 handicap scheme by Jack Atkinson. Parameters ---------- datum : float offset to set scratch point, default = 6.0, step : float percentage change in deviation per handicap step, default = 3.5, ang_0 : float datum angle used in scheme, default = 5.0e-4, kd : float factor controlling excess dispersion scaling with distance, default = 0.00365, Attributes ---------- name : str="AGB" The name of the handicap scheme. arw_d_out : float diameter of an outdoor arrow: 5.5e-3 [metres] arw_d_in: float diameter of an indoor arrow 9.3e-3 [metres] Warnings -------- Using non-default values for the kwargs may produce results that do not match the official values for this scheme. Only change if you are experimenting and know what you are doing! See Also -------- HandicapScheme : The base class for a handicap scheme from which this is subclassed containing details of additional methods. """ def __init__( self, datum: float = 6.0, step: float = 3.5, ang_0: float = 5.0e-4, kd: float = 0.00365, ): super().__init__() self.name: str = "AGB" self.arw_d_out: float = 5.5e-3 self.arw_d_in: float = 9.3e-3 # AGB Uses a descending scale with ceil. All numbers typically in [-75, 300] self.desc_scale: bool = True self.scale_bounds: npt.NDArray[np.float64] = np.array([-75, 300]) self.max_score_rounding_lim: float = 1.0 self.params = { "datum": datum, "step": step, "ang_0": ang_0, "kd": kd, }
[docs] def sigma_t(self, handicap: npt.ArrayLike, dist: float) -> npt.NDArray[np.float64]: """Calculate angular deviation for given handicap and distance. Parameters ---------- handicap : ArrayLike handicap(s) to calculate sigma_t at dist : float distance to target [metres] Returns ------- sig_t : NDArray[np.float64] angular deviation [rad] Examples -------- Angular deviation at a distance of 25m, using the AGB handicap system at a handicap of 10 can be calculated with: >>> import archeryutils.handicaps as hc >>> agb_scheme = hc.handicap_scheme("AGB") >>> agb_scheme.sigma_t(10.0, 25.0) 0.0009498280098103058 It can also be passed an array of handicaps: >>> import numpy as np >>> agb_scheme.sigma_t(np.array([10.0, 50.0, 100.0]), 25.0) array([0.00094983, 0.00376062, 0.02100276]) """ handicap = _cast_float_array(handicap) # Cast for calculation return ( self.params["ang_0"] * ((1.0 + self.params["step"] / 100.0) ** (handicap + self.params["datum"])) * np.exp(self.params["kd"] * dist) )
# Override rounding method for AGB to always round up to next highest score. @staticmethod def _rounded_score(score): return np.ceil(score)
[docs] class HandicapAGBold(HandicapScheme): """ Class to represent the old (pre-2023) Archery GB handicap scheme by D. Lane. Parameters ---------- datum : float offset to set scratch point, default = 12.9, step : float percentage change in deviation per handicap step, default = 3.6, ang_0 : float datum angle used in scheme, default = 5.0e-4, k1 : float constant k1 in the handicap equation, default = 1.429e-6, k2 : float constant k2 in the handicap equation, default = 1.07, k3 : float constant k3 in the handicap equation, default = 4.3, p1 : float exponent of distance scaling, default = 2.0, Attributes ---------- name : str="AGBold" The name of the handicap scheme. arw_d_out : float diameter of an outdoor arrow: 7.14e-3 [metres] arw_d_in: float diameter of an indoor arrow 7.14e-3 [metres] Warnings -------- Using non-default values for the kwargs may produce results that do not match the official values for this scheme. Only change if you are experimenting and know what you are doing! See Also -------- HandicapScheme : The base class for a handicap scheme from which this is subclassed containing details of additional methods. """ def __init__( # noqa: PLR0913 - Too many arguments self, datum: float = 12.9, step: float = 3.6, ang_0: float = 5.0e-4, k1: float = 1.429e-6, k2: float = 1.07, k3: float = 4.3, p1: float = 2.0, ): super().__init__() self.name: str = "AGBold" self.arw_d_out: float = 7.14e-3 self.arw_d_in: float = 7.14e-3 # AGBold Uses a descending scale with round. All numbers typically in [-75, 300] self.desc_scale: bool = True self.scale_bounds: npt.NDArray[np.float64] = np.array([-75, 300]) self.max_score_rounding_lim: float = 0.5 self.params = { "datum": datum, # Offset required to set handicap 0 at desired score. "step": step, # Percentage change in group size for each handicap step. "ang_0": ang_0, # Baseline angle used for group size 0.5 [millirad]. "k1": k1, # Constant 1 used in handicap equation. "k2": k2, # Constant 2 used in handicap equation. "k3": k3, # Constant 3 used in handicap equation. "p1": p1, # Exponent of distance scaling. }
[docs] def sigma_t(self, handicap: npt.ArrayLike, dist: float) -> npt.NDArray[np.float64]: """Calculate angular deviation for given handicap and distance. Parameters ---------- handicap : ArrayLike handicap(s) to calculate sigma_t at dist : float distance to target [metres] Returns ------- sig_t : NDArray[np.float64] angular deviation [rad] Notes ----- This is the key part of this scheme. The values are taken from Lane (2013) [1]_. References ---------- .. [1] Lane, D. (2013). "The Construction of the Graduated Handicap Tables for Target Archery." Examples -------- Angular deviation at a distance of 25m, using the AGBold handicap system at a handicap of 10 can be calculated with: >>> import archeryutils.handicaps as hc >>> agbold_scheme = hc.handicap_scheme("AGBold") >>> agbold_scheme.sigma_t(10.0, 25.0) 0.001126491382794861 It can also be passed an array of handicaps: >>> import numpy as np >>> agbold_scheme.sigma_t(np.array([10.0, 50.0, 100.0]), 25.0) array([0.00112649, 0.00478762, 0.05520862]) """ handicap = _cast_float_array(handicap) # Cast for calculation k_factor = self.params["k1"] * self.params["k2"] ** ( handicap + self.params["k3"] ) f_factor = 1.0 + k_factor * dist ** self.params["p1"] return ( self.params["ang_0"] * ((1.0 + self.params["step"] / 100.0) ** (handicap + self.params["datum"])) * f_factor )