Coverage for tests/physical_strategies.py: 68%

22 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-09-11 16:23 +0200

1"""Define strategies for physical scalar variables.""" 

2 

3from types import MappingProxyType 

4import typing 

5 

6from hypothesis import strategies as st 

7import numpy as np 

8 

9from swiift.lib.constants import PI_2 

10from swiift.model.model import Ice 

11from tests.utils import FloatSt, float_kw 

12 

13# Exagerated bounds for all independent physical parameters. 

14# Allows for testing exotic situations, without going into extremes either. 

15PHYSICAL_STRATEGIES = MappingProxyType( 

16 { 

17 ("floe", "left_edge"): st.floats(0, 5e3, **float_kw), 

18 ("ocean", "depth"): ( 

19 st.floats(min_value=1e-3, max_value=1000e3, **float_kw) | st.just(np.inf) 

20 ), 

21 ("ocean", "density"): st.floats(500, 5e3, **float_kw), 

22 ("ice", "frac_toughness"): st.floats(1e3, 1e7, **float_kw), 

23 ("ice", "poissons_ratio"): st.floats(-0.999, 0.5, **float_kw), 

24 ("ice", "strain_threshold"): st.floats(1e-7, 1e-3, **float_kw), 

25 ("ice", "thickness"): st.floats(1e-3, 1e3, **float_kw), 

26 ("ice", "youngs_modulus"): st.floats(1e6, 100e9, **float_kw), 

27 ("ice", "elastic_length"): st.floats(5e-4, 3e4, **float_kw), 

28 ("wave", "amplitude"): st.floats(1e-6, 1e3, **float_kw), 

29 ("wave", "period"): st.floats(min_value=1e-1, max_value=1e4, **float_kw), 

30 ("wave", "frequency"): st.floats(min_value=1e-4, max_value=10, **float_kw), 

31 ("wave", "phase"): st.floats(0, PI_2, exclude_max=True, **float_kw), 

32 ("wave", "wavenumber"): st.floats(7e-4, 600, **float_kw), 

33 ("gravity",): st.floats(0.1, 30, **float_kw), 

34 } 

35) 

36 

37 

38# For the composite strategies, set somewhat stricter upper bounds than plain 

39# inequality to avoid head-scratching floating point innacuracies. 

40@st.composite 

41def ice_density(draw: st.DrawFn, ocean_density: FloatSt) -> float: 

42 """Ice density contrained by ocean density. 

43 

44 We typically want ice to have a lower density for it to float. 

45 

46 Parameters 

47 ---------- 

48 draw : st.DrawFn 

49 Hypothesis dynamic callable. 

50 ocean_density : FloatSt 

51 Ocean density in kg m**-3. 

52 

53 Returns 

54 ------- 

55 float 

56 Ice density in kg m**-3. 

57 

58 """ 

59 ex = draw(ocean_density) 

60 return draw(st.floats(10, 0.9999 * ex, allow_nan=False, allow_subnormal=False)) 

61 

62 

63@st.composite 

64def ice_thickness( 

65 draw: st.DrawFn, 

66 ocean_density: FloatSt, 

67 ocean_depth: FloatSt, 

68 ice_density: FloatSt, 

69) -> float: 

70 """Ice thickness constrained by ocean depth and ice draught. 

71 

72 We typically want ice not to be grounded. That is, it must be thin enough 

73 that its draught is less than the water depth. 

74 

75 Parameters 

76 ---------- 

77 draw : st.DrawFn 

78 Hypothesis dynamic callable. 

79 ocean_density : FloatSt 

80 Strategy for ocean density in kg m**-3. 

81 ocean_depth : FloatSt 

82 Strategy for ocean depth in m. 

83 ice_density : FloatSt 

84 Strategy for ice density in kg m**-3. 

85 

86 Returns 

87 ------- 

88 float 

89 Ice thickness in m. 

90 

91 """ 

92 kwgs = {"rhow": ocean_density, "rhoi": ice_density, "H": ocean_depth} 

93 ex = {k: draw(v) for k, v in kwgs.items()} 

94 upper_bound = 0.9999 * ex["rhow"] / ex["rhoi"] * ex["H"] 

95 return draw( 

96 st.floats( 

97 0.1e-3, 

98 min(1000, upper_bound), 

99 exclude_max=True, 

100 allow_nan=False, 

101 allow_subnormal=False, 

102 ) 

103 ) 

104 

105 

106@st.composite 

107def floe_length(draw: st.DrawFn, ice: st.SearchStrategy[Ice]) -> float: 

108 """Floe length constrained by ice thickness. 

109 

110 We typically want floes to be longer than they are thick for stable buoyancy. 

111 

112 Parameters 

113 ---------- 

114 draw : st.DrawFn 

115 Hypothesis dynamic callable. 

116 ice : st.SearchStrategy[Ice] 

117 Strategy for an ice object. 

118 

119 Returns 

120 ------- 

121 float 

122 Floe length in m. 

123 

124 """ 

125 return draw( 

126 st.floats( 

127 2 * draw(ice).thickness, 1000e3, allow_nan=False, allow_subnormal=False 

128 ) 

129 ) 

130 

131 

132PHYSICAL_STRATEGIES_COMPOSITE: MappingProxyType[str, typing.Callable[..., FloatSt]] = ( 

133 MappingProxyType( 

134 { 

135 "floe_length": floe_length, 

136 "ice_density": ice_density, 

137 "ice_thickness": ice_thickness, 

138 } 

139 ) 

140) 

141