PrithviGuardian / client /src /lib /smartCalculators.ts
Varad Bakshi
Fresh upload
e7427b5
Raw
History Blame Contribute Delete
23.8 kB
// Smart Environmental & Civil Engineering Calculators
// Comprehensive calculation library with exception handling
interface CalculationResult {
value: number;
unit: string;
category: string;
recommendation?: string;
confidence: number;
warnings?: string[];
}
interface WaterQualityParams {
pH?: number;
bod?: number;
cod?: number;
tds?: number;
turbidity?: number;
chloride?: number;
hardness?: number;
temperature?: number;
}
interface AirQualityParams {
pm25?: number;
pm10?: number;
so2?: number;
no2?: number;
co?: number;
o3?: number;
temperature?: number;
humidity?: number;
}
interface SoilParams {
ph?: number;
nitrogen?: number;
phosphorus?: number;
potassium?: number;
organicCarbon?: number;
moisture?: number;
electricalConductivity?: number;
}
class SmartCalculators {
// Water Quality Analysis
calculateWaterQuality(params: WaterQualityParams): CalculationResult[] {
try {
const results: CalculationResult[] = [];
const warnings: string[] = [];
// Validate inputs
this.validateNumericParams(params, 'Water Quality');
// pH Analysis
if (params.pH !== undefined) {
const phResult = this.analyzePH(params.pH);
results.push(phResult);
if (phResult.warnings) warnings.push(...phResult.warnings);
}
// BOD Analysis
if (params.bod !== undefined) {
const bodResult = this.analyzeBOD(params.bod);
results.push(bodResult);
if (bodResult.warnings) warnings.push(...bodResult.warnings);
}
// COD Analysis
if (params.cod !== undefined) {
const codResult = this.analyzeCOD(params.cod);
results.push(codResult);
if (codResult.warnings) warnings.push(...codResult.warnings);
}
// TDS Analysis
if (params.tds !== undefined) {
const tdsResult = this.analyzeTDS(params.tds);
results.push(tdsResult);
if (tdsResult.warnings) warnings.push(...tdsResult.warnings);
}
// Overall Water Quality Index
if (results.length > 0) {
const wqiResult = this.calculateWQI(params);
results.push(wqiResult);
}
return results;
} catch (error) {
throw new Error(`Water quality calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Air Quality Analysis
calculateAirQuality(params: AirQualityParams): CalculationResult[] {
try {
const results: CalculationResult[] = [];
this.validateNumericParams(params, 'Air Quality');
// PM2.5 Analysis
if (params.pm25 !== undefined) {
results.push(this.analyzePM25(params.pm25));
}
// PM10 Analysis
if (params.pm10 !== undefined) {
results.push(this.analyzePM10(params.pm10));
}
// Overall AQI
if (params.pm25 !== undefined || params.pm10 !== undefined) {
const aqiResult = this.calculateAQI(params);
results.push(aqiResult);
}
return results;
} catch (error) {
throw new Error(`Air quality calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Soil Quality Analysis
calculateSoilQuality(params: SoilParams): CalculationResult[] {
try {
const results: CalculationResult[] = [];
this.validateNumericParams(params, 'Soil Quality');
// Soil pH Analysis
if (params.ph !== undefined) {
results.push(this.analyzeSoilPH(params.ph));
}
// NPK Analysis
if (params.nitrogen !== undefined || params.phosphorus !== undefined || params.potassium !== undefined) {
results.push(this.analyzeNPK(params));
}
// Soil Health Index
const soilHealthIndex = this.calculateSoilHealthIndex(params);
results.push(soilHealthIndex);
return results;
} catch (error) {
throw new Error(`Soil quality calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// NDVI Calculation
calculateNDVI(redBand: number, nirBand: number): CalculationResult {
try {
if (redBand === undefined || nirBand === undefined) {
throw new Error('Red and NIR band values are required for NDVI calculation');
}
if (redBand < 0 || nirBand < 0) {
throw new Error('Band values cannot be negative');
}
if (redBand + nirBand === 0) {
throw new Error('Cannot calculate NDVI: sum of bands is zero');
}
const ndvi = (nirBand - redBand) / (nirBand + redBand);
let category = 'Unknown';
let recommendation = '';
let confidence = 85;
if (ndvi < 0) {
category = 'Water/Snow';
recommendation = 'Indicates water bodies or snow-covered areas';
} else if (ndvi < 0.2) {
category = 'Bare Soil/Rock';
recommendation = 'Low vegetation coverage, consider soil conservation measures';
} else if (ndvi < 0.5) {
category = 'Sparse Vegetation';
recommendation = 'Moderate vegetation, suitable for grassland or agricultural areas';
} else if (ndvi < 0.8) {
category = 'Dense Vegetation';
recommendation = 'Healthy vegetation coverage, good for forest or agricultural productivity';
} else {
category = 'Very Dense Vegetation';
recommendation = 'Excellent vegetation health, optimal growing conditions';
}
return {
value: parseFloat(ndvi.toFixed(4)),
unit: 'index',
category,
recommendation,
confidence,
warnings: ndvi < 0.1 ? ['Very low vegetation coverage detected'] : undefined
};
} catch (error) {
throw new Error(`NDVI calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Load Estimation for Structures
calculateLoadEstimation(params: {
deadLoad?: number;
liveLoad?: number;
windLoad?: number;
seismicLoad?: number;
structureType?: string;
}): CalculationResult {
try {
this.validateNumericParams(params, 'Load Estimation');
const { deadLoad = 0, liveLoad = 0, windLoad = 0, seismicLoad = 0, structureType = 'general' } = params;
// Load combinations as per IS 875
const combinations = [
1.5 * (deadLoad + liveLoad), // Basic combination
1.2 * (deadLoad + liveLoad + windLoad), // Wind combination
1.2 * (deadLoad + liveLoad + seismicLoad), // Seismic combination
0.9 * deadLoad + 1.5 * windLoad, // Wind uplift
];
const designLoad = Math.max(...combinations);
let recommendation = '';
const safetyFactor = designLoad / (deadLoad + liveLoad || 1);
if (safetyFactor > 2.5) {
recommendation = 'Over-designed structure, consider optimization';
} else if (safetyFactor > 1.5) {
recommendation = 'Safe design with adequate safety margin';
} else {
recommendation = 'Consider increasing safety factors or reviewing design';
}
return {
value: parseFloat(designLoad.toFixed(2)),
unit: 'kN',
category: 'Structural Load',
recommendation,
confidence: 90,
warnings: safetyFactor < 1.5 ? ['Low safety factor detected'] : undefined
};
} catch (error) {
throw new Error(`Load estimation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Stormwater Management
calculateStormwaterRunoff(params: {
area?: number;
rainfallIntensity?: number;
runoffCoefficient?: number;
timeOfConcentration?: number;
}): CalculationResult {
try {
this.validateNumericParams(params, 'Stormwater');
const { area, rainfallIntensity, runoffCoefficient = 0.5, timeOfConcentration } = params;
if (!area || !rainfallIntensity) {
throw new Error('Area and rainfall intensity are required for stormwater calculation');
}
if (area <= 0 || rainfallIntensity <= 0) {
throw new Error('Area and rainfall intensity must be positive values');
}
if (runoffCoefficient < 0 || runoffCoefficient > 1) {
throw new Error('Runoff coefficient must be between 0 and 1');
}
// Rational method: Q = CiA
const runoff = runoffCoefficient * rainfallIntensity * area;
let recommendation = '';
if (runoffCoefficient > 0.8) {
recommendation = 'High runoff coefficient - consider implementing green infrastructure';
} else if (runoffCoefficient > 0.5) {
recommendation = 'Moderate runoff - adequate drainage system required';
} else {
recommendation = 'Low runoff coefficient - natural infiltration is significant';
}
return {
value: parseFloat(runoff.toFixed(2)),
unit: 'm³/hr',
category: 'Stormwater Runoff',
recommendation,
confidence: 85,
warnings: runoff > 1000 ? ['High runoff volume - flood risk assessment recommended'] : undefined
};
} catch (error) {
throw new Error(`Stormwater calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Noise Level Analysis
calculateNoiseLevel(params: {
soundLevels?: number[];
timeWeighted?: boolean;
zoneType?: 'residential' | 'commercial' | 'industrial' | 'silent';
}): CalculationResult {
try {
const { soundLevels, timeWeighted = false, zoneType = 'residential' } = params;
if (!soundLevels || soundLevels.length === 0) {
throw new Error('Sound level measurements are required');
}
soundLevels.forEach((level, index) => {
if (typeof level !== 'number' || level < 0 || level > 150) {
throw new Error(`Invalid sound level at position ${index + 1}: must be between 0-150 dB`);
}
});
// Calculate equivalent noise level
let leq: number;
if (timeWeighted) {
// Time-weighted average
const sum = soundLevels.reduce((acc, level) => acc + Math.pow(10, level / 10), 0);
leq = 10 * Math.log10(sum / soundLevels.length);
} else {
// Simple average
leq = soundLevels.reduce((acc, level) => acc + level, 0) / soundLevels.length;
}
// Noise limits as per CPCB norms
const limits = {
residential: { day: 55, night: 45 },
commercial: { day: 65, night: 55 },
industrial: { day: 75, night: 70 },
silent: { day: 50, night: 40 }
};
const limit = limits[zoneType];
let recommendation = '';
let warnings: string[] = [];
if (leq > limit.day) {
warnings.push(`Exceeds daytime noise limit for ${zoneType} zone (${limit.day} dB)`);
recommendation = 'Noise control measures required - consider sound barriers or source control';
} else if (leq > limit.night) {
warnings.push(`Exceeds nighttime noise limit for ${zoneType} zone (${limit.night} dB)`);
recommendation = 'Moderate noise levels - monitor during night hours';
} else {
recommendation = 'Noise levels within acceptable limits';
}
return {
value: parseFloat(leq.toFixed(1)),
unit: 'dB(A)',
category: 'Noise Level',
recommendation,
confidence: 88,
warnings: warnings.length > 0 ? warnings : undefined
};
} catch (error) {
throw new Error(`Noise level calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Private helper methods
private validateNumericParams(params: any, calculationType: string): void {
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && typeof value !== 'number') {
throw new Error(`${calculationType}: Parameter '${key}' must be a number`);
}
if (value !== undefined && (isNaN(value) || !isFinite(value))) {
throw new Error(`${calculationType}: Parameter '${key}' must be a valid finite number`);
}
}
}
private analyzePH(ph: number): CalculationResult {
let category = '';
let recommendation = '';
const warnings: string[] = [];
if (ph < 0 || ph > 14) {
throw new Error('pH must be between 0 and 14');
}
if (ph < 6.5) {
category = 'Acidic';
recommendation = 'Consider pH adjustment with lime or alkaline treatment';
if (ph < 4) warnings.push('Highly acidic - may be corrosive');
} else if (ph > 8.5) {
category = 'Alkaline';
recommendation = 'Consider pH adjustment with acid treatment';
if (ph > 11) warnings.push('Highly alkaline - may be harmful');
} else {
category = 'Neutral';
recommendation = 'pH within acceptable range for most applications';
}
return {
value: ph,
unit: 'pH units',
category,
recommendation,
confidence: 95,
warnings: warnings.length > 0 ? warnings : undefined
};
}
private analyzeBOD(bod: number): CalculationResult {
if (bod < 0) throw new Error('BOD cannot be negative');
let category = '';
let recommendation = '';
const warnings: string[] = [];
if (bod < 3) {
category = 'Clean Water';
recommendation = 'Excellent water quality';
} else if (bod < 6) {
category = 'Slightly Polluted';
recommendation = 'Good water quality with minor treatment needed';
} else if (bod < 30) {
category = 'Moderately Polluted';
recommendation = 'Secondary treatment required';
warnings.push('Moderate pollution detected');
} else {
category = 'Heavily Polluted';
recommendation = 'Advanced treatment required before discharge';
warnings.push('High pollution levels - immediate action needed');
}
return {
value: bod,
unit: 'mg/L',
category,
recommendation,
confidence: 90,
warnings: warnings.length > 0 ? warnings : undefined
};
}
private analyzeCOD(cod: number): CalculationResult {
if (cod < 0) throw new Error('COD cannot be negative');
let category = '';
let recommendation = '';
if (cod < 10) {
category = 'Clean Water';
recommendation = 'Excellent water quality';
} else if (cod < 50) {
category = 'Lightly Polluted';
recommendation = 'Minor treatment may be required';
} else if (cod < 150) {
category = 'Moderately Polluted';
recommendation = 'Treatment required before discharge';
} else {
category = 'Heavily Polluted';
recommendation = 'Advanced treatment necessary';
}
return {
value: cod,
unit: 'mg/L',
category,
recommendation,
confidence: 90
};
}
private analyzeTDS(tds: number): CalculationResult {
if (tds < 0) throw new Error('TDS cannot be negative');
let category = '';
let recommendation = '';
if (tds < 500) {
category = 'Excellent';
recommendation = 'Suitable for drinking and most applications';
} else if (tds < 1000) {
category = 'Good';
recommendation = 'Generally acceptable for most uses';
} else if (tds < 2000) {
category = 'Fair';
recommendation = 'May require treatment for drinking water';
} else {
category = 'Poor';
recommendation = 'Treatment required before use';
}
return {
value: tds,
unit: 'mg/L',
category,
recommendation,
confidence: 88
};
}
private calculateWQI(params: WaterQualityParams): CalculationResult {
let wqi = 100;
let factors = 0;
// Simplified WQI calculation
if (params.pH !== undefined) {
const phScore = params.pH >= 6.5 && params.pH <= 8.5 ? 100 : Math.max(0, 100 - Math.abs(7 - params.pH) * 20);
wqi = (wqi * factors + phScore) / (factors + 1);
factors++;
}
if (params.bod !== undefined) {
const bodScore = Math.max(0, 100 - params.bod * 3);
wqi = (wqi * factors + bodScore) / (factors + 1);
factors++;
}
let category = '';
if (wqi > 80) category = 'Excellent';
else if (wqi > 60) category = 'Good';
else if (wqi > 40) category = 'Fair';
else category = 'Poor';
return {
value: parseFloat(wqi.toFixed(1)),
unit: 'WQI',
category: `Water Quality: ${category}`,
recommendation: wqi > 60 ? 'Water quality is acceptable' : 'Water treatment recommended',
confidence: 85
};
}
private analyzePM25(pm25: number): CalculationResult {
if (pm25 < 0) throw new Error('PM2.5 cannot be negative');
let category = '';
let recommendation = '';
const warnings: string[] = [];
if (pm25 <= 12) {
category = 'Good';
recommendation = 'Air quality is satisfactory';
} else if (pm25 <= 35.4) {
category = 'Moderate';
recommendation = 'Acceptable for most people';
} else if (pm25 <= 55.4) {
category = 'Unhealthy for Sensitive Groups';
recommendation = 'Sensitive individuals should limit outdoor exposure';
warnings.push('Sensitive groups should take precautions');
} else {
category = 'Unhealthy';
recommendation = 'Everyone should limit outdoor activities';
warnings.push('Poor air quality - health advisory in effect');
}
return {
value: pm25,
unit: 'μg/m³',
category,
recommendation,
confidence: 92,
warnings: warnings.length > 0 ? warnings : undefined
};
}
private analyzePM10(pm10: number): CalculationResult {
if (pm10 < 0) throw new Error('PM10 cannot be negative');
let category = '';
let recommendation = '';
if (pm10 <= 54) {
category = 'Good';
recommendation = 'Air quality is satisfactory';
} else if (pm10 <= 154) {
category = 'Moderate';
recommendation = 'Acceptable for most people';
} else if (pm10 <= 254) {
category = 'Unhealthy for Sensitive Groups';
recommendation = 'Sensitive individuals should limit outdoor exposure';
} else {
category = 'Unhealthy';
recommendation = 'Everyone should limit outdoor activities';
}
return {
value: pm10,
unit: 'μg/m³',
category,
recommendation,
confidence: 90
};
}
private calculateAQI(params: AirQualityParams): CalculationResult {
let maxAqi = 0;
let dominantPollutant = '';
if (params.pm25 !== undefined) {
const pm25Aqi = this.convertToAQI(params.pm25, 'PM2.5');
if (pm25Aqi > maxAqi) {
maxAqi = pm25Aqi;
dominantPollutant = 'PM2.5';
}
}
if (params.pm10 !== undefined) {
const pm10Aqi = this.convertToAQI(params.pm10, 'PM10');
if (pm10Aqi > maxAqi) {
maxAqi = pm10Aqi;
dominantPollutant = 'PM10';
}
}
let category = '';
if (maxAqi <= 50) category = 'Good';
else if (maxAqi <= 100) category = 'Moderate';
else if (maxAqi <= 150) category = 'Unhealthy for Sensitive Groups';
else if (maxAqi <= 200) category = 'Unhealthy';
else category = 'Very Unhealthy';
return {
value: Math.round(maxAqi),
unit: 'AQI',
category: `Air Quality: ${category}`,
recommendation: `Dominant pollutant: ${dominantPollutant}. ${category === 'Good' ? 'Air quality is acceptable' : 'Consider limiting outdoor activities'}`,
confidence: 88
};
}
private convertToAQI(concentration: number, pollutant: string): number {
// Simplified AQI calculation based on EPA standards
const breakpoints = {
'PM2.5': [
[0, 12, 0, 50],
[12.1, 35.4, 51, 100],
[35.5, 55.4, 101, 150],
[55.5, 150.4, 151, 200]
],
'PM10': [
[0, 54, 0, 50],
[55, 154, 51, 100],
[155, 254, 101, 150],
[255, 354, 151, 200]
]
};
const points = breakpoints[pollutant as keyof typeof breakpoints];
if (!points) return 0;
for (const [cLow, cHigh, aqiLow, aqiHigh] of points) {
if (concentration >= cLow && concentration <= cHigh) {
return ((aqiHigh - aqiLow) / (cHigh - cLow)) * (concentration - cLow) + aqiLow;
}
}
return points[points.length - 1][3]; // Return max AQI if above all ranges
}
private analyzeSoilPH(ph: number): CalculationResult {
if (ph < 0 || ph > 14) throw new Error('Soil pH must be between 0 and 14');
let category = '';
let recommendation = '';
if (ph < 5.5) {
category = 'Highly Acidic';
recommendation = 'Lime application recommended to raise pH';
} else if (ph < 6.5) {
category = 'Moderately Acidic';
recommendation = 'Consider lime application for most crops';
} else if (ph < 7.5) {
category = 'Neutral';
recommendation = 'Optimal pH range for most crops';
} else if (ph < 8.5) {
category = 'Slightly Alkaline';
recommendation = 'Generally acceptable, monitor nutrient availability';
} else {
category = 'Highly Alkaline';
recommendation = 'Consider sulfur application to lower pH';
}
return {
value: ph,
unit: 'pH units',
category,
recommendation,
confidence: 94
};
}
private analyzeNPK(params: SoilParams): CalculationResult {
const { nitrogen = 0, phosphorus = 0, potassium = 0 } = params;
if (nitrogen < 0 || phosphorus < 0 || potassium < 0) {
throw new Error('NPK values cannot be negative');
}
// NPK rating based on typical soil test values
const nRating = nitrogen > 40 ? 'High' : nitrogen > 20 ? 'Medium' : 'Low';
const pRating = phosphorus > 25 ? 'High' : phosphorus > 15 ? 'Medium' : 'Low';
const kRating = potassium > 150 ? 'High' : potassium > 100 ? 'Medium' : 'Low';
const totalNPK = nitrogen + phosphorus + potassium;
let recommendation = `N: ${nRating}, P: ${pRating}, K: ${kRating}. `;
if (nRating === 'Low') recommendation += 'Nitrogen fertilization recommended. ';
if (pRating === 'Low') recommendation += 'Phosphorus supplementation needed. ';
if (kRating === 'Low') recommendation += 'Potassium application suggested.';
return {
value: parseFloat(totalNPK.toFixed(1)),
unit: 'kg/ha',
category: 'NPK Analysis',
recommendation: recommendation.trim(),
confidence: 87
};
}
private calculateSoilHealthIndex(params: SoilParams): CalculationResult {
let score = 0;
let factors = 0;
// pH contribution
if (params.ph !== undefined) {
const phScore = params.ph >= 6.0 && params.ph <= 7.5 ? 100 : Math.max(0, 100 - Math.abs(6.75 - params.ph) * 20);
score += phScore;
factors++;
}
// Organic carbon contribution
if (params.organicCarbon !== undefined) {
const ocScore = Math.min(100, params.organicCarbon * 20);
score += ocScore;
factors++;
}
// Moisture contribution
if (params.moisture !== undefined) {
const moistureScore = params.moisture >= 15 && params.moisture <= 25 ? 100 : Math.max(0, 100 - Math.abs(20 - params.moisture) * 5);
score += moistureScore;
factors++;
}
const healthIndex = factors > 0 ? score / factors : 50;
let category = '';
if (healthIndex > 80) category = 'Excellent';
else if (healthIndex > 60) category = 'Good';
else if (healthIndex > 40) category = 'Fair';
else category = 'Poor';
return {
value: parseFloat(healthIndex.toFixed(1)),
unit: 'index',
category: `Soil Health: ${category}`,
recommendation: healthIndex > 60 ? 'Soil health is adequate' : 'Soil improvement measures recommended',
confidence: 83
};
}
}
export const smartCalculators = new SmartCalculators();
export { CalculationResult, WaterQualityParams, AirQualityParams, SoilParams };