Skip to content

waterrocketpy.core.validation

Validation functions for water rocket simulation parameters.

ParameterValidator

Validates simulation and rocket parameters.

Source code in waterrocketpy/core/validation.py
class ParameterValidator:
    """Validates simulation and rocket parameters."""

    @staticmethod
    def validate_positive(
        value: float, name: str, min_value: float = 0
    ) -> None:
        """Validate that a value is positive."""
        if value <= min_value:
            raise ValidationError(
                f"{name} must be greater than {min_value}, got {value}"
            )

    @staticmethod
    def validate_range(
        value: float, name: str, min_val: float, max_val: float
    ) -> None:
        """Validate that a value is within a specified range."""
        if not (min_val <= value <= max_val):
            raise ValidationError(
                f"{name} must be between {min_val} and {max_val}, got {value}"
            )

    @staticmethod
    def validate_fraction(value: float, name: str) -> None:
        """Validate that a value is a valid fraction (0-1)."""
        ParameterValidator.validate_range(value, name, 0.0, 1.0)

    @staticmethod
    def validate_rocket_parameters(params: Dict[str, Any]) -> List[str]:
        """
        Validate rocket parameters and return list of warnings.

        Args:
            params: Dictionary of rocket parameters

        Returns:
            List of warning messages

        Raises:
            ValidationError: If critical parameters are invalid
        """
        warnings = []

        # Required parameters
        required_params = [
            "P0",
            "A_nozzle",
            "V_bottle",
            "water_fraction",
            "C_d",
            "m_empty",
            "C_drag",
            "A_rocket",
        ]

        for param in required_params:
            if param not in params:
                raise ValidationError(f"Missing required parameter: {param}")

        # Validate pressure
        ParameterValidator.validate_positive(
            params["P0"], "Initial pressure (P0)"
        )
        if params["P0"] < ATMOSPHERIC_PRESSURE:
            warnings.append("Initial pressure is below atmospheric pressure")
        if params["P0"] > 50 * ATMOSPHERIC_PRESSURE:
            warnings.append(
                "Initial pressure is very high (>50 bar) - safety concern"
            )

        # Validate areas
        ParameterValidator.validate_positive(
            params["A_nozzle"], "Nozzle area (A_nozzle)"
        )
        ParameterValidator.validate_positive(
            params["A_rocket"], "Rocket cross-sectional area (A_rocket)"
        )

        # Validate volume
        ParameterValidator.validate_positive(
            params["V_bottle"], "Bottle volume (V_bottle)"
        )

        # Validate water fraction
        ParameterValidator.validate_fraction(
            params["water_fraction"], "Water fraction"
        )
        if params["water_fraction"] < 0.1:
            warnings.append(
                "Water fraction is very low (<10%) - may result in poor performance"
            )
        if params["water_fraction"] > 0.8:
            warnings.append(
                "Water fraction is very high (>80%) - may result in poor performance"
            )

        # Validate coefficients
        ParameterValidator.validate_range(
            params["C_d"], "Discharge coefficient (C_d)", 0.1, 1.0
        )
        ParameterValidator.validate_range(
            params["C_drag"], "Drag coefficient (C_drag)", 0.1, 2.0
        )

        # Validate mass
        ParameterValidator.validate_positive(
            params["m_empty"], "Empty mass (m_empty)"
        )

        # Check for reasonable values
        if params["C_d"] < 0.6:
            warnings.append(
                "Discharge coefficient is low (<0.6) - check nozzle design"
            )
        if params["C_drag"] > 1.5:
            warnings.append(
                "Drag coefficient is high (>1.5) - check rocket aerodynamics"
            )

        return warnings

    @staticmethod
    def validate_simulation_parameters(params: Dict[str, Any]) -> List[str]:
        """
        Validate simulation parameters.

        Args:
            params: Dictionary of simulation parameters

        Returns:
            List of warning messages
        """
        warnings = []

        if "max_time" in params:
            ParameterValidator.validate_positive(
                params["max_time"], "Maximum simulation time"
            )
            if params["max_time"] > 60:
                warnings.append("Maximum simulation time is very long (>60s)")

        if "time_step" in params:
            ParameterValidator.validate_positive(
                params["time_step"], "Time step"
            )
            if params["time_step"] > 0.1:
                warnings.append(
                    "Time step is large (>0.1s) - may affect accuracy"
                )
            if params["time_step"] < 0.001:
                warnings.append(
                    "Time step is very small (<0.001s) - may be computationally expensive"
                )

        return warnings

    @staticmethod
    def validate_flight_data(
        time: np.ndarray,
        altitude: np.ndarray,
        velocity: np.ndarray,
        mass: np.ndarray,
    ) -> bool:
        """
        Validate flight data for physical consistency.

        Args:
            time: Time array
            altitude: Altitude array
            velocity: Velocity array
            mass: Mass array

        Returns:
            bool: True if data is valid
        """
        # Check array lengths
        if not (len(time) == len(altitude) == len(velocity) == len(mass)):
            raise ValidationError(
                "All flight data arrays must have the same length"
            )

        # Check for NaN or infinite values
        for arr, name in [
            (time, "time"),
            (altitude, "altitude"),
            (velocity, "velocity"),
            (mass, "mass"),
        ]:
            if np.any(np.isnan(arr)) or np.any(np.isinf(arr)):
                raise ValidationError(
                    f"{name} array contains NaN or infinite values"
                )

        # Check physical constraints
        if np.any(mass < 0):
            raise ValidationError("Mass cannot be negative")

        if np.any(altitude < 0):
            raise ValidationError("Altitude cannot be negative")

        # Check time monotonicity
        if not np.all(np.diff(time) > 0):
            raise ValidationError(
                "Time array must be monotonically increasing"
            )

        return True

validate_flight_data(time, altitude, velocity, mass) staticmethod

Validate flight data for physical consistency.

Parameters:

Name Type Description Default
time ndarray

Time array

required
altitude ndarray

Altitude array

required
velocity ndarray

Velocity array

required
mass ndarray

Mass array

required

Returns:

Type Description
bool

True if data is valid

Source code in waterrocketpy/core/validation.py
@staticmethod
def validate_flight_data(
    time: np.ndarray,
    altitude: np.ndarray,
    velocity: np.ndarray,
    mass: np.ndarray,
) -> bool:
    """
    Validate flight data for physical consistency.

    Args:
        time: Time array
        altitude: Altitude array
        velocity: Velocity array
        mass: Mass array

    Returns:
        bool: True if data is valid
    """
    # Check array lengths
    if not (len(time) == len(altitude) == len(velocity) == len(mass)):
        raise ValidationError(
            "All flight data arrays must have the same length"
        )

    # Check for NaN or infinite values
    for arr, name in [
        (time, "time"),
        (altitude, "altitude"),
        (velocity, "velocity"),
        (mass, "mass"),
    ]:
        if np.any(np.isnan(arr)) or np.any(np.isinf(arr)):
            raise ValidationError(
                f"{name} array contains NaN or infinite values"
            )

    # Check physical constraints
    if np.any(mass < 0):
        raise ValidationError("Mass cannot be negative")

    if np.any(altitude < 0):
        raise ValidationError("Altitude cannot be negative")

    # Check time monotonicity
    if not np.all(np.diff(time) > 0):
        raise ValidationError(
            "Time array must be monotonically increasing"
        )

    return True

validate_fraction(value, name) staticmethod

Validate that a value is a valid fraction (0-1).

Source code in waterrocketpy/core/validation.py
@staticmethod
def validate_fraction(value: float, name: str) -> None:
    """Validate that a value is a valid fraction (0-1)."""
    ParameterValidator.validate_range(value, name, 0.0, 1.0)

validate_positive(value, name, min_value=0) staticmethod

Validate that a value is positive.

Source code in waterrocketpy/core/validation.py
@staticmethod
def validate_positive(
    value: float, name: str, min_value: float = 0
) -> None:
    """Validate that a value is positive."""
    if value <= min_value:
        raise ValidationError(
            f"{name} must be greater than {min_value}, got {value}"
        )

validate_range(value, name, min_val, max_val) staticmethod

Validate that a value is within a specified range.

Source code in waterrocketpy/core/validation.py
@staticmethod
def validate_range(
    value: float, name: str, min_val: float, max_val: float
) -> None:
    """Validate that a value is within a specified range."""
    if not (min_val <= value <= max_val):
        raise ValidationError(
            f"{name} must be between {min_val} and {max_val}, got {value}"
        )

validate_rocket_parameters(params) staticmethod

Validate rocket parameters and return list of warnings.

Parameters:

Name Type Description Default
params Dict[str, Any]

Dictionary of rocket parameters

required

Returns:

Type Description
List[str]

List of warning messages

Exceptions:

Type Description
ValidationError

If critical parameters are invalid

Source code in waterrocketpy/core/validation.py
@staticmethod
def validate_rocket_parameters(params: Dict[str, Any]) -> List[str]:
    """
    Validate rocket parameters and return list of warnings.

    Args:
        params: Dictionary of rocket parameters

    Returns:
        List of warning messages

    Raises:
        ValidationError: If critical parameters are invalid
    """
    warnings = []

    # Required parameters
    required_params = [
        "P0",
        "A_nozzle",
        "V_bottle",
        "water_fraction",
        "C_d",
        "m_empty",
        "C_drag",
        "A_rocket",
    ]

    for param in required_params:
        if param not in params:
            raise ValidationError(f"Missing required parameter: {param}")

    # Validate pressure
    ParameterValidator.validate_positive(
        params["P0"], "Initial pressure (P0)"
    )
    if params["P0"] < ATMOSPHERIC_PRESSURE:
        warnings.append("Initial pressure is below atmospheric pressure")
    if params["P0"] > 50 * ATMOSPHERIC_PRESSURE:
        warnings.append(
            "Initial pressure is very high (>50 bar) - safety concern"
        )

    # Validate areas
    ParameterValidator.validate_positive(
        params["A_nozzle"], "Nozzle area (A_nozzle)"
    )
    ParameterValidator.validate_positive(
        params["A_rocket"], "Rocket cross-sectional area (A_rocket)"
    )

    # Validate volume
    ParameterValidator.validate_positive(
        params["V_bottle"], "Bottle volume (V_bottle)"
    )

    # Validate water fraction
    ParameterValidator.validate_fraction(
        params["water_fraction"], "Water fraction"
    )
    if params["water_fraction"] < 0.1:
        warnings.append(
            "Water fraction is very low (<10%) - may result in poor performance"
        )
    if params["water_fraction"] > 0.8:
        warnings.append(
            "Water fraction is very high (>80%) - may result in poor performance"
        )

    # Validate coefficients
    ParameterValidator.validate_range(
        params["C_d"], "Discharge coefficient (C_d)", 0.1, 1.0
    )
    ParameterValidator.validate_range(
        params["C_drag"], "Drag coefficient (C_drag)", 0.1, 2.0
    )

    # Validate mass
    ParameterValidator.validate_positive(
        params["m_empty"], "Empty mass (m_empty)"
    )

    # Check for reasonable values
    if params["C_d"] < 0.6:
        warnings.append(
            "Discharge coefficient is low (<0.6) - check nozzle design"
        )
    if params["C_drag"] > 1.5:
        warnings.append(
            "Drag coefficient is high (>1.5) - check rocket aerodynamics"
        )

    return warnings

validate_simulation_parameters(params) staticmethod

Validate simulation parameters.

Parameters:

Name Type Description Default
params Dict[str, Any]

Dictionary of simulation parameters

required

Returns:

Type Description
List[str]

List of warning messages

Source code in waterrocketpy/core/validation.py
@staticmethod
def validate_simulation_parameters(params: Dict[str, Any]) -> List[str]:
    """
    Validate simulation parameters.

    Args:
        params: Dictionary of simulation parameters

    Returns:
        List of warning messages
    """
    warnings = []

    if "max_time" in params:
        ParameterValidator.validate_positive(
            params["max_time"], "Maximum simulation time"
        )
        if params["max_time"] > 60:
            warnings.append("Maximum simulation time is very long (>60s)")

    if "time_step" in params:
        ParameterValidator.validate_positive(
            params["time_step"], "Time step"
        )
        if params["time_step"] > 0.1:
            warnings.append(
                "Time step is large (>0.1s) - may affect accuracy"
            )
        if params["time_step"] < 0.001:
            warnings.append(
                "Time step is very small (<0.001s) - may be computationally expensive"
            )

    return warnings

ValidationError (Exception)

Custom exception for validation errors.

Source code in waterrocketpy/core/validation.py
class ValidationError(Exception):
    """Custom exception for validation errors."""

    pass