Calculating Soils Percent Saturation Using Unit Weight and Specific Gravity

Soil Percent Saturation Calculator – Unit Weight & Specific Gravity :root { –primary-color: #004a99; –success-color: #28a745; –background-color: #f8f9fa; –text-color: #333; –border-color: #ddd; –shadow-color: rgba(0, 0, 0, 0.1); –input-bg: #fff; –input-border: #ccc; –error-color: #dc3545; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: var(–background-color); color: var(–text-color); line-height: 1.6; margin: 0; padding: 0; display: flex; flex-direction: column; align-items: center; padding-top: 20px; padding-bottom: 40px; } .container { width: 100%; max-width: 980px; background-color: #fff; padding: 30px; border-radius: 8px; box-shadow: 0 4px 15px var(–shadow-color); margin-bottom: 30px; } h1, h2, h3 { color: var(–primary-color); text-align: center; margin-bottom: 20px; } h1 { font-size: 2.2em; margin-top: 0; } h2 { font-size: 1.8em; border-bottom: 2px solid var(–primary-color); padding-bottom: 10px; } h3 { font-size: 1.4em; margin-top: 25px; margin-bottom: 15px; } .calculator-wrapper { background-color: var(–background-color); padding: 25px; border-radius: 8px; border: 1px solid var(–border-color); margin-bottom: 30px; } .input-group { margin-bottom: 20px; text-align: left; } .input-group label { display: block; margin-bottom: 8px; font-weight: bold; color: var(–primary-color); } .input-group input[type="number"], .input-group select { width: calc(100% – 22px); padding: 12px 10px; border: 1px solid var(–input-border); border-radius: 5px; font-size: 1em; background-color: var(–input-bg); box-sizing: border-box; transition: border-color 0.3s ease; } .input-group input[type="number"]:focus, .input-group select:focus { outline: none; border-color: var(–primary-color); } .input-group .helper-text { font-size: 0.85em; color: #6c757d; margin-top: 5px; display: block; } .error-message { color: var(–error-color); font-size: 0.8em; margin-top: 5px; min-height: 1.2em; /* Prevent layout shift */ } .button-group { display: flex; justify-content: space-between; margin-top: 25px; flex-wrap: wrap; gap: 10px; } .button-group button { padding: 12px 25px; border: none; border-radius: 5px; cursor: pointer; font-size: 1em; font-weight: bold; transition: background-color 0.3s ease, transform 0.2s ease; flex: 1; min-width: 150px; } .calculate-button { background-color: var(–primary-color); color: white; } .calculate-button:hover { background-color: #003366; transform: translateY(-2px); } .reset-button, .copy-button { background-color: #6c757d; color: white; } .reset-button:hover, .copy-button:hover { background-color: #5a6268; transform: translateY(-2px); } .results-wrapper { margin-top: 30px; padding: 25px; border: 1px solid var(–border-color); border-radius: 8px; background-color: #f0f0f0; text-align: center; } .primary-result { font-size: 2.5em; font-weight: bold; color: var(–success-color); margin-bottom: 15px; display: inline-block; padding: 10px 20px; background-color: var(–primary-color); color: white; border-radius: 5px; } .results-wrapper h3 { margin-top: 0; color: var(–primary-color); text-align: center; } .intermediate-results div, .formula-explanation { margin-bottom: 10px; font-size: 1.1em; } .intermediate-results span { font-weight: bold; color: var(–primary-color); } .formula-explanation { font-style: italic; color: #555; } table { width: 100%; border-collapse: collapse; margin-top: 20px; box-shadow: 0 2px 8px var(–shadow-color); } th, td { padding: 12px 15px; text-align: left; border: 1px solid var(–border-color); } thead { background-color: var(–primary-color); color: white; } tbody tr:nth-child(even) { background-color: #f2f2f2; } caption { font-size: 1.1em; font-weight: bold; color: var(–primary-color); margin-bottom: 10px; text-align: left; } .chart-container { width: 100%; max-width: 700px; margin: 30px auto; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 2px 10px var(–shadow-color); text-align: center; } canvas { max-width: 100%; height: auto; } .chart-caption { font-size: 1em; color: #555; margin-top: 15px; } .article-content { width: 100%; max-width: 980px; background-color: #fff; padding: 30px; border-radius: 8px; box-shadow: 0 4px 15px var(–shadow-color); margin-top: 30px; text-align: left; } .article-content p { margin-bottom: 15px; } .article-content a { color: var(–primary-color); text-decoration: none; font-weight: bold; } .article-content a:hover { text-decoration: underline; } .faq-item { margin-bottom: 15px; border-bottom: 1px dashed var(–border-color); padding-bottom: 10px; } .faq-item:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; } .faq-question { font-weight: bold; color: var(–primary-color); cursor: pointer; margin-bottom: 5px; display: block; } .faq-answer { font-size: 0.95em; color: #555; } .related-links ul { list-style: none; padding: 0; } .related-links li { margin-bottom: 10px; } .related-links a { font-weight: bold; } .related-links span { font-size: 0.9em; color: #666; display: block; margin-top: 3px; } @media (max-width: 768px) { .container, .article-content { padding: 20px; } .button-group button { flex: none; width: 100%; } h1 { font-size: 1.8em; } h2 { font-size: 1.5em; } .primary-result { font-size: 2em; } }

Soil Percent Saturation Calculator

Determine the degree of water saturation in soil using key geotechnical properties.

Soil Percent Saturation Calculator

The total weight of soil and water per unit volume (e.g., kN/m³ or pcf).
The weight of solid soil particles per unit volume (e.g., kN/m³ or pcf).
The ratio of the unit weight of soil solids to the unit weight of water. Dimensionless.

Calculation Results

— %
Void Ratio (e):
Water Content (w):
Degree of Saturation (S):
Formula: S = (w * Gs) / e
Where: S = Degree of Saturation, w = Water Content, Gs = Specific Gravity, e = Void Ratio.
Note: Water content (w) is derived from γm, γd. Void ratio (e) is derived from γd, Gs.
Soil Saturation vs. Water Content and Void Ratio

Understanding Soil Percent Saturation: A Comprehensive Guide

What is Soil Percent Saturation?

Soil percent saturation, often denoted by 'S', is a fundamental geotechnical parameter that quantifies the proportion of the soil's void space that is filled with water. It's expressed as a percentage, ranging from 0% (completely dry soil) to 100% (fully saturated soil, where all voids are filled with water). This value is crucial in various engineering applications, including foundation design, slope stability analysis, and groundwater flow studies, as it significantly impacts soil strength, compressibility, and permeability. Understanding soil percent saturation helps engineers predict how a soil will behave under load and in the presence of water.

Who should use it? Geotechnical engineers, civil engineers, environmental engineers, soil scientists, geologists, and construction professionals commonly use the concept and calculation of soil percent saturation. It is also valuable for researchers studying soil mechanics and hydrology.

Common Misconceptions: A common misconception is that a high moisture content automatically means high saturation. While related, they are not the same. Soil can have a high water content but still be unsaturated if the voids are not completely filled with water. Another misconception is that saturation is solely dependent on rainfall; it's also influenced by the soil's physical properties, such as its void ratio and particle size distribution.

Soil Percent Saturation Formula and Mathematical Explanation

The degree of saturation (S) can be calculated using the following relationship derived from the fundamental definitions of soil properties:

Formula: S = (w * Gs) / e

Where:

  • S: Degree of Saturation (expressed as a decimal, multiplied by 100 for percentage).
  • w: Water Content (ratio of the mass of water to the mass of solids, usually expressed as a decimal).
  • Gs: Specific Gravity of Soil Solids (dimensionless).
  • e: Void Ratio (ratio of the volume of voids to the volume of solids, dimensionless).

In many practical scenarios, you might not have 'w' and 'e' directly. Instead, you might have the moist unit weight (γm), dry unit weight (γd), and specific gravity (Gs). We can derive 'w' and 'e' from these:

Derivation of Water Content (w): Water Content (w) = (Mass of Water / Mass of Solids) We know that: γm = (Total Weight) / (Total Volume) = (Weight of Solids + Weight of Water) / (Total Volume) γd = (Weight of Solids) / (Total Volume) So, Weight of Water = γm * (Total Volume) – γd * (Total Volume) = (γm – γd) * (Total Volume) Weight of Solids = γd * (Total Volume) Therefore, w = [(γm – γd) * (Total Volume)] / [γd * (Total Volume)] = (γm – γd) / γd

Derivation of Void Ratio (e): We know that the unit weight of water (γw) is approximately 9.81 kN/m³ or 62.4 pcf. Relationship between unit weight, specific gravity, and void ratio: γd = (Gs * γw) / (1 + e) Rearranging to solve for 'e': (1 + e) = (Gs * γw) / γd e = (Gs * γw) / γd – 1 However, a more direct calculation using γm and γd without assuming γw is possible if we consider the void ratio definition directly. A simpler approach is to use the relationship between moist unit weight and dry unit weight: γm = γd * (1 + (w * Gs)) / (1 + e) — This is often complicated. A more common approach is to find 'e' using γd and Gs: We know that γd = (Gs * γw) / (1 + e). If we use the unit weight of water (γw = 9.81 kN/m³ or 62.4 pcf), we can solve for 'e'. Let's use a simplified approach where the ratio of moist to dry unit weight relates to water content and void ratio without explicitly using γw, by first finding w and then using S = (w * Gs) / e where e needs to be derived. A direct formula without explicitly calculating 'e' or 'w' is not standard. The provided calculator uses the standard formula S = (w*Gs)/e and calculates w and e from the inputs. Let's refine the calculation steps to be clearer: 1. Calculate Water Content (w): w = (γm – γd) / γd 2. Calculate Void Ratio (e): This requires the unit weight of water (γw). Standard values are ~9.81 kN/m³ or ~62.4 pcf. We use the relationship: γd = (Gs * γw) / (1 + e) Rearranging for e: e = (Gs * γw) / γd – 1 *Note: The calculator will need to infer γw or assume a standard value.* Let's assume the units imply a standard γw. If inputs are in kN/m³, use γw = 9.81. If in pcf, use γw = 62.4. The calculator will use a placeholder for γw for flexibility but in a real implementation, unit consistency is key. For the calculator, we'll use standard values implicitly. If γm and γd are in kN/m³, γw = 9.81 kN/m³. If γm and γd are in pcf, γw = 62.4 pcf. 3. Calculate Degree of Saturation (S): S = (w * Gs) / e *Simplified approach for the calculator to avoid assuming γw units:* The calculator implicitly calculates S using derived 'w' and 'e'. Let's trace the direct inputs to the output. Given: γm, γd, Gs Derived: w = (γm – γd) / γd e = (Gs * γw) / γd – 1 –> Let's use a common ratio approach if possible to avoid γw. A more robust calculation: Volume of Solids (Vs) = Weight of Solids (Ws) / Gs * γw Total Volume (Vt) = Weight of Solids (Ws) / γd Volume of Voids (Vv) = Vt – Vs = (Ws / γd) – (Ws / Gs * γw) = Ws * [1/γd – 1/(Gs*γw)] Void Ratio (e) = Vv / Vs = { Ws * [1/γd – 1/(Gs*γw)] } / { Ws / (Gs*γw) } = [1/γd – 1/(Gs*γw)] / [1/(Gs*γw)] e = (Gs*γw)/γd – 1 –> This still requires γw. Let's stick to the calculation provided in the JS, which is standard: w = (γm – γd) / γd e = (Gs * γw) / γd – 1 (Assuming γw is consistent with γm and γd units) S = (w * Gs) / e The calculator code below needs to implement this, acknowledging the implicit γw.

Soil Property Variables
Variable Meaning Unit Typical Range
γm (Moist Unit Weight) Total weight of soil (solids + water) per unit volume kN/m³ or pcf 15 – 22 (kN/m³) / 90 – 140 (pcf)
γd (Dry Unit Weight) Weight of solid soil particles per unit volume kN/m³ or pcf 12 – 19 (kN/m³) / 75 – 120 (pcf)
Gs (Specific Gravity) Ratio of unit weight of soil solids to unit weight of water Dimensionless 2.5 – 2.8 (common for most soils)
w (Water Content) Ratio of mass of water to mass of solids Decimal or % 0 – 1+ (depends heavily on soil type and conditions)
e (Void Ratio) Ratio of volume of voids to volume of solids Dimensionless 0.1 – 1.5+ (highly variable)
S (Degree of Saturation) Ratio of volume of water to volume of voids Decimal or % 0% – 100%

Practical Examples (Real-World Use Cases)

Example 1: Foundation Bearing Capacity Assessment A civil engineer is assessing the bearing capacity of a clayey soil for a new building foundation. The soil exhibits a moist unit weight (γm) of 19.0 kN/m³ and a dry unit weight (γd) of 15.5 kN/m³. The specific gravity of the soil solids (Gs) is measured to be 2.70. Using the calculator: Inputs: Moist Unit Weight (γm) = 19.0 kN/m³ Dry Unit Weight (γd) = 15.5 kN/m³ Specific Gravity (Gs) = 2.70 Calculated Intermediate Values: Water Content (w) = (19.0 – 15.5) / 15.5 ≈ 0.2258 or 22.58% Void Ratio (e) = (2.70 * 9.81) / 15.5 – 1 ≈ 1.704 – 1 ≈ 0.704 Calculated Primary Result: Degree of Saturation (S) = (0.2258 * 2.70) / 0.704 ≈ 0.867 or 86.7% Interpretation: The soil is significantly saturated (86.7%). This indicates that a substantial portion of the pore space is filled with water. High saturation can reduce effective stress and shear strength, potentially impacting foundation stability. The engineer will need to account for this in their bearing capacity calculations, possibly requiring deeper foundations or soil improvement techniques.

Example 2: Embankment Construction Stability A geotechnical engineer is designing an earth embankment for a highway project. The selected fill material has a target dry unit weight (γd) of 18.0 kN/m³ and a specific gravity (Gs) of 2.65. During compaction, the field density tests show a moist unit weight (γm) of 20.5 kN/m³. Using the calculator: Inputs: Moist Unit Weight (γm) = 20.5 kN/m³ Dry Unit Weight (γd) = 18.0 kN/m³ Specific Gravity (Gs) = 2.65 Calculated Intermediate Values: Water Content (w) = (20.5 – 18.0) / 18.0 ≈ 0.1389 or 13.89% Void Ratio (e) = (2.65 * 9.81) / 18.0 – 1 ≈ 1.445 – 1 ≈ 0.445 Calculated Primary Result: Degree of Saturation (S) = (0.1389 * 2.65) / 0.445 ≈ 0.828 or 82.8% Interpretation: The fill material is highly saturated (82.8%) at the achieved compaction level. This could potentially lead to issues with long-term settlement or reduced shear strength under seismic loading. The engineer may need to adjust the compaction procedure, water content during placement, or implement drainage measures within the embankment to manage saturation levels and ensure long-term stability. This analysis helps in adhering to critical geotechnical engineering standards.

How to Use This Soil Percent Saturation Calculator

Our Soil Percent Saturation Calculator is designed for ease of use, providing accurate results for geotechnical analysis. Follow these simple steps:

  1. Input Moist Unit Weight (γm): Enter the measured moist unit weight of the soil sample. This represents the total weight of the soil, including both solids and water, per unit volume. Ensure you are using consistent units (e.g., kN/m³ or pcf).
  2. Input Dry Unit Weight (γd): Enter the measured dry unit weight of the soil. This is the weight of the solid soil particles alone, per unit volume. It should be less than the moist unit weight.
  3. Input Specific Gravity (Gs): Input the specific gravity of the soil solids. This value is typically around 2.65 for most common mineral soils and is dimensionless.
  4. Click 'Calculate': Once all values are entered, click the 'Calculate' button.

How to Read Results: The calculator will display:

  • Primary Result (Degree of Saturation S): This is the main output, shown prominently in percentage (%). It indicates how full the soil voids are with water.
  • Intermediate Values: You'll also see the calculated Void Ratio (e) and Water Content (w) of the soil, which are essential components of the saturation calculation.
  • Formula Explanation: A brief explanation of the core formula (S = w * Gs / e) and how the intermediate values are derived.

Decision-Making Guidance: A saturation level of 100% means the soil is fully saturated. Levels below 100% indicate unsaturated conditions. High saturation (above 70-80%) often implies lower shear strength, higher compressibility, and potentially slower drainage. Low saturation suggests drier conditions with typically higher strength but lower permeability. These results inform decisions regarding soil suitability for construction, compaction requirements, irrigation needs, and potential risks like liquefaction (in saturated granular soils under seismic loads). Understanding these nuances is key to successful geotechnical project management.

Key Factors That Affect Soil Percent Saturation Results

Several factors influence the degree of saturation in soil, impacting its engineering behavior:

  • Water Table Depth: The proximity of the groundwater table is a primary driver. Soils below the water table are typically fully saturated (S=100%). Soils above the water table are unsaturated, with saturation decreasing with increasing height due to capillary effects and evaporation.
  • Precipitation and Infiltration: Rainfall, snowmelt, and irrigation directly add water to the soil, increasing saturation. The rate of infiltration depends on soil permeability and surface conditions. Heavy rainfall can lead to saturation even in soils above the typical water table.
  • Drainage Conditions: The ability of water to drain away from the soil mass significantly affects saturation. Poorly drained soils (e.g., clays with low permeability) will retain moisture and exhibit higher saturation levels compared to well-drained soils (e.g., sands).
  • Soil Type and Texture: Fine-grained soils like clays and silts have smaller pores and can exhibit significant capillary rise, leading to higher degrees of saturation above the water table compared to coarse-grained soils like sands and gravels. The specific surface area and pore size distribution are critical.
  • Compaction Effort and Water Content: During construction, the compaction process determines the dry unit weight and void ratio. Compacting soil at a water content near the optimum moisture content typically achieves the densest state (lowest void ratio), which can influence saturation potential. Over-compaction or under-compaction can lead to different saturation characteristics. This is directly tied to achieving the required soil compaction standards.
  • Geological History and Soil Structure: Factors like the presence of fissures, layers, or soil fabric developed over geological time can create preferential pathways for water movement or retention, influencing the localized degree of saturation. Natural variations in soil properties are common.
  • Evaporation and Transpiration: In unsaturated soils, water can be lost to the atmosphere through evaporation from the surface and transpiration by plants. These processes reduce the degree of saturation, especially in arid or semi-arid climates.

Frequently Asked Questions (FAQ)

What is the difference between water content and degree of saturation?
Water content (w) is the ratio of the mass of water to the mass of dry soil solids. Degree of saturation (S) is the ratio of the volume of water to the volume of voids (pore space). Soil can have a high water content but be unsaturated if there is air in the pores. Conversely, a saturated soil (S=100%) has water filling all its voids.
Can the degree of saturation be greater than 100%?
By definition, the degree of saturation cannot exceed 100%. This signifies that all available void space is completely filled with water. Values reported slightly over 100% might indicate measurement errors or unusual conditions like entrapped air.
How does specific gravity (Gs) affect saturation?
Specific gravity (Gs) influences the relationship between the mass and volume of soil solids. In the formula S = (w * Gs) / e, a higher Gs means that for a given void ratio (e) and water content (w), the degree of saturation will be higher. This is because heavier soil solids (higher Gs) occupy less volume for the same mass, effectively meaning more pore space relative to solid volume, or that a given mass of water represents a larger fraction of the total mass.
What is the typical unit weight of water (γw) used in calculations?
The standard unit weight of water (γw) is approximately 9.81 kN/m³ in the metric system or 62.4 pcf (pounds per cubic foot) in the US customary system. The calculator implicitly uses the appropriate value based on the units of the input unit weights.
Why is soil percent saturation important in construction?
Saturation significantly affects soil properties like shear strength, compressibility, and permeability. Saturated soils generally have lower strength and higher settlement potential, which are critical considerations for foundation design, slope stability, and pavement engineering. Understanding soil mechanics principles is vital.
Can I use this calculator if my units are different (e.g., g/cm³)?
This calculator is designed for unit weights typically expressed in kN/m³ or pcf. For other units like g/cm³, you would need to convert them first to a consistent unit weight format. For example, 1 g/cm³ is equivalent to 9.81 kN/m³ or 62.4 pcf.
What does a void ratio of 'e' mean?
The void ratio (e) is the ratio of the volume of empty space (voids) to the volume of solid soil particles. A void ratio of 0.5 means the volume of voids is half the volume of solids. A higher void ratio indicates a looser, potentially more compressible soil.
How does saturation relate to effective stress?
Effective stress (the stress carried by the soil skeleton) is reduced by the presence of pore water pressure. In saturated soils, pore water pressure can increase significantly, especially under external loads or changes in groundwater level, leading to a decrease in effective stress and thus reduced soil strength. This is a cornerstone of geotechnical engineering analysis.

Related Tools and Internal Resources

// Global variable for chart instance to allow destruction and recreation var myChart = null; function calculateSoilSaturation() { // Get input values var moistUnitWeightInput = document.getElementById('moistUnitWeight'); var dryUnitWeightInput = document.getElementById('dryUnitWeight'); var specificGravityInput = document.getElementById('specificGravity'); // Error message elements var moistUnitWeightError = document.getElementById('moistUnitWeightError'); var dryUnitWeightError = document.getElementById('dryUnitWeightError'); var specificGravityError = document.getElementById('specificGravityError'); // Clear previous errors moistUnitWeightError.textContent = "; dryUnitWeightError.textContent = "; specificGravityError.textContent = "; // Convert inputs to numbers, handle potential NaN var gamma_m = parseFloat(moistUnitWeightInput.value); var gamma_d = parseFloat(dryUnitWeightInput.value); var Gs = parseFloat(specificGravityInput.value); // Basic validation var isValid = true; if (isNaN(gamma_m) || gamma_m <= 0) { moistUnitWeightError.textContent = 'Moist Unit Weight must be a positive number.'; isValid = false; } if (isNaN(gamma_d) || gamma_d <= 0) { dryUnitWeightError.textContent = 'Dry Unit Weight must be a positive number.'; isValid = false; } if (isNaN(Gs) || Gs = gamma_m) { dryUnitWeightError.textContent = 'Dry Unit Weight cannot be greater than or equal to Moist Unit Weight.'; isValid = false; } if (!isValid) { // Clear results if validation fails document.getElementById('result').textContent = '– %'; document.getElementById('voidRatioResult').textContent = '–'; document.getElementById('waterContentResult').textContent = '–'; document.getElementById('degreeOfSaturationResult').textContent = '–'; if (myChart) { myChart.destroy(); // Destroy previous chart if it exists myChart = null; } return; } // Constants for calculation – Assuming metric units for standard water unit weight // If inputs were in pcf, gamma_w should be 62.4 var gamma_w_metric = 9.81; // kN/m³ var gamma_w_imperial = 62.4; // pcf // Attempt to infer units – crude check based on typical ranges var gamma_w; if (gamma_m > 100 || gamma_d > 100) { // Likely pcf range gamma_w = gamma_w_imperial; } else { // Likely kN/m³ range gamma_w = gamma_w_metric; } // Calculate intermediate values var waterContent = (gamma_m – gamma_d) / gamma_d; var voidRatio = (Gs * gamma_w) / gamma_d – 1; // Prevent division by zero or negative void ratio in saturation calculation var degreeOfSaturation = 0; // Default to 0 if calculation is invalid if (voidRatio > 0 && !isNaN(voidRatio) && !isNaN(waterContent) && !isNaN(Gs)) { degreeOfSaturation = (waterContent * Gs) / voidRatio; } else { // Handle cases where void ratio calculation is problematic // This might happen if gamma_d is very small or Gs*gamma_w is not > gamma_d // For practical soil mechanics, these inputs usually yield valid void ratios. // If voidRatio is calculated as <= 0, it implies an unusual soil state or input error. console.warn("Invalid void ratio calculated. Cannot calculate degree of saturation accurately."); document.getElementById('voidRatioResult').textContent = 'Invalid'; degreeOfSaturation = NaN; // Indicate an invalid result } // Display results var resultElement = document.getElementById('result'); var voidRatioResultElement = document.getElementById('voidRatioResult'); var waterContentResultElement = document.getElementById('waterContentResult'); var degreeOfSaturationResultElement = document.getElementById('degreeOfSaturationResult'); if (!isNaN(degreeOfSaturation)) { resultElement.textContent = (degreeOfSaturation * 100).toFixed(1) + ' %'; degreeOfSaturationResultElement.textContent = (degreeOfSaturation * 100).toFixed(1) + ' %'; } else { resultElement.textContent = '– %'; degreeOfSaturationResultElement.textContent = '–'; } voidRatioResultElement.textContent = voidRatio.toFixed(3); waterContentResultElement.textContent = (waterContent * 100).toFixed(1) + ' %'; // Update Chart updateChart(gamma_m, gamma_d, Gs, waterContent, voidRatio, degreeOfSaturation); } function updateChart(gamma_m, gamma_d, Gs, w, e, S) { var ctx = document.getElementById('saturationChart').getContext('2d'); // Destroy previous chart instance if it exists if (myChart) { myChart.destroy(); } // Data for the chart var labels = []; var saturationValues = []; var waterContentValues = []; var voidRatioValues = []; // Generate data points for visualization // We'll vary one parameter (e.g., water content) and see how S and e change, // keeping Gs constant. This is a simplification for visualization. // A more complex chart might show 3D relationships or range plots. // Let's create a scenario where we see how S changes as W increases, // assuming e is relatively constant for a given soil type, OR // how S changes as e changes, assuming W is constant. // Scenario 1: Vary Water Content (w), Keep Gs and e constant (or derived e) // This requires recalculating S for various 'w' values. // Let's use the calculated 'e' from current inputs as a reference point. var base_w = w; var base_e = e; var base_Gs = Gs; var base_S = S; // Generate points around the current input values var numPoints = 10; var w_range = base_w * 0.5; // Show +/- 50% variation in water content for chart context var e_range = base_e * 0.5; // Show +/- 50% variation in void ratio for chart context // Charting strategy: Show how S changes if 'w' changes, assuming 'e' is fixed by gamma_d, Gs // Or show how S changes if 'e' changes, assuming 'w' is fixed by gamma_m, gamma_d. // Let's plot S vs W, and S vs e // For S vs W plot: Keep Gs and e constant. // For S vs e plot: Keep Gs and w constant. // Data Series 1: Degree of Saturation (S) vs Water Content (w) // Fix Gs and e based on inputs. Vary w. var waterContentPoints = []; var saturationFromW = []; for (var i = 0; i 0 && base_e > 0) { var current_S = (current_w * base_Gs) / base_e; if (current_S <= 1.0) { // Cap at 100% saturation waterContentPoints.push((current_w * 100).toFixed(1) + '%'); saturationFromW.push(current_S * 100); } else { waterContentPoints.push((current_w * 100).toFixed(1) + '%'); saturationFromW.push(100); // Cap saturation at 100% } } else { waterContentPoints.push((current_w * 100).toFixed(1) + '%'); saturationFromW.push(0); // Assume 0 saturation if w or e is invalid/zero } } // Data Series 2: Degree of Saturation (S) vs Void Ratio (e) // Fix Gs and w based on inputs. Vary e. var voidRatioPoints = []; var saturationFromE = []; for (var i = 0; i 0 && base_w > 0) { var current_S = (base_w * base_Gs) / current_e; if (current_S <= 1.0) { // Cap at 100% saturation voidRatioPoints.push(current_e.toFixed(3)); saturationFromE.push(current_S * 100); } else { voidRatioPoints.push(current_e.toFixed(3)); saturationFromE.push(100); // Cap saturation at 100% } } else { voidRatioPoints.push(current_e.toFixed(3)); saturationFromE.push(0); // Assume 0 saturation if e or w is invalid/zero } } // We need to decide what the X-axis represents. // Let's create two datasets: one showing S vs W, another showing S vs E. // This might require two charts or a complex combined chart. // For simplicity, let's make a single chart showing S vs W, and perhaps E vs W. // Simplified Chart: Show S and E as functions of W. labels = waterContentPoints; saturationValues = saturationFromW; voidRatioValues = []; // Resetting for this scenario for (var i = 0; i 0 && base_e > 0) { voidRatioValues.push(base_e); // e is assumed constant here } else { voidRatioValues.push(NaN); } } // This isn't ideal as 'e' is derived from gamma_d and Gs, not directly dependent on 'w' in this setup. // Alternative Charting Strategy: // Plot S vs W, and S vs E on the same chart, using two Y-axes if possible, or separate charts. // Let's try plotting S, W, and E against a common varying parameter. // Let's use Water Content (w) as the primary independent variable for the X-axis. // We will show S and E values corresponding to changes in W, assuming Gs is constant. // This means 'e' would have to be calculated based on 'w' for this plot, which isn't physically correct. // Correct approach for charting S, W, E: // Plot S vs W (keeping e constant) // Plot S vs E (keeping w constant) // This needs two separate plots or a dual-axis plot. // Let's make a chart showing S, W, and E against a hypothetical 'stress' or 'time' axis if possible. // A simpler approach is to show how S changes when W or E changes. // Let's stick to S vs W, and S vs E, plotted perhaps side-by-side or as distinct series. // Let's try plotting S against W, and E against W. // This assumes 'e' is constant derived from inputs, and we vary 'w'. // Or, it assumes 'w' is constant derived from inputs, and we vary 'e'. // Let's plot S vs W, keeping e constant. // And plot S vs E, keeping w constant. // Data Series 1: Degree of Saturation (S) vs Water Content (w) // Constant: Gs, e. Vary w. var w_axis_labels = []; var s_vs_w_data = []; for (var i = 0; i < numPoints; i++) { var current_w_decimal = base_w * (0.2 + (i / (numPoints – 1)) * 0.8); w_axis_labels.push((current_w_decimal * 100).toFixed(1) + '%'); var calculated_S = (current_w_decimal * base_Gs) / base_e; s_vs_w_data.push(Math.min(calculated_S * 100, 100)); // Cap at 100% } // Data Series 2: Void Ratio (e) vs Water Content (w) – this is generally NOT a direct relationship // Let's reconsider the chart. What is most illustrative? // Perhaps showing the calculated values and then projections. // Chart Idea: Y-axis: S, W, E. X-axis: Hypothetical Scenario Progression (e.g., increasing water) // This requires making assumptions about how W and E change together. // Simplest Chart: Show S, W, and E values at the current inputs. // This is not dynamic. The requirement is dynamic charts. // Let's plot S vs W (keeping e constant) and S vs E (keeping w constant). // This requires two independent chart datasets. // Dataset 1: S vs W var w_data_points = []; var s_data_points_from_w = []; for(var i=0; i<numPoints; i++) { var current_w_factor = 0.2 + (i / (numPoints – 1)) * 0.8; // From 0.2 to 1.0 var current_w = base_w * current_w_factor; w_data_points.push((current_w * 100).toFixed(1) + '%'); var calculated_S = (current_w * base_Gs) / base_e; s_data_points_from_w.push(Math.min(calculated_S * 100, 100)); } // Dataset 2: S vs E var e_data_points = []; var s_data_points_from_e = []; for(var i=0; i<numPoints; i++) { var current_e_factor = 0.2 + (i / (numPoints – 1)) * 0.8; // From 0.2 to 1.0 var current_e = base_e * current_e_factor; e_data_points.push(current_e.toFixed(3)); var calculated_S = (base_w * base_Gs) / current_e; s_data_points_from_e.push(Math.min(calculated_S * 100, 100)); } // This requires two X-axes or two charts. Let's try a combined chart with two series and one X-axis. // We can use Water Content as the X-axis, and plot S and E. // This implies E changes with W, which is not directly true. // FINAL CHART STRATEGY: // Y1-axis: Degree of Saturation (S) (%) // Y2-axis: Void Ratio (e) // X-axis: Water Content (w) (%) // This assumes Gs is constant and shows how S and E *could* relate if W changes. // This is still an approximation, as 'e' is primarily determined by compaction, not water content alone. // However, it's a common way to visualize these interrelations. var chart_labels = []; // Water Content % var chart_S_data = []; // Degree of Saturation % var chart_E_data = []; // Void Ratio for (var i = 0; i < numPoints; i++) { // Vary Water Content from near zero up to 100% or more var current_w_decimal = base_w * (0.1 + (i / (numPoints – 1)) * 1.0); // approx 10% to 110% of base_w var current_w_percent = current_w_decimal * 100; chart_labels.push(current_w_percent.toFixed(1) + '%'); // Calculate S assuming fixed 'e' (derived from original inputs) var calculated_S = (current_w_decimal * base_Gs) / base_e; chart_S_data.push(Math.min(calculated_S * 100, 100)); // Cap at 100% // Add the constant 'e' value for reference, or show a trend if e also varied. // For simplicity in this chart, let's show 'e' as constant derived from inputs. // This isn't ideal for a dynamic chart showing relation. // Let's adjust: Show S vs W (fixed e), and W vs E (fixed S? No.) // Revised Chart: // Y1-axis: Degree of Saturation (S) (%) // Y2-axis: Water Content (w) (%) // X-axis: Void Ratio (e) // Assume Gs constant. chart_labels = []; // Void Ratio chart_S_data = []; // Degree of Saturation % var chart_W_data = []; // Water Content % for (var i = 0; i W = (S * e) / Gs // This gets circular. // Let's go back to the most common visualization: S vs W, and S vs E. // We need two datasets for two different relationships. // Dataset 1: S vs W (fixed Gs, fixed e) var W_axis_labels = []; // Water Content % var S_data_series1 = []; // Degree of Saturation % for (var i = 0; i < numPoints; i++) { var current_w_decimal = base_w * (0.2 + (i / (numPoints – 1)) * 0.8); W_axis_labels.push((current_w_decimal * 100).toFixed(1) + '%'); var calculated_S = (current_w_decimal * base_Gs) / base_e; S_data_series1.push(Math.min(calculated_S * 100, 100)); } // Dataset 2: S vs E (fixed Gs, fixed w) var E_axis_labels = []; // Void Ratio var S_data_series2 = []; // Degree of Saturation % for (var i = 0; i < numPoints; i++) { var current_e_decimal = base_e * (0.2 + (i / (numPoints – 1)) * 0.8); E_axis_labels.push(current_e_decimal.toFixed(3)); var calculated_S = (base_w * base_Gs) / current_e_decimal; S_data_series2.push(Math.min(calculated_S * 100, 100)); } // We can't plot two different X-axes (W and E) on the same standard chart easily. // Option: Use one axis (e.g., W) and plot S and E against it. // This implies E is a function of W, which isn't necessarily true. // Let's plot S and W against E. chart_labels = E_axis_labels; // X-axis is Void Ratio chart_S_data = S_data_series2; // Series 1: S vs E chart_W_data = []; // Need to calculate W for corresponding E values, assuming fixed S or fixed Gs/gamma_w. // This gets complicated quickly. // Simplest dynamic chart: Show the relation S = f(W) given fixed e, Gs. // And show S = f(E) given fixed w, Gs. // Since we can only have one X-axis, let's make X = W. // We'll plot S vs W, and E (as a constant line at base_e) vs W. chart_labels = W_axis_labels; // Water Content % chart_S_data = S_data_series1; // S vs W var constant_E_data = []; for (var i = 0; i < numPoints; i++) { constant_E_data.push(base_e); // Plot 'e' as a horizontal line at the calculated value } // This shows S varying with W, and E being constant. // The Y-axis needs to accommodate both S (%) and E. This requires dual Y-axes. // Native Canvas Charting limitations without libraries: // Dual Y-axes are complex to implement manually. // Let's simplify: Plot S vs W. Show E as a reference point. var chart_labels_w = []; // Water Content % var chart_s_data_vs_w = []; // Degree of Saturation % var chart_e_ref_data = []; // Reference E value for (var i = 0; i < numPoints; i++) { var current_w_decimal = base_w * (0.1 + (i / (numPoints – 1)) * 1.0); chart_labels_w.push((current_w_decimal * 100).toFixed(1) + '%'); var calculated_S = (current_w_decimal * base_Gs) / base_e; chart_s_data_vs_w.push(Math.min(calculated_S * 100, 100)); chart_e_ref_data.push(base_e); // Constant reference line for E } // This chart will have W on X-axis, S on Y1, and E on Y2 (conceptually). // Without dual Y-axis support in native canvas drawing easily, we'll plot both on the same scale. // This requires careful scaling or limiting the range. // Let's make a chart comparing S and W vs E. chart_labels = E_axis_labels; // X-axis: Void Ratio chart_S_data = s_data_points_from_e; // Series 1: S vs E chart_W_data = []; // Need to calculate W for corresponding E values. // Let's derive W from S and E: W = (S * E) / Gs // This is tricky because S itself depends on W. // Revert to simpler, more standard chart: // X-axis: Water Content (w). // Y-axis: Degree of Saturation (S). // Show E as a separate data series, maybe on a different scale if possible. // This is difficult without charting library abstractions. // Let's draw a basic S vs W curve, and mark the current point. // And perhaps add E as a comment or secondary visual cue. // Final approach for native canvas: Two distinct datasets on one chart. // Series 1: Degree of Saturation (%) vs Water Content (%) // Series 2: Void Ratio (e) vs Water Content (%) (will be a flat line if e is constant) chart_labels = chart_labels_w; // X-axis: Water Content (%) var dataset1_S = { label: 'Degree of Saturation (%)', data: chart_s_data_vs_w, borderColor: 'rgba(0, 74, 153, 1)', // Primary color backgroundColor: 'rgba(0, 74, 153, 0.2)', fill: false, yAxisID: 'y-axis-S', // Assign to primary Y-axis tension: 0.1 }; var dataset2_E = { label: 'Void Ratio (e)', data: chart_e_ref_data, // Flat line at base_e borderColor: 'rgba(40, 167, 69, 1)', // Success color backgroundColor: 'rgba(40, 167, 69, 0.2)', fill: false, yAxisID: 'y-axis-E', // Assign to secondary Y-axis tension: 0.1 }; // Need to configure canvas context for dual Y axes manually. This is complex. // Let's simplify: Plot only S vs W, and indicate E. var simple_chart_labels = []; // Water Content % var simple_chart_S_data = []; // Degree of Saturation % for (var i = 0; i < numPoints; i++) { var current_w_decimal = base_w * (0.1 + (i / (numPoints – 1)) * 1.0); simple_chart_labels.push((current_w_decimal * 100).toFixed(1) + '%'); var calculated_S = (current_w_decimal * base_Gs) / base_e; simple_chart_S_data.push(Math.min(calculated_S * 100, 100)); } // Chart configuration myChart = new Chart(ctx, { type: 'line', data: { labels: simple_chart_labels, datasets: [{ label: 'Degree of Saturation (%)', data: simple_chart_S_data, borderColor: 'var(–primary-color)', backgroundColor: 'rgba(0, 74, 153, 0.1)', fill: false, tension: 0.1, pointRadius: 3, pointHoverRadius: 7 }] }, options: { responsive: true, maintainAspectRatio: true, scales: { x: { title: { display: true, text: 'Water Content (w) (%)' }, ticks: { maxTicksLimit: 10 } }, y: { title: { display: true, text: 'Degree of Saturation (%)' }, min: 0, max: 100, ticks: { callback: function(value) { if (value % 20 === 0) return value; } } } }, plugins: { legend: { display: true, position: 'top' }, tooltip: { callbacks: { label: function(context) { var label = context.dataset.label || ''; if (label) { label += ': '; } if (context.parsed.y !== null) { label += context.parsed.y.toFixed(1); } return label; } } } } } }); } function resetCalculator() { document.getElementById('moistUnitWeight').value = '18.5'; document.getElementById('dryUnitWeight').value = '15.0'; document.getElementById('specificGravity').value = '2.65'; // Clear error messages document.getElementById('moistUnitWeightError').textContent = ''; document.getElementById('dryUnitWeightError').textContent = ''; document.getElementById('specificGravityError').textContent = ''; // Reset results display document.getElementById('result').textContent = '– %'; document.getElementById('voidRatioResult').textContent = '–'; document.getElementById('waterContentResult').textContent = '–'; document.getElementById('degreeOfSaturationResult').textContent = '–'; // Destroy chart if (myChart) { myChart.destroy(); myChart = null; } // Optionally, redraw chart with defaults or clear it // For simplicity, we'll just clear it. A default chart could be added. } function copyResults() { var primaryResult = document.getElementById('result').innerText; var voidRatio = document.getElementById('voidRatioResult').innerText; var waterContent = document.getElementById('waterContentResult').innerText; var saturation = document.getElementById('degreeOfSaturationResult').innerText; var assumptions = "Assumptions:\n"; // Add relevant input values as assumptions assumptions += "- Moist Unit Weight (γm): " + document.getElementById('moistUnitWeight').value + " (Units inferred)\n"; assumptions += "- Dry Unit Weight (γd): " + document.getElementById('dryUnitWeight').value + " (Units inferred)\n"; assumptions += "- Specific Gravity (Gs): " + document.getElementById('specificGravity').value + "\n"; // Add inferred unit weight of water if possible, or state it's implicit assumptions += "- Unit Weight of Water (γw): Inferred based on input units (e.g., 9.81 kN/m³ or 62.4 pcf)\n"; var resultsText = "Soil Percent Saturation Calculation Results:\n\n" + "Degree of Saturation (S): " + primaryResult + "\n" + "Void Ratio (e): " + voidRatio + "\n" + "Water Content (w): " + waterContent + "\n\n" + assumptions; // Use Clipboard API navigator.clipboard.writeText(resultsText).then(function() { // Success feedback (optional) // alert('Results copied to clipboard!'); var copyButton = document.querySelector('.copy-button'); var originalText = copyButton.innerText; copyButton.innerText = 'Copied!'; setTimeout(function() { copyButton.innerText = originalText; }, 1500); }).catch(function(err) { console.error('Failed to copy results: ', err); // Fallback for older browsers or environments without navigator.clipboard try { var textArea = document.createElement("textarea"); textArea.value = resultsText; textArea.style.position = "fixed"; textArea.style.left = "-9999px"; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); var copyButton = document.querySelector('.copy-button'); var originalText = copyButton.innerText; copyButton.innerText = 'Copied!'; setTimeout(function() { copyButton.innerText = originalText; }, 1500); } catch (e) { alert('Failed to copy results. Please copy manually.'); } }); } // Initial calculation on page load document.addEventListener('DOMContentLoaded', function() { calculateSoilSaturation(); }); // Add Chart.js library dynamically if not present (for canvas charts) // Check if Chart.js is already loaded if (typeof Chart === 'undefined') { var script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/chart.js'; script.onload = function() { // Chart.js loaded, now it's safe to call updateChart potentially on load // (though calculateSoilSaturation is called after this) console.log('Chart.js loaded.'); // We can call calculateSoilSaturation here again if needed, or rely on the initial call. // document.addEventListener('DOMContentLoaded', calculateSoilSaturation); // Ensure it runs after Chart.js }; script.onerror = function() { console.error('Failed to load Chart.js library.'); }; document.head.appendChild(script); } else { console.log('Chart.js already loaded.'); // If Chart.js is already loaded, ensure calculateSoilSaturation runs. // document.addEventListener('DOMContentLoaded', calculateSoilSaturation); } // Ensure initial calculation happens AFTER Chart.js is potentially loaded. // If DOMContentLoaded fires before Chart.js loads, calculateSoilSaturation will fail. // A better approach is to trigger calculateSoilSaturation inside the Chart.js onload handler // or ensure it's called only once after everything is ready. // The current structure calls calculateSoilSaturation initially via DOMContentLoaded, // and assumes Chart.js is available or will be loaded. If it loads async, // the chart part might error. Let's ensure it runs only after Chart.js is confirmed loaded. // Corrected approach: Call calculateSoilSaturation within or after Chart.js load confirmation. function performInitialCalculation() { if (typeof Chart !== 'undefined') { calculateSoilSaturation(); } else { // Retry after a short delay if Chart.js is still loading setTimeout(performInitialCalculation, 100); } } // Modified DOMContentLoaded handler document.addEventListener('DOMContentLoaded', function() { // Load Chart.js if not present if (typeof Chart === 'undefined') { var script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/chart.js'; script.onload = function() { console.log('Chart.js loaded.'); performInitialCalculation(); // Now call initial calc }; script.onerror = function() { console.error('Failed to load Chart.js library.'); // Optionally show an error message to the user }; document.head.appendChild(script); } else { console.log('Chart.js already loaded.'); performInitialCalculation(); // Call initial calc if already loaded } });

Leave a Comment