Skip to content

Latest commit

 

History

History
368 lines (268 loc) · 11 KB

File metadata and controls

368 lines (268 loc) · 11 KB

Quick-Start Guide

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.

Examples Notebook

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

Importing

To import the archeryutils package use:

.. ipython:: python

    import archeryutils as au

Target

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:

  1. Targets must be formed of concentric rings
  2. 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()}.",
        )

Pass

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)

Round

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",
    )

Default Rounds

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 rounds
  • AGB_outdoor_metric - Archery GB outdoor metric rounds
  • AGB_indoor - Archery GB indoor rounds
  • WA_outdoor - World Archery outdoor rounds
  • WA_indoor - World Archery indoor rounds
  • WA_field - World Archery field rounds
  • WA_experimental - Experimental World Archery 660 rounds used at Antalya WC 2025
  • IFAA_field - IFAA indoor and outdoor rounds
  • AA_outdoor_metric - Archery Australia outdoor metric rounds
  • AA_indoor - Archery Australia indoor rounds
  • AA_field - Archery Australia field rounds
  • AGB_VI - Archery GB Visually Impaired rounds
  • WA_VI - World Archery Visually Impaired rounds
  • misc - Miscellaneous rounds such as individual distances, 252 awards, frostbites etc.

Handicap Schemes

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()


Classifications

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)