Pavement_Design / src /models.py
GitsSaikat
first commit
8e9b78d
# src/models.py
import numpy as np
import pandas as pd
from typing import List, Dict
import logging
from src.utils.logger import setup_logger
logger = setup_logger(__name__)
class TrafficData:
def __init__(self, axle_loads: List[float], traffic_growth_rate: float, analysis_period: int):
"""
Initialize TrafficData with axle loads, growth rate, and analysis period.
:param axle_loads: List of axle loads in kN.
:param traffic_growth_rate: Annual traffic growth rate (decimal, e.g., 0.02 for 2%).
:param analysis_period: Number of years for analysis.
"""
self.axle_loads = axle_loads
self.traffic_growth_rate = traffic_growth_rate
self.analysis_period = analysis_period
def get_total_axle_loads(self) -> List[List[float]]:
"""
Project total axle loads over the analysis period considering growth rate.
:return: A list of lists, each sublist represents axle loads for a year.
"""
total_loads = []
for year in range(self.analysis_period):
growth_factor = (1 + self.traffic_growth_rate) ** year
projected_loads = [load * growth_factor for load in self.axle_loads]
total_loads.append(projected_loads)
logger.debug(f"Year {year + 1}: {projected_loads}")
return total_loads
@staticmethod
def from_dataframe(df: pd.DataFrame) -> 'TrafficData':
"""
Create TrafficData instance from a pandas DataFrame.
:param df: DataFrame with columns 'Axle_Loads', 'Traffic_Growth_Rate', 'Analysis_Period'.
:return: TrafficData instance.
"""
try:
axle_loads = df['Axle_Loads'].dropna().tolist()
traffic_growth_rate = float(df['Traffic_Growth_Rate'].iloc[0])
analysis_period = int(df['Analysis_Period'].iloc[0])
logger.info("TrafficData loaded successfully from DataFrame.")
return TrafficData(axle_loads, traffic_growth_rate, analysis_period)
except Exception as e:
logger.error(f"Error creating TrafficData from DataFrame: {e}")
raise ValueError(f"Invalid Traffic Data: {e}")
class ClimateData:
def __init__(self, average_temperature: float, temperature_variation: float, rainfall: float):
"""
Initialize ClimateData with average temperature, temperature variation, and rainfall.
:param average_temperature: Average temperature in °C.
:param temperature_variation: Temperature variation in °C.
:param rainfall: Annual rainfall in mm.
"""
self.average_temperature = average_temperature
self.temperature_variation = temperature_variation
self.rainfall = rainfall
@staticmethod
def from_dataframe(df: pd.DataFrame) -> 'ClimateData':
"""
Create ClimateData instance from a pandas DataFrame.
:param df: DataFrame with columns 'Average_Temperature', 'Temperature_Variation', 'Rainfall'.
:return: ClimateData instance.
"""
try:
average_temperature = float(df['Average_Temperature'].iloc[0])
temperature_variation = float(df['Temperature_Variation'].iloc[0])
rainfall = float(df['Rainfall'].iloc[0])
logger.info("ClimateData loaded successfully from DataFrame.")
return ClimateData(average_temperature, temperature_variation, rainfall)
except Exception as e:
logger.error(f"Error creating ClimateData from DataFrame: {e}")
raise ValueError(f"Invalid Climate Data: {e}")
class SubgradeProperties:
def __init__(self, modulus: float, CBR: float):
"""
Initialize SubgradeProperties with modulus and California Bearing Ratio (CBR).
:param modulus: Modulus of subgrade reaction in kPa/m.
:param CBR: California Bearing Ratio in %.
"""
self.modulus = modulus
self.CBR = CBR
@staticmethod
def from_dataframe(df: pd.DataFrame) -> 'SubgradeProperties':
"""
Create SubgradeProperties instance from a pandas DataFrame.
:param df: DataFrame with columns 'Modulus', 'CBR'.
:return: SubgradeProperties instance.
"""
try:
modulus = float(df['Modulus'].iloc[0])
CBR = float(df['CBR'].iloc[0])
logger.info("SubgradeProperties loaded successfully from DataFrame.")
return SubgradeProperties(modulus, CBR)
except Exception as e:
logger.error(f"Error creating SubgradeProperties from DataFrame: {e}")
raise ValueError(f"Invalid Subgrade Properties Data: {e}")
class MaterialProperties:
def __init__(self, asphalt_modulus: float, concrete_strength: float, thermal_coeff: float):
"""
Initialize MaterialProperties with asphalt modulus, concrete strength, and thermal coefficient.
:param asphalt_modulus: Asphalt modulus in MPa.
:param concrete_strength: Concrete strength in MPa.
:param thermal_coeff: Thermal coefficient (°C^-1).
"""
self.asphalt_modulus = asphalt_modulus
self.concrete_strength = concrete_strength
self.thermal_coeff = thermal_coeff
@staticmethod
def from_dataframe(df: pd.DataFrame) -> 'MaterialProperties':
"""
Create MaterialProperties instance from a pandas DataFrame.
:param df: DataFrame with columns 'Asphalt_Modulus', 'Concrete_Strength', 'Thermal_Coeff'.
:return: MaterialProperties instance.
"""
try:
asphalt_modulus = float(df['Asphalt_Modulus'].iloc[0])
concrete_strength = float(df['Concrete_Strength'].iloc[0])
thermal_coeff = float(df['Thermal_Coeff'].iloc[0])
logger.info("MaterialProperties loaded successfully from DataFrame.")
return MaterialProperties(asphalt_modulus, concrete_strength, thermal_coeff)
except Exception as e:
logger.error(f"Error creating MaterialProperties from DataFrame: {e}")
raise ValueError(f"Invalid Material Properties Data: {e}")
class Pavement:
def __init__(self, layers: List[float], pavement_type: str = 'Flexible'):
"""
Initialize Pavement structure.
:param layers: List of layer thicknesses in mm.
:param pavement_type: Type of pavement ('Flexible', 'Rigid', 'Composite').
"""
self.layers = layers
self.pavement_type = pavement_type
def add_layer(self, thickness: float):
"""
Add a new layer to the pavement structure.
:param thickness: Thickness of the new layer in mm.
"""
self.layers.append(thickness)
logger.debug(f"Added layer: {thickness} mm. Total layers: {self.layers}")
def remove_layer(self, index: int):
"""
Remove a layer from the pavement structure by index.
:param index: Index of the layer to remove.
"""
if 0 <= index < len(self.layers):
removed = self.layers.pop(index)
logger.debug(f"Removed layer at index {index}: {removed} mm. Remaining layers: {self.layers}")
else:
logger.warning(f"Attempted to remove non-existent layer at index {index}.")
def set_pavement_type(self, pavement_type: str):
"""
Set the type of pavement.
:param pavement_type: Type of pavement ('Flexible', 'Rigid', 'Composite').
"""
if pavement_type in ['Flexible', 'Rigid', 'Composite']:
self.pavement_type = pavement_type
logger.debug(f"Pavement type set to {pavement_type}.")
else:
logger.error(f"Invalid pavement type: {pavement_type}. Must be 'Flexible', 'Rigid', or 'Composite'.")
raise ValueError("Invalid pavement type. Choose from 'Flexible', 'Rigid', or 'Composite'.")