Skip to content

Multiple_GGmax_Curves.get_curve_matrix() and Multiple_Damping_Curves.get_curve_matrix() raise ValueError when a shorter curve starts at strain 0 #109

Open

Description

@jsh9

Found while reviewing #74. Pre-existing on main (b764057); #74 does not change the behavior.

Problem

When the curves have different lengths, get_curve_matrix() resamples each shorter curve with np.geomspace(np.min(strain), np.max(strain), max_length). np.geomspace() cannot start at 0, so if a shorter curve has a strain of 0, get_curve_matrix() raises ValueError: Geometric sequence cannot include zero. The curve classes accept a strain of 0 (check_two_column_format(..., ensure_non_negative=True) only rejects negative values), and curves of equal length with a strain of 0 work, because they are not resampled.

else: # otherwise we need a new strain array to match `max_length`
strain_ = np.geomspace(
np.min(strain), np.max(strain), max_length
)
damping_ = np.interp(strain_, strain, curve_.damping)

else: # otherwise we need a new strain array to match `max_length`
strain_ = np.geomspace(
np.min(strain), np.max(strain), max_length
)
GGmax_ = np.interp(strain_, strain, curve_.GGmax)

import numpy as np

from PySeismoSoil.class_curves import (
    Damping_Curve,
    GGmax_Curve,
    Multiple_Damping_Curves,
    Multiple_GGmax_Curves,
)

long_strain = np.array([0.0001, 0.001, 0.01, 0.1, 1.0])  # 5 points, unit: %
short_strain = np.array([0.0, 0.01, 0.1, 1.0])  # 4 points, starts at 0

GGmax_long = np.column_stack((long_strain, [1.0, 0.98, 0.9, 0.6, 0.2]))
GGmax_short = np.column_stack((short_strain, [1.0, 0.9, 0.6, 0.2]))
damping_long = np.column_stack((long_strain, [1.0, 1.2, 2.0, 6.0, 15.0]))
damping_short = np.column_stack((short_strain, [1.0, 2.0, 6.0, 15.0]))

# Zero strain is accepted by the curve classes
print(GGmax_Curve(GGmax_short).strain, Damping_Curve(damping_short).strain)
# [0.   0.01 0.1  1.  ] [0.   0.01 0.1  1.  ]

# Same lengths: works
mgc = Multiple_GGmax_Curves([GGmax_short, GGmax_short])
print(mgc.get_curve_matrix().shape)
# (4, 8)

# Different lengths, the shorter curve starts at 0
for curves in [
    Multiple_GGmax_Curves([GGmax_long, GGmax_short]),
    Multiple_Damping_Curves([damping_long, damping_short]),
]:
    try:
        print(curves.get_curve_matrix().shape)
    except ValueError as e:
        print(f'{type(e).__name__}: {e}')
# ValueError: Geometric sequence cannot include zero
# ValueError: Geometric sequence cannot include zero
# (expected (5, 8) for both)

Fix

  1. In both get_curve_matrix() methods, build the resampled strain array so that it can start at 0, for example:
    if np.min(strain) > 0:
        strain_ = np.geomspace(np.min(strain), np.max(strain), max_length)
    else:
        positive = strain[strain > 0]
        strain_ = np.concatenate(
            ([0.0], np.geomspace(np.min(positive), np.max(strain), max_length - 1))
        )
    For the 4-point curve above this gives [0, 0.01, 0.0464, 0.215, 1.0] (rounded), and np.interp() then gives G/Gmax [1.0, 0.9, 0.779, 0.549, 0.2] (rounded). The same code is in both classes, so it could move into one shared helper.
  2. Add a test with two curves of different lengths where the shorter one starts at strain 0, for both Multiple_GGmax_Curves and Multiple_Damping_Curves.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions