def optimize_irrigated_area(self, zone) -> Dict: """Apply Linear Programming to naively optimize irrigated area. Occurs at start of season. Parameters ---------- * zone : FarmZone object, representing a farm or a farming zone. """ calc = [] areas = [] constraints = [] zone_ws = zone.water_sources total_avail_water = zone.avail_allocation field_areas = {} for f in zone.fields: area_to_consider = f.total_area_ha did = f"{f.name}__".replace(" ", "_") naive_crop_income = f.crop.estimate_income_per_ha() naive_req_water = f.crop.water_use_ML_per_ha app_cost_per_ML = self.ML_water_application_cost(zone, f, naive_req_water) pos_field_area = [w.allocation / naive_req_water for ws_name, w in zone_ws.items() ] pos_field_area = min(sum(pos_field_area), area_to_consider) field_areas[f.name] = { ws_name: Variable(f"{did}{ws_name}", lb=0, ub=min(w.allocation / naive_req_water, area_to_consider)) for ws_name, w in zone_ws.items() } # total_pump_cost = sum([ws.pump.maintenance_cost(year_step) for ws in zone_ws]) profits = [field_areas[f.name][ws_name] * (naive_crop_income - app_cost_per_ML[ws_name]) for ws_name in zone_ws ] calc += profits curr_field_areas = list(field_areas[f.name].values()) areas += curr_field_areas # Total irrigated area cannot be greater than field area # or area possible with available water constraints += [ Constraint(sum(curr_field_areas), lb=0.0, ub=pos_field_area) ] # End for # for ws_name in zone_ws: # total_f_ws = 0 # for f in zone.fields: # total_f_ws += field_areas[f.name][ws_name] # # End for # # End for # constraints += [Constraint(total_f_ws, # lb=0.0, # ub=zone.total_area_ha)] constraints += [Constraint(sum(areas), lb=0.0, ub=zone.total_area_ha)] # Generate appropriate OptLang model model = Model.clone(self.opt_model) model.objective = Objective(sum(calc), direction='max') model.add(constraints) model.optimize() if model.status != 'optimal': raise RuntimeError("Could not optimize!") return model.primal_values
def optimize_irrigation(self, zone, dt: object) -> tuple: """Apply Linear Programming to optimize irrigation water use. Results can be used to represent percentage mix e.g. if the field area is 100 ha, and the optimal area to be irrigated by a water source is `SW: 70 ha GW: 30 ha` and the required amount is 20mm `SW: 70 / 100 = 0.7 (irrigated area / total area, 70%) GW: 30 / 100 = 0.3 (30%)` Then the per hectare amount to be applied from each water source is calculated as: `SW = 20mm * 0.7 = 14mm GW = 20mm * 0.3 = 6mm` Parameters ---------- * zone : FarmZone * dt : datetime object, current datetime Returns --------- * Tuple : OrderedDict[str, float] : keys based on field and water source names values are hectare area Float : $/ML cost of applying water """ model = self.opt_model areas = [] profit = [] app_cost = OrderedDict() constraints = [] zone_ws = zone.water_sources total_irrigated_area = sum(map(lambda f: f.irrigated_area if f.irrigated_area is not None else 0.0, zone.fields)) field_area = {} possible_area = {} for f in zone.fields: f_name = f.name did = f"{f_name}__".replace(" ", "_") if f.irrigation.name == 'dryland': areas += [Variable(f"{did}{ws_name}", lb=0, ub=0) for ws_name in zone_ws] continue # End if # Disable this for now - estimated income includes variable costs # Will always incur maintenance costs and crop costs # total_pump_cost = sum([ws.pump.maintenance_cost(dt.year) for ws in zone_ws]) # total_irrig_cost = f.irrigation.maintenance_cost(dt.year) # maintenance_cost = (total_pump_cost + total_irrig_cost) # estimated gross income - variable costs per ha crop_income_per_ha = f.crop.estimate_income_per_ha() req_water_ML_ha = f.calc_required_water(dt) / ML_to_mm if req_water_ML_ha == 0.0: field_area[f_name] = { ws_name: Variable(f"{did}{ws_name}", lb=0.0, ub=0.0) for ws_name in zone_ws } else: max_ws_area = zone.possible_area_by_allocation(f) field_area[f_name] = { ws_name: Variable(f"{did}{ws_name}", lb=0, ub=max_ws_area[ws_name]) for ws_name in zone_ws } # End if # Costs to pump needed water volume from each water source app_cost_per_ML = self.ML_water_application_cost(zone, f, req_water_ML_ha) app_cost.update({ f"{did}{k}": v for k, v in app_cost_per_ML.items() }) profit += [ (crop_income_per_ha - (app_cost_per_ML[ws_name] * req_water_ML_ha) ) * field_area[f_name][ws_name] for ws_name in zone_ws ] # End for # Total irrigation area cannot be more than available area constraints += [Constraint(sum(areas), lb=0.0, ub=min(total_irrigated_area, zone.total_area_ha)) ] # 0 <= field1*sw + field2*sw + field_n*sw <= possible area to be irrigated by sw for ws_name, w in zone_ws.items(): alloc = w.allocation pos_area = zone.possible_irrigation_area(alloc) f_ws_var = [] for f in zone.fields: f_ws_var += [field_area[f.name][ws_name]] # End for constraints += [Constraint(sum(f_ws_var), lb=0.0, ub=pos_area)] # End for # Generate appropriate OptLang model model = Model.clone(self.opt_model) model.objective = Objective(sum(profit), direction='max') model.add(constraints) model.optimize() return model.primal_values, app_cost