This page contains a few quick examples of how you can use archeryutils. For full details and functionalities you should consult the rest of the documentation.
The source repository contains a notebook of examples exercises.ipynb which is very similar to the examples on this page. This is the perfect place to learn the basic functionalities of archeryutils and experiment yourself with what the code can do. We advise starting by running this.
To do so online you can access the Binder instance we have set up. This will spin up an interactive online session in which you can run the code.
Alternatively you can run it locally in your browser by downloading the notebook and running:
pip install jupyter jupyter notebook examples.ipynb
To import the archeryutils package use:
.. ipython:: python
import archeryutils as au
The most basic building block of archeryutils is the :py:class:`archeryutils.Target` class which represents a target with a particular target face at a certain distance.
It can be invoked by specifying a scoring system, face size (cm) and distance (m):
.. ipython:: python
my720target = au.Target("10_zone", 122, 70.0)
mycompound720target = au.Target("10_zone_6_ring", 80, 50.0)
In more complicated cases specific units can be passed in with the diameter and distance as a plain tuple, or (recommeneded for clarity) as a :py:class:`~archeryutils.targets.Quantity`:
.. ipython:: python
myWorcesterTarget = au.Target(
"Worcester", diameter=(16, "inches"), distance=(20.0, "yards"), indoor=True
)
myIFAATarget = au.Target(
"IFAA_field", diameter=80, distance=au.Quantity(80.0, "yards")
)
If the target you want is not supported, you can manually supply the target ring sizes and scores as a FaceSpec and construct a target as so.
.. ipython:: python
# Kings of archery recurve scoring target
face_spec = {8: 10, 12: 8, 16: 7, 20: 6}
myKingsTarget = au.Target.from_face_spec((face_spec, "cm"), 40, 18, indoor=True)
print(myKingsTarget.scoring_system)
Note
Although we can provide the face_spec in any unit listed under :py:attr:`~archeryutils.Target._supported_diameter_units`, the sizes in the specification are converted to metres and stored in this form. Therefore unlike the target diameter paramater, the default unit for specifications is [metres]
The only limitations to the target faces you can represent in this way are:
- Targets must be formed of concentric rings
- The score must monotonically decrease as the rings get larger
Under the hood, all standard scoring systems autogenerate their own FaceSpec and this is used internally when calculating handicaps and classifications. You can see this stored under the :py:attr:`~archeryutils.Target.face_spec` property:
.. ipython:: python
print(my720target.face_spec)
The target features max_score() and min_score() methods:
.. ipython:: python
for target in [
my720target,
mycompound720target,
myIFAATarget,
myWorcesterTarget,
myKingsTarget,
]:
print(
f"{target.scoring_system} has max score {target.max_score()} ",
f"and min score {target.min_score()}.",
)
The next unit up is the :py:class:`archeryutils.Pass` - a number of arrows shot at a target:
.. ipython:: python
my70mPass = au.Pass(36, my720target)
print(my70mPass.max_score())
We can also bypass the Target class and directly construct our Pass using the at_target constructor
.. ipython:: python
my70mPass = au.Pass.at_target(36, "10_zone", 122, 70.0)
Finally we have the :py:class:`archeryutils.Round` class made up of a number of Passes.
It may also take the following optional string arguments:
location- where the round is shot, e.g. 'Indoor', 'Outdoor', 'Field' etc.body- The governing body the round is defined by, e.g. 'WA', 'IFAA', 'AGB', 'AA' etc.family- The larger family of rounds to which this round belongs, e.g. 'WA1440', 'WA720', 'Nationals' etc.
.. ipython:: python
my720Round = au.Round(
"WA 720 (70m)",
[my70mPass, my70mPass],
location="Outdoor Target",
body="WA",
family="WA720",
)
A number of useful rounds are pre-defined and come preloaded as dictionaries that can be imported:
.. ipython:: python
from archeryutils import load_rounds
agb_outdoor = load_rounds.AGB_outdoor_imperial
for round_i in agb_outdoor.values():
print(round_i.name)
Individial rounds are accessible via 'dot' notation (using the alias listed in agb_outdoor.keys()) as follows:
.. ipython:: python
agb_outdoor.york.get_info()
agb_outdoor.york.max_score()
Possible options for round collections are:
AGB_outdoor_imperial- Archery GB outdoor imperial roundsAGB_outdoor_metric- Archery GB outdoor metric roundsAGB_indoor- Archery GB indoor roundsWA_outdoor- World Archery outdoor roundsWA_indoor- World Archery indoor roundsWA_field- World Archery field roundsWA_experimental- Experimental World Archery 660 rounds used at Antalya WC 2025IFAA_field- IFAA indoor and outdoor roundsAA_outdoor_metric- Archery Australia outdoor metric roundsAA_indoor- Archery Australia indoor roundsAA_field- Archery Australia field roundsAGB_VI- Archery GB Visually Impaired roundsWA_VI- World Archery Visually Impaired roundsmisc- Miscellaneous rounds such as individual distances, 252 awards, frostbites etc.
archeryutils features support for calculations using a number of different handicap schemes for accuracy measurement, including those of Archery GB (Atkinson (2023), Lane (1978)) and Archery Australia (Park (2014)).
.. ipython:: python
from archeryutils import handicaps as hc
Given a handicap and a round we can calculate the score that would be achieved:
.. ipython:: python
score_from_hc = hc.score_for_round(
38,
agb_outdoor.york,
"AGB",
)
print(f"A handicap of 38 on a York is a score of {score_from_hc}.")
pass_scores = hc.score_for_passes(
38,
agb_outdoor.york,
"AGB",
)
print(f"A handicap of 38 on a York gives pass scores of {pass_scores}.")
Perhaps more interestingly we can take a score on a particular round and convert it to a handicap:
.. ipython:: python
hc_from_score = hc.handicap_from_score(
950,
agb_outdoor.york,
"AGB",
)
print(f"A score of 950 on a York is a continuous handicap of {hc_from_score}.")
hc_from_score = hc.handicap_from_score(
950,
agb_outdoor.york,
"AGB",
int_prec=True,
)
print(f"A score of 950 on a York is a discrete handicap of {hc_from_score}.")
There is also the HandicapTable class for generating handicap tables:
.. ipython:: python
handicaps = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
rounds = [
agb_outdoor.york,
agb_outdoor.hereford,
agb_outdoor.st_george,
agb_outdoor.albion,
]
# The following would allow printing of handicap tables for an entire group of rounds:
# rounds = list(load_rounds.AGB_outdoor_imperial.values())
my_agb_table = hc.HandicapTable(
"AGB",
handicaps,
rounds,
)
my_agb_table.print()
Finally there is support for the various Archery GB classification schemes
For full details see the summary on archerycalculator.com, the Archery GB website here and here, and the Shooting Administrative Procedures.
The classification routines make use of built in types to specify the age, bowstyle, and gender for the category for which data is being collected. This allows the code to be robust, less prone to unexpected errors and misuse, and to provide useful feedback when given invalid options.
The options available are as follows:
.. ipython:: python
from archeryutils.classifications import AGB_ages, AGB_bowstyles, AGB_genders
for age in AGB_ages:
print(age.name)
for bowstyle in AGB_bowstyles:
print(bowstyle.name)
for gender in AGB_genders:
print(gender.name)
They can be specified as the input to a function using dot notation as the following examples illustrate:
Given a score we can calculate the classification it achieves:
.. ipython:: python
from archeryutils import classifications as cf
from archeryutils.classifications import AGB_ages, AGB_bowstyles, AGB_genders
from archeryutils import load_rounds
# AGB Outdoor
agb_outdoor_imperial = load_rounds.AGB_outdoor_imperial
class_from_score = cf.calculate_agb_outdoor_classification(
965,
agb_outdoor_imperial.hereford,
AGB_bowstyles.RECURVE,
AGB_genders.OPEN,
AGB_ages.OVER_50,
)
print(
f"A score of 965 on a Hereford is class {class_from_score} for a 50+ open recurve."
)
# AGB Indoor
wa_indoor = load_rounds.WA_indoor
class_from_score = cf.calculate_agb_indoor_classification(
562,
wa_indoor.wa18,
AGB_bowstyles.COMPOUND,
AGB_genders.FEMALE,
AGB_ages.ADULT,
)
print(
f"A score of 562 on a WA 18 is class {class_from_score} for adult female compound."
)
# AGB Field
wa_field = load_rounds.WA_field
class_from_score = cf.calculate_agb_field_classification(
168,
wa_field.wa_field_24_blue_unmarked,
AGB_bowstyles.TRADITIONAL,
AGB_genders.FEMALE,
AGB_ages.ADULT,
)
print(
f"A score of 168 on a WA Unmarked 24 is class {class_from_score} for an under adult female traditional."
)
Or, given a round we can output the scores required for each classification band:
.. ipython:: python
agb_outdoor_imperial = load_rounds.AGB_outdoor_imperial
class_scores = cf.agb_outdoor_classification_scores(
agb_outdoor_imperial.hereford,
AGB_bowstyles.RECURVE,
AGB_genders.OPEN,
AGB_ages.ADULT,
)
print(class_scores)
agb_indoor = load_rounds.AGB_indoor
class_scores = cf.agb_indoor_classification_scores(
agb_indoor.portsmouth,
AGB_bowstyles.COMPOUND,
AGB_genders.FEMALE,
AGB_ages.ADULT,
)
print(class_scores)
wa_field = load_rounds.WA_field
class_scores = cf.agb_field_classification_scores(
wa_field.wa_field_24_blue_unmarked,
AGB_bowstyles.FLATBOW,
AGB_genders.FEMALE,
AGB_ages.UNDER_18,
)
print(class_scores)