Open o-murphy opened 1 year ago
I implemented constant time-step calculation here: https://github.com/dbookstaber/py_ballistics/blob/time-step/py_ballisticcalc/trajectory_calc.py
However it looks like it will always take more iterations than the current method to achieve any particular level of precision, so I do not recommend pursuing this.
but in addition to the number of iterations, it is necessary to take into account the time for performing the calculation, it may turn out that due to a decrease in the number of calculations, the number of iterations can be increased without losing performance
we have to make a profiling and performance comparing
u can use timeit
built-in module to got time some function are executing
By "iterations" I meant the number of passes through the #region Trajectory Loop
to calculate any particular trajectory. (This is without regard to the zero-finding logic.)
I think the current method of setting the time step based on the velocity is optimal. Any other approach will create excess precision at some points and lower precision at others.
To improve efficiency, JBM recommends switching from this simple integration approach to something like RK4, which is what he uses.
I implemented RK4 integration in this branch. It is more robust than the Euler implementation we are running, meaning that as step_size increases errors grow more slowly. However on the example I studied it's not like orders of magnitude better.
This provides a foundation for some desirable improvements, including:
time
and the other using range.x
as the independent variable. The latter (CalcMethod.RK_dx
) version could particularly benefit TrajectoryCalc.zero_angle
since, as long as zero_distance % step_size == 0
then it will end exactly at the distance of interest.Right now I don't have much time to study alternative calculation methods, so I'm completely relying on you. In any case, if we're worried about breaking something, we can always implement it as an experimental feature and enable it via the library's global settings. Like we do with gunpowder sensitivity, we can enable an alternative calculation algorithm. Use inheritance, it allows to implement new feature without massive base code changes and lost of backwards compatibility and also allows extend functionality without changing major release version
Create new module, inherit from existing backend.TrajectoryCalculator
# my_new_solver.py
from py_ballisticcalc.backend import TrajectoryCalculator
class MyNewSolver(TrajectoryCalculator):
def trajectory(*args, **kwargs):
# reimplement methods you need
...
Add global flags to trajectory_calc.py
# existing flags
_globalUsePowderSensitivity = False
def set_global_use_powder_sensitivity(value: bool) -> None:
# pylint: disable=global-statement
global _globalUsePowderSensitivity
if not isinstance(value, bool):
raise TypeError(f"set_global_use_powder_sensitivity {value=} is not a boolean")
_globalUsePowderSensitivity = value
# new flag
_globalUseNewSolver = False
def set_global_use_new_solver(value: bool) -> None:
global _globalUseNewSolver
_globalUseNewSolver = value
Add conditional import of your new solver to interface.Calculator
from py_ballisticcalc.backend import TrajectoryCalc, get_global_use_new_solver
# Calculator class
def barrel_elevation_for_target(self, shot: Shot, target_distance: Union[float, Distance]) -> Angular:
if get_global_use_new_solver():
from my_new_solver import MyNewSolver
calc = MyNewSolver()
else:
calc = TrajectoryCalc()
This is implemented in pull request #50