Skip to content

Commit

Permalink
model attributes not initializable
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiefl committed Jan 2, 2025
1 parent 0e671b3 commit 2e77291
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 41 deletions.
9 changes: 7 additions & 2 deletions pooltool/physics/resolve/ball_ball/friction.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ def calculate_friction(self, ball1: Ball, ball2: Ball) -> float:
class AlciatoreBallBallFriction:
"""Friction fit curve u_b = a + b * exp(-c * v_rel) used in David Alciatore's TP A-14"""

model: BallBallFrictionModel = BallBallFrictionModel.ALCIATORE
a: float = 9.951e-3
b: float = 0.108
c: float = 1.088

model: BallBallFrictionModel = attrs.field(
default=BallBallFrictionModel.ALCIATORE, init=False
)

def calculate_friction(self, ball1: Ball, ball2: Ball) -> float:
unit_x = np.array([1.0, 0.0, 0.0])
v1_c = ptmath.surface_velocity(ball1.state.rvw, unit_x, ball1.params.R)
Expand All @@ -43,7 +46,9 @@ def calculate_friction(self, ball1: Ball, ball2: Ball) -> float:

@attrs.define
class AverageBallBallFriction:
model: BallBallFrictionModel = BallBallFrictionModel.AVERAGE
model: BallBallFrictionModel = attrs.field(
default=BallBallFrictionModel.AVERAGE, init=False
)

def calculate_friction(self, ball1: Ball, ball2: Ball) -> float:
return (ball1.params.u_b + ball2.params.u_b) / 2

Check warning on line 54 in pooltool/physics/resolve/ball_ball/friction.py

View check run for this annotation

Codecov / codecov/patch

pooltool/physics/resolve/ball_ball/friction.py#L54

Added line #L54 was not covered by tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,12 @@ class FrictionalInelastic(CoreBallBallCollision):
and a more complete analysis of velocity and angular velocity in their vector forms.
"""

model: BallBallModel = attrs.field(default=BallBallModel.FRICTIONAL_INELASTIC)
friction: BallBallFrictionStrategy = AlciatoreBallBallFriction()

model: BallBallModel = attrs.field(
default=BallBallModel.FRICTIONAL_INELASTIC, init=False
)

def solve(self, ball1: Ball, ball2: Ball) -> Tuple[Ball, Ball]:
"""Resolves the collision."""
rvw1, rvw2 = _resolve_ball_ball(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,13 @@ class FrictionalMathavan(CoreBallBallCollision):
https://billiards.colostate.edu/physics_articles/Mathavan_Sports_2014.pdf
"""

model: BallBallModel = attrs.field(default=BallBallModel.FRICTIONAL_MATHAVAN)
friction: BallBallFrictionStrategy = AlciatoreBallBallFriction()
num_iterations: int = 1000

model: BallBallModel = attrs.field(
default=BallBallModel.FRICTIONAL_MATHAVAN, init=False
)

def solve(self, ball1: Ball, ball2: Ball) -> Tuple[Ball, Ball]:
"""Resolve ball-ball collision via Mathavan et al. (2014).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ class FrictionlessElastic(CoreBallBallCollision):
https://ekiefl.github.io/2020/04/24/pooltool-theory/#1-elastic-instantaneous-frictionless
"""

model: BallBallModel = attrs.field(default=BallBallModel.FRICTIONLESS_ELASTIC)
model: BallBallModel = attrs.field(
default=BallBallModel.FRICTIONLESS_ELASTIC, init=False
)

def solve(self, ball1: Ball, ball2: Ball) -> Tuple[Ball, Ball]:
"""Resolves the collision."""
Expand Down
8 changes: 6 additions & 2 deletions pooltool/physics/resolve/ball_cushion/han_2005/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ def _solve(ball: Ball, cushion: Cushion) -> Tuple[Ball, Cushion]:

@attrs.define
class Han2005Linear(CoreBallLCushionCollision):
model: BallLCushionModel = attrs.field(default=BallLCushionModel.HAN_2005)
model: BallLCushionModel = attrs.field(
default=BallLCushionModel.HAN_2005, init=False
)

def solve(
self, ball: Ball, cushion: LinearCushionSegment
Expand All @@ -121,7 +123,9 @@ def solve(

@attrs.define
class Han2005Circular(CoreBallCCushionCollision):
model: BallCCushionModel = attrs.field(default=BallCCushionModel.HAN_2005)
model: BallCCushionModel = attrs.field(
default=BallCCushionModel.HAN_2005, init=False
)

def solve(
self, ball: Ball, cushion: CircularCushionSegment
Expand Down
10 changes: 8 additions & 2 deletions pooltool/physics/resolve/ball_cushion/unrealistic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@ def _solve(

@attrs.define
class UnrealisticLinear(CoreBallLCushionCollision):
model: BallLCushionModel = attrs.field(default=BallLCushionModel.HAN_2005)
restitution: bool = True

model: BallLCushionModel = attrs.field(
default=BallLCushionModel.HAN_2005, init=False
)

def solve(
self, ball: Ball, cushion: LinearCushionSegment
) -> Tuple[Ball, LinearCushionSegment]:
Expand All @@ -82,9 +85,12 @@ def solve(

@attrs.define
class UnrealisticCircular(CoreBallCCushionCollision):
model: BallCCushionModel = attrs.field(default=BallCCushionModel.HAN_2005)
restitution: bool = True

model: BallCCushionModel = attrs.field(
default=BallCCushionModel.HAN_2005, init=False
)

def solve(
self, ball: Ball, cushion: CircularCushionSegment
) -> Tuple[Ball, CircularCushionSegment]:
Expand Down
2 changes: 1 addition & 1 deletion pooltool/physics/resolve/ball_pocket/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def resolve(

@attrs.define
class CanonicalBallPocket:
model: BallPocketModel = attrs.field(default=BallPocketModel.CANONICAL)
model: BallPocketModel = attrs.field(default=BallPocketModel.CANONICAL, init=False)

def resolve(
self, ball: Ball, pocket: Pocket, inplace: bool = False
Expand Down
37 changes: 8 additions & 29 deletions pooltool/physics/resolve/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,22 @@
from __future__ import annotations

from pathlib import Path
from typing import Any, Dict, Mapping, Optional, Type
from typing import Optional

import attrs

import pooltool.user_config
from pooltool.events.datatypes import AgentType, Event, EventType
from pooltool.physics.resolve.ball_ball import (
BallBallCollisionStrategy,
ball_ball_models,
)
from pooltool.physics.resolve.ball_ball.friction import (
AlciatoreBallBallFriction,
BallBallFrictionStrategy,
ball_ball_friction_models,
)
from pooltool.physics.resolve.ball_ball.frictional_mathavan import FrictionalMathavan
from pooltool.physics.resolve.ball_cushion import (
BallCCushionCollisionStrategy,
BallLCushionCollisionStrategy,
ball_ccushion_models,
ball_lcushion_models,
)
from pooltool.physics.resolve.ball_cushion.han_2005.model import (
Han2005Circular,
Expand All @@ -32,20 +27,17 @@
from pooltool.physics.resolve.ball_pocket import (
BallPocketStrategy,
CanonicalBallPocket,
ball_pocket_models,
)
from pooltool.physics.resolve.serialize import register_serialize_hooks
from pooltool.physics.resolve.stick_ball import (
StickBallCollisionStrategy,
stick_ball_models,
)
from pooltool.physics.resolve.stick_ball.instantaneous_point import InstantaneousPoint
from pooltool.physics.resolve.transition import (
BallTransitionStrategy,
CanonicalTransition,
ball_transition_models,
)
from pooltool.serialize import Pathish, conversion
from pooltool.serialize.serializers import SerializeFormat
from pooltool.system.datatypes import System
from pooltool.terminal import Run

Expand Down Expand Up @@ -142,7 +134,11 @@ def default(cls) -> Resolver:

resolver = cls(

Check warning on line 135 in pooltool/physics/resolve/resolver.py

View check run for this annotation

Codecov / codecov/patch

pooltool/physics/resolve/resolver.py#L135

Added line #L135 was not covered by tests
ball_ball=FrictionalMathavan(
friction=AlciatoreBallBallFriction(),
friction=AlciatoreBallBallFriction(
a=0.009951,
b=0.108,
c=1.088,
),
num_iterations=1000,
),
ball_linear_cushion=Han2005Linear(),
Expand Down Expand Up @@ -184,21 +180,4 @@ def _snapshot_final(shot: System, event: Event) -> None:
agent.set_final(shot.table.pockets[agent.id])


_model_map: Mapping[Any, Mapping[Any, Type]] = {
BallBallCollisionStrategy: ball_ball_models,
BallLCushionCollisionStrategy: ball_lcushion_models,
BallCCushionCollisionStrategy: ball_ccushion_models,
BallPocketStrategy: ball_pocket_models,
StickBallCollisionStrategy: stick_ball_models,
BallTransitionStrategy: ball_transition_models,
BallBallFrictionStrategy: ball_ball_friction_models,
}


def _disambiguate_model_structuring(v: Dict[str, Any], t: Type) -> Any:
return conversion[SerializeFormat.YAML].structure(v, _model_map[t][v["model"]])


conversion.register_structure_hook_func(
check_func=lambda t: t in _model_map, func=_disambiguate_model_structuring
)
register_serialize_hooks()
67 changes: 67 additions & 0 deletions pooltool/physics/resolve/serialize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from __future__ import annotations

from typing import Any, Dict, Mapping, Type

from cattrs.gen import make_dict_unstructure_fn

from pooltool.physics.resolve.ball_ball import (
BallBallCollisionStrategy,
ball_ball_models,
)
from pooltool.physics.resolve.ball_ball.friction import (
BallBallFrictionStrategy,
ball_ball_friction_models,
)
from pooltool.physics.resolve.ball_cushion import (
BallCCushionCollisionStrategy,
BallLCushionCollisionStrategy,
ball_ccushion_models,
ball_lcushion_models,
)
from pooltool.physics.resolve.ball_pocket import (
BallPocketStrategy,
ball_pocket_models,
)
from pooltool.physics.resolve.stick_ball import (
StickBallCollisionStrategy,
stick_ball_models,
)
from pooltool.physics.resolve.transition import (
BallTransitionStrategy,
ball_transition_models,
)
from pooltool.serialize import conversion
from pooltool.serialize.serializers import SerializeFormat

_model_map: Mapping[Any, Mapping[Any, Type]] = {
BallBallCollisionStrategy: ball_ball_models,
BallLCushionCollisionStrategy: ball_lcushion_models,
BallCCushionCollisionStrategy: ball_ccushion_models,
BallPocketStrategy: ball_pocket_models,
StickBallCollisionStrategy: stick_ball_models,
BallTransitionStrategy: ball_transition_models,
BallBallFrictionStrategy: ball_ball_friction_models,
}


def _disambiguate_model_structuring(v: Dict[str, Any], t: Type) -> Any:
return conversion[SerializeFormat.YAML].structure(v, _model_map[t][v["model"]])


def register_serialize_hooks():
conversion.register_structure_hook_func(
check_func=lambda t: t in _model_map, func=_disambiguate_model_structuring
)

for models in _model_map.values():
for model_cls in models.values():
# Unstructure init=False attrs fields
conversion.register_unstructure_hook(
model_cls,
make_dict_unstructure_fn(
model_cls,
conversion[SerializeFormat.YAML],
_cattrs_include_init_false=True,
),
which=(SerializeFormat.YAML,),
)
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,13 @@ class InstantaneousPoint(CoreStickBallCollision):
:mod:`pooltool.physics.resolve.stick_ball.squirt`).
"""

model: StickBallModel = attrs.field(default=StickBallModel.INSTANTANEOUS_POINT)
english_throttle: float = 1.0
squirt_throttle: float = 1.0

model: StickBallModel = attrs.field(
default=StickBallModel.INSTANTANEOUS_POINT, init=False
)

def solve(self, cue: Cue, ball: Ball) -> Tuple[Cue, Ball]:
v, w = cue_strike(
ball.params.m,
Expand Down
4 changes: 3 additions & 1 deletion pooltool/physics/resolve/transition/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def resolve(self, ball: Ball, transition: EventType, inplace: bool = False) -> B

@attrs.define
class CanonicalTransition:
model: BallTransitionModel = attrs.field(default=BallTransitionModel.CANONICAL)
model: BallTransitionModel = attrs.field(
default=BallTransitionModel.CANONICAL, init=False
)

def resolve(self, ball: Ball, transition: EventType, inplace: bool = False) -> Ball:
if not inplace:
Expand Down

0 comments on commit 2e77291

Please sign in to comment.