AquaCrop-OSPy

repository·master·Indexed 41 days ago

https://github.com/aquacropos/aquacrop

An open-source Python implementation of the Soil-Crop-Water model based on AquaCrop-OS. It simulates crop growth and water use, mirroring features of the official FAO AquaCrop model. The library provides tools for configuring soil profiles, crop parameters, and irrigation management strategies, and allows for the projection of climate change impacts and estimation of irrigation water demands.

Tokens
8.1K
Snippets
27
Records
36
Agent score
51%

What's inside AquaCrop-OSPy

  1. Access Model Outputs

    master

    After running model.run_model(), you can access simulation results through the model._outputs attribute. Common output groups include:

    • model._outputs.final_stats: Summary statistics (e.g., Yield, Seasonal irrigation).
    • model._outputs.crop_growth: Time-series data for crop growth parameters (e.g., biomass, harvest index).
    • model._outputs.water_flux: Time-series data for water movement (e.g., Runoff, Infiltration, Deep Percolation/Drainage, Evaporation).
    # Accessing growth data
    grouped1 = model._outputs.crop_growth.groupby('season_counter').max()
    
    # Accessing water flux data
    grouped = model._outputs.water_flux.groupby('season_counter').sum()
  2. Manage model progression with the `aquacrop.timestep` module

    master
    The aquacrop.timestep module provides the core functions for controlling the temporal progression of the AquaCrop model. It allows you to execute the model one step at a time, check if the simulation has reached its end, retrieve outputs upon completion, and manage the internal clock and initial conditions.
  3. AquaCrop-OSPy Quickstart Tutorials

    master

    The following Google Colab notebooks provide guided tutorials for common tasks:

    • Running an AquaCrop-OSPy model: Basic simulation setup.
    • Estimation of irrigation water demands: Calculating water needs.
    • Optimisation of irrigation management strategies: Strategy optimization.
    • Projection of climate change impacts: Simulating climate scenarios.
  4. Install AquaCrop-OSPy dependencies

    master

    To use the notebooks and examples, ensure you have the necessary Python libraries installed. You can install them via pip:

    pip install aquacrop matplotlib seaborn
    !pip install aquacrop matplotlib seaborn
  5. Run AquaCrop-OSPy in development mode

    master

    If you want to run the model in pure Python without pre-compiling the AquaCrop modules (which is slower but easier for development), set the DEVELOPMENT environment variable to True before importing the library.

    import os
    os.environ['DEVELOPMENT'] = 'True'
  6. Optimize irrigation strategies using soil-moisture thresholds

    master

    AquaCrop-OSPy can be linked with optimization libraries like scipy.optimize to identify optimal irrigation schedules. A common approach is to optimize soil-moisture thresholds (SMTs) for different growing periods (e.g., emergence, canopy growth, max canopy, and senescence).

    When the soil-moisture content drops below a defined threshold, irrigation is applied to refill the soil profile to field capacity, subject to a maximum irrigation depth. By minimizing a negative reward (such as negative yield), you can find the set of thresholds that maximizes yield for a given water supply constraint.

    from aquacrop import AquaCropModel, Soil, Crop, InitialWaterContent, IrrigationManagement
    from scipy.optimize import fmin
    
    # Example setup for optimization
    def evaluate(smts, max_irr_season, test=False):
        # smts: list of soil moisture targets
        # max_irr_season: maximum seasonal irrigation limit
        # ... run model ...
        # return -yield for minimization or (yield, irrigation, -yield) for analysis
        pass
    
    # Find optimal thresholds
    # smts = fmin(evaluate, x0, args=(max_irr_season,))
  7. Configure irrigation management strategies

    master

    The IrrigationManagement class allows you to define different irrigation strategies by setting the irrigation_method (or IrrMethod) parameter.

    Available methods:

    • 0: Rainfed (no irrigation)
    • 1: Soil moisture targets (triggers irrigation if soil water content drops below specified thresholds for growth stages)
    • 2: Set time interval (irrigates every N days)
    • 3: Predefined schedule (uses a provided DataFrame)
    • 4: Net irrigation (maintains a soil-water level by topping up compartments daily)
    • 5: Constant depth (applies a fixed amount of water each day)

    Common parameters for IrrigationManagement:

    • SMT (list[float]): Soil moisture targets (%TAW) to maintain in each growth stage. Used with IrrMethod=1.
    • IrrInterval (int): Irrigation interval in days. Used with IrrMethod=2.
    • Schedule (pandas.DataFrame): DataFrame containing 'Date' and 'Depth'. Used with IrrMethod=3.
    • NetIrrSMT (float): Net irrigation threshold moisture level (% of TAW). Used with IrrMethod=4.
    • depth (float): Constant depth (mm) to apply each day. Used with IrrMethod=5.
    • WetSurf (int): Soil surface wetted by irrigation (%).
    • AppEff (int): Irrigation application efficiency (%).
    • MaxIrr (float): Maximum depth (mm) that can be applied each day.
    • MaxIrrSeason (float): Maximum total irrigation (mm) that can be applied in one season.
    # Example: Soil moisture targets strategy
    irr_mngt = IrrigationManagement(irrigation_method=1, SMT=[40, 60, 70, 30])
    
    # Example: Interval strategy
    interval_7 = IrrigationManagement(irrigation_method=2, IrrInterval=7)
    
    # Example: Predefined schedule
    schedule = pd.DataFrame({'Date': [pd.Timestamp('2023-01-01')], 'Depth': [25]})
    irrigate_schedule = IrrigationManagement(irrigation_method=3, schedule=schedule)
  8. Implement a custom irrigation strategy

    master

    You can implement complex, data-driven irrigation logic by manually controlling the model's simulation loop. This allows you to incorporate external data, custom code, or machine learning models.

    To do this:

    1. Initialize the model with irrigation_method=5 (Constant depth).
    2. Call model._initialize() to set up the initial state.
    3. Use a while loop to iterate through the simulation steps using model._clock_struct.model_is_finished.
    4. Inside the loop, calculate the required irrigation depth based on your custom logic (accessing weather or soil data via model._weather or model._init_cond).
    5. Update the model's parameter using model._param_struct.IrrMngt.depth = depth.
    6. Run the model for the current step using model.run_model(initialize_model=False).

    Note: Accessing attributes starting with _ (like _clock_struct or _param_struct) indicates interaction with internal structures to facilitate this extension point.

    # Custom logic function
    def get_depth(model):
        t = model._clock_struct.time_step_counter
        weather10 = model._weather[t+1:min(t+10+1, len(model._weather))]
        if sum(weather10[:,2]) > 0:
            if t > 0 and model._init_cond.depletion/model._init_cond.taw > 0.7:
                return 10
            return 0
        return 10
    
    # Simulation loop
    model = AquaCropModel(sim_start, sim_end, wdf, soil, crop, 
                          initial_water_content=initWC, 
                          irrigation_management=IrrigationManagement(irrigation_method=5))
    model._initialize()
    
    while not model._clock_struct.model_is_finished:
        depth = get_depth(model)
        model._param_struct.IrrMngt.depth = depth
        model.run_model(initialize_model=False)
  9. Parallelize AquaCrop-OSPy simulations with multiprocessing

    master

    To speed up optimization or large-scale simulations, you can use Python's multiprocessing.Pool to run multiple model instances in parallel across available CPU cores. This is particularly useful when performing parameter sweeps or repeated optimizations.

    from multiprocessing import Pool
    
    def func(max_irr):
        # Perform optimization for a specific irrigation limit
        smts = optimize(4, max_irr)
        yld, tirr, _ = evaluate(smts, max_irr, True)
        return yld, tirr, smts
    
    # Run in parallel using 8 cores
    with Pool(8) as p:
        results = p.map(func, list(range(0, 500, 50)))