Interface
Calculator
dataclass
¶
Calculator(
config: Optional[ConfigT] = None,
engine: EngineFactoryProtocolEntry = DEFAULT_ENTRY,
)
Bases: Generic[ConfigT]
The main interface for the ballistics calculator.
This class provides thread-safe access to the underlying integration engines by creating a new, isolated engine instance for every method call.
Methods:
| Name | Description |
|---|---|
__post_init__ |
Loads the engine class. |
__getattr__ |
Delegate attribute access to the underlying engine instance. |
__getstate__ |
Called by pickle for serialization. |
__setstate__ |
Called by pickle for deserialization. |
barrel_elevation_for_target |
Calculate barrel elevation to hit target at zero_distance. |
set_weapon_zero |
Set shot.weapon.zero_elevation so that it hits a target at zero_distance. |
fire |
Calculate the trajectory for the given shot parameters. |
iter_engines |
Iterate all available engines in the entry points. |
Attributes¶
_engine_instance
property
¶
_engine_instance: EngineProtocol
Creates and returns a fresh, isolated engine instance upon every access.
This implementation is the core mechanism for ensuring thread safety in the Calculator class,
particularly essential in free-threaded Python (e.g., CPython with GIL disabled, Python 3.13+).
Thread Safety Rationale¶
Instead of using traditional synchronization primitives (like threading.Lock)
to protect a single, shared engine instance, this method employs isolation.
Since the underlying engine instances are not guaranteed to be thread-safe
internally, generating a new instance for each operation ensures that
no two concurrent threads will ever modify the same engine object. This
approach eliminates race conditions without introducing the overhead or
potential deadlocks associated with locking mechanisms.
Performance Consideration¶
Note: The overall performance of concurrent operations critically depends
on the initialization cost of the underlying engine class (self._engine_class).
If the engine's constructor performs extensive I/O, loads large data tables,
or executes complex setup, repeated instantiation may introduce significant
overhead. For optimal performance, the engine's initialization (__init__)
should be designed to be as lightweight as possible.
Returns:
| Type | Description |
|---|---|
EngineProtocol
|
EngineProtocol[Any]: A new, single-use engine instance configured
with the |
Functions¶
__post_init__
¶
__post_init__() -> None
Loads the engine class.
Crucially: The engine instance is not created here. To ensure thread safety (especially in free-threaded Python), each method call must operate on a new, isolated engine instance.
Source code in py_ballisticcalc/interface.py
111 112 113 114 115 116 117 118 119 | |
__getattr__
¶
Delegate attribute access to the underlying engine instance.
This method is called when an attribute is requested on the Calculator
instance that is not found through normal attribute lookup (i.e., it's
not a direct attribute of Calculator or its class). It then attempts
to retrieve the attribute from the _engine_instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
str
|
The name of the attribute to retrieve. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Any |
Any
|
The value of the attribute from |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If the attribute is not found on either the
|
Examples:
>>> calc = Calculator(engine=DEFAULT_ENTRY)
>>> calc_step = calc.get_calc_step()
>>> print(calc_step)
0.0025
>>> try:
... calc.unknown_method()
... except AttributeError as e:
... print(e)
'Calculator' object or its underlying engine 'RK4IntegrationEngine' has no attribute 'unknown_method'
Source code in py_ballisticcalc/interface.py
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | |
__getstate__
¶
__getstate__()
Called by pickle for serialization. We only serialize the public fields required for reconstruction. We explicitly exclude the calculated fields like _engine_class to ensure proper re-initialization in the new process.
Source code in py_ballisticcalc/interface.py
191 192 193 194 195 196 197 198 | |
__setstate__
¶
__setstate__(state)
Called by pickle for deserialization. We manually set the fields and call post_init to reload the engine class.
Source code in py_ballisticcalc/interface.py
200 201 202 203 204 205 206 207 208 209 | |
barrel_elevation_for_target
¶
Calculate barrel elevation to hit target at zero_distance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shot
|
Shot
|
Shot instance we want to zero. |
required |
target_distance
|
Union[float, Distance]
|
Look-distance to "zero," which is point we want to hit. This is the distance that a rangefinder would return with no ballistic adjustment. |
required |
Note
Some rangefinders offer an adjusted distance based on inclinometer measurement. However, without a complete ballistic model these can only approximate the effects on ballistic trajectory of shooting uphill or downhill. Therefore: For maximum accuracy, use the raw sight distance and look_angle as inputs here.
Source code in py_ballisticcalc/interface.py
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | |
set_weapon_zero
¶
Set shot.weapon.zero_elevation so that it hits a target at zero_distance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shot
|
Shot
|
Shot instance to zero. |
required |
zero_distance
|
Union[float, Distance]
|
Look-distance to "zero," which is point we want to hit. |
required |
Source code in py_ballisticcalc/interface.py
229 230 231 232 233 234 235 236 237 | |
fire
¶
fire(
shot: Shot,
trajectory_range: Union[float, Distance],
trajectory_step: Optional[
Union[float, Distance]
] = None,
*,
extra_data: bool = False,
dense_output: bool = False,
time_step: float = 0.0,
flags: Union[TrajFlag, int] = NONE,
raise_range_error: bool = True,
) -> HitResult
Calculate the trajectory for the given shot parameters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shot
|
Shot
|
Shot parameters, including position and barrel angle. |
required |
trajectory_range
|
Union[float, Distance]
|
Distance at which to stop computing the trajectory. |
required |
trajectory_step
|
Optional[Union[float, Distance]]
|
Distance between recorded trajectory points. Defaults to |
None
|
extra_data
|
bool
|
[DEPRECATED] Requests flags=TrajFlags.ALL and trajectory_step=PreferredUnits.distance(1). |
False
|
dense_output
|
bool
|
HitResult stores all calculation steps so it can interpolate any point. |
False
|
time_step
|
float
|
Maximum time between recorded points. If > 0, points are recorded at least this frequently. Defaults to 0.0. |
0.0
|
flags
|
Union[TrajFlag, int]
|
Flags for specific points of interest. Defaults to TrajFlag.NONE. |
NONE
|
raise_range_error
|
bool
|
If True, raises RangeError if returned by integration. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
HitResult |
HitResult
|
Object containing computed trajectory. |
Source code in py_ballisticcalc/interface.py
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | |
iter_engines
staticmethod
¶
iter_engines() -> Generator[EntryPoint, None, None]
Iterate all available engines in the entry points.
Source code in py_ballisticcalc/interface.py
292 293 294 295 | |
_EngineLoader
dataclass
¶
_EngineLoader()
Methods:
| Name | Description |
|---|---|
iter_engines |
Iterate over all available engines in the entry points. |
iter_engines
classmethod
¶
iter_engines() -> Generator[EntryPoint, None, None]
Iterate over all available engines in the entry points.
Source code in py_ballisticcalc/interface.py
53 54 55 56 57 58 59 | |