diff --git a/moorpy/helpers.py b/moorpy/helpers.py index 05b4aa9..76cf6bd 100644 --- a/moorpy/helpers.py +++ b/moorpy/helpers.py @@ -671,14 +671,16 @@ def getLineProps(dnommm, material, lineProps=None, source=None, name="", rho=102 # Shorthand for the sub-dictionary of properties for the material in question mat = lineProps[material] + # convert nominal diameter from mm to m (needed for the range checks below) + d = dnommm*0.001 + # Check valid diameter ranges if mat['d_max'] >= 0 and d > mat['d_max']: # if a max value is given and the diameter is greater than the max raise Exception(f"Input diameter {d} m is greater than the max valid value of {mat['d_max']} m for {material}.") if mat['d_min'] >= 0 and d < mat['d_min']: # if a min value is given and the diameter is less than the min raise Exception(f"Input diameter {d} m is less than the min valid value of {mat['d_min']} m for {material}.") - + # calculate the relevant properties for this specific line type - d = dnommm*0.001 # convert nominal diameter from mm to m mass = mat['mass_d2']*d**2 MBL = mat[ 'MBL_0'] + mat[ 'MBL_d']*d + mat[ 'MBL_d2']*d**2 + mat[ 'MBL_d3']*d**3 @@ -841,7 +843,7 @@ def loadLineProps(source): output[mat]['cost_EA' ] = getFromDict(props, 'cost_EA' , default=0.0) output[mat]['cost_MBL' ] = getFromDict(props, 'cost_MBL' , default=0.0) output[mat]['d_min' ] = getFromDict(props, 'd_min' , default=-1.0) # -1 to disable checking - output[mat]['d_max' ] = getFromDict(props, 'd_dmax' , default=-1.0) # -1 to disable checking + output[mat]['d_max' ] = getFromDict(props, 'd_max' , default=-1.0) # -1 to disable checking return output diff --git a/tests/test_line.py b/tests/test_line.py index c9b0ea6..00678c5 100644 --- a/tests/test_line.py +++ b/tests/test_line.py @@ -18,9 +18,37 @@ def test_line_stiffness(): '''Checks stiffness of mooring lines.''' - - - + + +def test_getLineProps_diameter_limits(): + '''getLineProps must apply the d_min / d_max range checks correctly. + + Regression test for two bugs in the diameter validation: + 1. ``d`` (the diameter in metres) was referenced in the range checks + before it was assigned, so any material with ``d_min >= 0`` or + ``d_max >= 0`` raised ``UnboundLocalError`` instead of the intended + range Exception. + 2. ``loadLineProps`` read ``d_max`` from the key ``'d_dmax'`` (a typo), + so a user-specified ``d_max`` was silently dropped to the -1 + "disabled" default and the upper-bound check never fired. + ''' + base = {'mass_d2': 100.0, 'MBL_0': 0.0, 'MBL_d': 1e6} + + # within the valid range -> succeeds and returns a line type dict + src = {'lineProps': {'rope': {**base, 'd_min': 0.01, 'd_max': 0.20}}} + lt = getLineProps(50.0, 'rope', source=src) # 50 mm = 0.05 m, in range + assert isinstance(lt, dict) and lt['material'] == 'rope' + + # below d_min -> the intended Exception (not UnboundLocalError) + with pytest.raises(Exception, match='less than the min'): + getLineProps(5.0, 'rope', source=src) # 5 mm = 0.005 m < 0.01 + + # above d_max -> the intended Exception; this only works if the d_max + # typo is fixed (otherwise d_max stays -1 and the check is disabled) + with pytest.raises(Exception, match='greater than the max'): + getLineProps(300.0, 'rope', source=src) # 300 mm = 0.3 m > 0.20 + + if __name__ == '__main__': import moorpy as mp