# Drip Irrigation Design Logic & Accuracy Guide ## Architecture Overview The design pipeline has **3 independent but sequential stages**: ``` Input (farm boundary + crop zones + pump) ↓ [1] VALVE PLACEMENT ENGINE (valve_engine.py) • Decides: how many valves, where, why ↓ [2] DRIP LAYOUT GENERATOR (drip_engine.py) • For each valve zone: generate main + laterals ↓ [3] BOM & SUMMARY (drip_engine.py) • Compute costs, material counts, flow rates ↓ Output (GeoJSON with valves, zones, drips, BOM) ``` Each stage can be improved independently. This doc explains the logic, current assumptions, and where accuracy hurts most. --- ## Stage 1: Valve Placement (THE MOST CRITICAL STAGE) ### Decision Matrix (4-layer hierarchy) When to split the farm into multiple zones? The engine applies these rules **in order**, taking the **maximum** zone count that satisfies all constraints: ```python num_zones = max( num_zones_capacity, # [1] Pump can't deliver all emitter demand at once num_zones_crop, # [2] Different crops in different zones num_zones_area, # [3] Area-based minimum (crop density floor) num_zones_topo, # [4] Elevation split ) ``` ### Layer 1: Capacity Constraint **Logic:** ```python total_emitter_flow_lph = sum(area_m2 * emitter_density * emitter_flow_lph per crop) pump_flow_lph = HP_TO_LPH[pump_hp] # lookup table num_zones_capacity = ceil(total_emitter_flow_lph / pump_flow_lph) ``` **Current assumptions:** - Emitter density derived from `lateral_spacing × emitter_spacing` (see `CROP_FLOW_PARAMS` in `valve_engine.py:47-54`) - Tomato: 0.5m rows, 0.3m emitter spacing → 4.17 emitters/m² - Lettuce: 0.4m rows, 0.2m spacing → 12.5 emitters/m² (intensive) - Orchard: 2.0m rows, 1.0m spacing → 0.5 emitters/m² (sparse) - Pump HP → L/h curve is linear interpolation between discrete values (`HP_TO_LPH` dict) **Accuracy issues:** - **Emitter density is naive**: assumes rows are perpendicular to flow, which isn't always true on irregular plots. - **Pump curve is a lookup**: real pumps have pressure-dependent flow; we ignore head loss over long laterals. - **No derating for pressure drop**: a 200m lateral with 4.17 emitters/m² may have 30%+ flow drop by the end; we ignore this. **When to improve:** - If you're seeing valve zones with highly uneven emitter counts, increase `CROP_FLOW_PARAMS[crop]["emitter_density_m2"]` for intensive crops. - If the pump can't deliver enough flow, you likely **underestimated density**. ### Layer 2: Crop Type Constraint **Logic:** ```python num_zones_crop = len(set(z["crop"] for z in crop_zones)) ``` **Current behavior:** - Each **unique crop name** gets its own zone (minimum). - Example: if you have tomato in plot_1 and lettuce in plot_2, you'll get ≥2 zones. **Accuracy issues:** - This is **exact** — no assumptions. - But it forces fragmentation if you have many small plots with different crops, even if their water needs are similar. **When to improve:** - If you want to group similar crops (e.g., tomato + pepper both ~0.5m spacing), add a crop "family" system. ### Layer 3: Area-Density Floor (THE MOST OPINIONATED) **Logic:** ```python density_valves_per_ha = VALVE_DENSITY_PER_HA[primary_crop] # e.g., 6 for tomato num_zones_area = ceil(farm_area_ha * density) ``` **Current densities (`VALVE_DENSITY_PER_HA`):** ```python "tomato": 6, # ~0.17 ha/valve = 1700 m² per valve "pepper": 6, "lettuce": 7, "cucumber": 4, "orchard": 2, "generic": 5, # ≈2 valves/acre, standard default ``` **Why this matters:** - A 1-hectare tomato field gets **at least 6 zones**, even if the pump could handle it in 1. - This is **not** capacity-driven; it's **operational best practice**: narrower zones → shorter laterals → less pressure drop → more uniform water. **Current assumption:** - Rule of thumb from FAO irrigation guides: 1 valve per 1500–2000 m² for row crops. - But these are **empirical, not physics-based**. **Accuracy issues:** - **These numbers are guessed**, not calibrated to your farms. - Real farms might benefit from 3 valves/ha or 10 valves/ha depending on topography, soil, crop sensitivity. - **No feedback loop**: the engine doesn't learn from actual irrigation records. **When to improve (HIGH IMPACT):** 1. **Collect data**: run 5-10 farms and measure which density gives the most uniform soil moisture or yield. 2. **Stratify by local conditions**: clay soils may tolerate coarser zones (lower density); sandy soils need more (higher density). 3. **Make it configurable**: add `valve_density_override` to the API so users can tune. ### Layer 4: Topography Constraint **Logic:** ```python should_split_topo = (max_elevation_m - min_elevation_m) > ELEVATION_DELTA_THRESHOLD_M # 5m if should_split_topo: num_zones_required += 1 # Just adds 1 zone; not sophisticated ``` **Current assumption:** - If elevation differs by >5m across the farm, you need separate high/low zones. - This avoids excessive pressure imbalance (5m ≈ 0.5 bar). **Accuracy issues:** - **Very coarse**: if your field has 20m elevation span, splitting into high/low is crude. - **No secondary valve placement logic**: we don't intelligently place the "high zone" valve uphill. - **Ignores soil variability**: even flat farms have water-retention differences. **When to improve:** - Implement a real topographic split: use Voronoi diagram based on elevation contours. --- ## Stage 2: Drip Layout Generation (PURELY GEOMETRIC) ### Main Line Selection **Logic:** ```python edges = polygon.exterior edges (between consecutive vertices) edge_lengths = [distance(v1, v2) for each edge] main_idx = argmax(edge_lengths) # or argmin if main_line_edge="shortest" main_line = edges[main_idx] ``` **Current assumption:** - The longest edge is the best place for the main pipe (supplies all laterals perpendicular to it). - This works well for **rectangular fields**; poor for **irregular/triangular** fields. **Accuracy issues:** - **Doesn't optimize for minimal piping**: placing the main along the longest edge doesn't minimize total pipe (main + laterals). - **Doesn't consider pump location**: if pump is far from the longest edge, the main should be closer to the pump. **When to improve:** - Implement a "cost-optimal main placement" algorithm: ``` for each edge: cost = main_length + avg_lateral_length_if_main_on_this_edge best_edge = min(cost) ``` - Result: main placement adapts to both field shape and pump location. ### Lateral Generation & Clipping **Logic:** ```python # Generate parallel lines perpendicular to main, spaced at crop-specific intervals spacing = params["lateral_spacing_m"] # e.g., 0.5m for tomato num_laterals = ceil(main_length / spacing) for i in range(num_laterals): point_on_main = main.interpolate(i * spacing) lateral = perpendicular_line(point_on_main, direction=perp) clipped_lateral = lateral.intersection(polygon) # Clip to field boundary ``` **Current assumption:** - Equally-spaced laterals perpendicular to the main. - Works perfectly for **rectangles**, OK for **gentle polygons**, poor for **irregular shapes** (some laterals clip to very short lengths, wasting water). **Accuracy issues:** - **Uneven emitter distribution**: clipping can leave some laterals very short (e.g., 10m) while others are 100m, causing huge flow imbalance. - **Dead zones**: if the field is very irregular (e.g., L-shaped), the corners near the short clipped laterals will be under-irrigated. **When to improve:** 1. **Adaptive lateral spacing**: instead of fixed spacing, space laterals so each has ~similar length (within 20% variation). 2. **Lateral grouping**: group short laterals and feed them from a single branch main, not from the primary main. 3. **Visualization**: highlight in the output which laterals are "problematic" (too short, too long). ### Headland Buffer **Logic:** ```python buffered_polygon = polygon.buffer(-headland_buffer_m) ``` **Current assumption:** - Shrink the field inward by a fixed distance. - Avoids putting drip on field edges (where machinery turns). **Accuracy issues:** - **All headland is the same**: real farms have varied headland: one side might be a road (no drip), other side a boundary (need drip). - **No input for headland shape**: assume rectangular; doesn't account for irregular field edges. **When to improve:** - Accept a **per-edge headland map**: `{ "north": 1.5, "east": 1.0, "south": 2.0, "west": 0.5 }`. --- ## Stage 3: BOM & Summary (COST ESTIMATION) ### Current Cost Model **Logic:** ```python main_pipe_cost = main_length_m * PIPE_COSTS["main_line_16mm"] drip_tape_cost = drip_length_m * PIPE_COSTS["drip_tape_16mm"] emitter_cost = emitter_count * PIPE_COSTS["emitter_inline"] valve_cost = num_valves * 15 # Fixed $ per valve total_cost = main + drip + emitter + valve ``` **Current assumption:** - All pipes are 16mm. - All emitters are $0.08 each. - All valves are $15 each. - These are **order-of-magnitude guesses** from generic sourcing. **Accuracy issues:** - **No regional pricing**: India vs. Kenya vs. Brazil have very different pipe costs. - **No volume discounts**: a 1-hectare vs. 100-hectare farm have different unit costs. - **No installation labor**: only materials, no digging, trenching, connection labor. - **No waste allowance**: we assume 100% of pipe is used; real installations have ~5-10% waste. **When to improve:** 1. **Collect regional pricing data**: build a cost table by country/region. 2. **Add waste factor**: multiply final quantities by 1.10 (10% waste). 3. **Surface cost per hectare & cost per emitter**: help users compare. --- ## Summary: Accuracy Levers (in order of impact) | Lever | Current | Improvement | Impact | |-------|---------|-------------|--------| | **Valve density (Layer 3)** | Fixed 6 valves/ha for tomato | Calibrate to local data + user input | 🔴 HIGHEST — wrong density = under/over-designed | | **Lateral spacing uniformity** | Clipped equally-spaced lines | Adaptive spacing by target lateral length | 🔴 HIGH — short laterals = dead zones | | **Main line placement** | Longest edge | Cost-optimal (main + laterals) | 🟡 MEDIUM — 10-20% improvement typical | | **Pump head loss** | Ignored | Model pressure drop over lateral length | 🟡 MEDIUM — matters >100m laterals | | **Headland map** | Fixed inward buffer | Per-edge buffer (N/E/S/W) | 🟡 MEDIUM — irregular fields | | **Cost calibration** | Guessed $ per unit | Regional/seasonal sourcing data | 🟡 MEDIUM — users need local confidence | | **Topography split** | Elevation delta > 5m → +1 zone | Voronoi split + contour-aware placement | 🟢 LOW — only needed for >10m deltas | --- ## How to Test & Validate Your Improvements ### 1. Unit Tests - Each improvement should have a test in `test_drip_engine.py` or `test_valve_engine.py`. - Example: if you improve lateral spacing uniformity, add a test that checks `max(lateral_lengths) / min(lateral_lengths) < 1.5` (no more than 50% variation). ### 2. Visual Inspection - Use the Gradio UI or REST API to generate designs for **known farms**. - Compare your design to hand-drawn designs or existing irrigation schemes. - Look for: - Balanced lateral lengths ✓ - Valves placed logically ✓ - No huge dead zones ✓ ### 3. Field Metrics - Once you have real farm data, collect: - Soil moisture at 0-30cm depth (3-5 spots per field) - Before / after irrigation water use - Crop yield uniformity - Correlate with design metrics: - `avg_lateral_length`, `lateral_length_std_dev` - `emitters_per_zone` - Compare uniform designs vs. non-uniform designs ### 4. Sensitivity Analysis - For each improvement, run the design with: - `-10% parameter` → output - `nominal parameter` → output - `+10% parameter` → output - Measure sensitivity: `(output_+10% - output_-10%) / output_nominal` - Example: "Reducing valve density from 6→5.4 /ha increases avg_lateral_length by 8%; this is acceptable for loose clay soils but risky for sandy soils." --- ## Next Steps 1. **Identify your priority**: - Cost accuracy? → Fix regional pricing (Step 3). - Design uniformity? → Adaptive lateral spacing (Step 2). - Operational realism? → Tune valve density + add headland map (Step 1). 2. **Collect data**: 5-10 real farms (geometry, crops, pump, existing irrigation scheme if any). 3. **Iterate**: - Improve one lever at a time. - Validate against your test farms. - Document assumptions. 4. **Share improvements**: push back to the repo so the next user benefits.