Garlic.jl

Garlic.jl is a reimplementation of garlic model using Cropbox framework. The original model was written in C++ and published in "A process-based model for leaf development and growth in hardneck garlic (Allium sativum)".

Installation

using Pkg
Pkg.add("Garlic")

Getting Started

using Cropbox
using Garlic

The model is a system named Model defined in Garlic module.

parameters(Garlic.Model; alias = true, recursive = true)
Config for 21 systems:
  Clock
    init = 0 hr
    step = 1 hr
  Context
  BetaFunction
    maximum_temperature = nothing
    minimum_temperature = 0 °C
    optimal_temperature = nothing
    beta                = 1
  GrowingDegree
    base_temperature    = nothing
    maximum_temperature = nothing
    optimal_temperature = nothing
  Q10Function
    Q10                 = 2
    optimal_temperature = nothing
  Phenology
    optimal_temperature           = 22.28 °C
    ceiling_temperature           = 34.23 °C
    planting_date                 = nothing
    emergence_date                = nothing
    maximum_emergence_rate        = 0.0876 d⁻¹
    emergence_optimal_temperature = 12.7 °C
    emergence_ceiling_temperature = 35.9 °C
    critical_photoperiod          = 12.5 hr
    storage_days                  = 100 d
    storage_temperature           = 5 °C
    initial_leaves_at_harvest     = 4
    maximum_leaf_initiation_rate  = 0.2 d⁻¹
    maximum_phyllochron_asymptote = 0.4421 d⁻¹
    (LeafAppearance) _SDm         = 117.752 d
    (LeafAppearance) _k           = 0.0256 d⁻¹
    initial_maximum_phyllochron   = missing
    leaves_generic                = 10
    scape_removal_date            = nothing
    flower_appearance_threshold   = 5.0
    scape_appearance_threshold    = 3.0
    bulbil_appearance_threshold   = 5.5
  Soil
  Location
    latitude  = 36°
    longitude = 128°
    altitude  = 20 m
  Sun
    transmissivity                                    = 0.5
    solar_constant                                    = 1370 W m⁻²
    photosynthetic_active_radiation_conversion_factor = 4.6 μmol J⁻¹
  Calendar
    init = nothing
    last = nothing
  VaporPressure
    a = 0.611 kPa
    b = 17.502
    c = 240.97
  Weather
    store = nothing
    CO2   = 400 μmol mol⁻¹
  LeafLengthTracker
    beta = 1
  Leaf
    maximum_elongation_rate                   = 12 cm d⁻¹
    longevity                                 = 50 d
    cold_injury_factor1                       = -0.1 K⁻¹
    cold_injury_factor2                       = 1.6
    cold_injury_critical_temperature          = 0 °C
    (LeafColdInjury) _enable                  = true
    minimum_length_of_longest_leaf            = 60 cm
    length_to_width_ratio                     = 0.05
    area_ratio                                = 0.75
    stay_green                                = 3.5
    maximum_length_of_longest_leaf_adjustment = 0 cm²
  Sheath
    growth_duration = 10 d
    longevity       = 50 d
  NodalUnit
    growth_duration = 10 d
    longevity       = 50 d
  RespirationTracker
    Q10                 = 2
    optimal_temperature = 20 °C
  Radiation
    leaf_angle        = ellipsoidal
    leaf_angle_factor = 0.7
    wave_band         = photosynthetically_active_radiation
    scattering        = 0.15
    clumping          = 1.0
    soil_reflectivity = 0.1
  GasExchange
    leaf_width                                   = 0.1 m
    stomatal_ratio                               = 1.0
    kinematic_viscosity_of_air_at_20             = 1.51e-5 m² s⁻¹
    thermal_diffusivity_of_air_at_20             = 2.15e-5 m² s⁻¹
    g0                                           = 0.096 mol bar⁻¹ m⁻² s⁻¹
    g1                                           = 6.824
    diffusivity_ratio_boundary_layer             = 1.37
    diffusivity_ratio_air                        = 1.6
    leaf_scattering                              = 0.15
    leaf_spectral_correction                     = 0.15
    leaf_thermal_emissivity                      = 0.97
    latent_heat_of_vaporization_at_25            = 44 kJ mol⁻¹
    specific_heat_of_air                         = 29.3 J K⁻¹ mol⁻¹
    absorption_coefficient                       = 0.5
    base_temperature                             = 25 °C
    rubisco_constant_for_co2_at_25               = 404.9 μbar
    activation_energy_for_co2                    = 79.43 kJ mol⁻¹
    rubisco_constant_for_o2_at_25                = 278.4 mbar
    activation_energy_for_o2                     = 36.38 kJ mol⁻¹
    mesophyll_o2_partial_pressure                = 210 mbar
    maximum_carboxylation_rate_at_25             = 108.4 μmol m⁻² s⁻¹
    activation_energy_for_carboxylation          = 52.1573 kJ mol⁻¹
    maximum_electron_transport_rate_at_25        = 169.0 μmol m⁻² s⁻¹
    activation_energy_for_electron_transport     = 23.9976 kJ mol⁻¹
    electron_transport_temperature_response      = 616.4 J K⁻¹ mol⁻¹
    electron_transport_curvature                 = 200 kJ mol⁻¹
    light_transition_sharpness                   = 0.7
    triose_phosphate_limitation_at_25            = 16.03 μmol m⁻² s⁻¹
    activation_energy_for_Tp                     = 47.1 kJ mol⁻¹
    dark_respiration_at_25                       = 1.08 μmol m⁻² s⁻¹
    activation_energy_for_respiration            = 49.39 kJ mol⁻¹
    co2_compensation_point_at_25                 = 42.75 μbar
    activation_energy_for_co2_compensation_point = 37.83 kJ mol⁻¹
  Root
    growth_duration = 10 d
    longevity       = 50 d
  Model
    primordia                           = 5
    partitioning_table                  = [0.0 0.0 … 0.0 0.0; 0.35 0.3 … 0.0 0.1; … ; 0.05 0.0 … 0.0 0.95; 0.0 0.0 … 0.0 0.0]
    initial_seed_mass                   = 0.275 g
    initial_planting_density            = 55 m⁻²
    maintenance_respiration_coefficient = 0.012 d⁻¹
    synthesis_efficiency                = 0.8
    cold_damage_shape_factor            = 0.9 K⁻¹
    cold_damage_critical_temperature    = -15 °C
    enable_cold_damage                  = true
    leaf_width                          = 1.5 cm

As the parameter list goes quite long, let's try a parameter set included in the package. It was calibrated for Korean Mountain (KM) cultivar planted and grown in Seattle as described in the paper.

config = @config Garlic.Examples.AoB.KM_2014_P2_SR0
Config for 8 systems:
  Phenology
    optimal_temperature           = 22.28
    ceiling_temperature           = 34.23
    critical_photoperiod          = 12
    maximum_leaf_initiation_rate  = 0.1003
    storage_temperature           = 5
    maximum_phyllochron_asymptote = 0.4421
    leaves_generic                = 10
    initial_leaves_at_harvest     = 4
    storage_days                  = 143
    planting_date                 = ZonedDateTime(2014, 11, 20, tz"America/Los_Angeles")
    emergence_date                = ZonedDateTime(2014, 12, 30, tz"America/Los_Angeles")
    scape_removal_date            = nothing
  Leaf
    maximum_elongation_rate        = 4.7
    minimum_length_of_longest_leaf = 65.0
    stay_green                     = 1.84
  Carbon
    maintenance_respiration_coefficient = 0.012
    synthesis_efficiency                = 0.8
    partitioning_table                  = [0.0 0.0 … 0.0 0.0; 0.35 0.3 … 0.0 0.1; … ; 0.05 0.0 … 0.0 0.95; 0.0 0.0 … 0.0 0.0]
  Meta
    cultivar       = :KM
    year           = 2014
    planting_group = 2
  Location
    latitude  = 47.66
    longitude = 122.29
    altitude  = 20.0
  Weather
    CO2   = 390
    store = 7454×11 DataFrame…
  Plant
    initial_planting_density = 55
  Calendar
    init = ZonedDateTime(2014, 9, 1, 1, tz"America/Los_Angeles")
    last = ZonedDateTime(2015, 7, 7, tz"America/Los_Angeles")

Simulation is run until stop condition is met which corresponds to the number of hours (calendar.count) between starting (calendar.init) and ending date (calendar.last) as specified in the configuration above. The output of simulation is snap-ped each day, not by every hour, to reduce overhead.

r = simulate(Garlic.Model; config, stop = "calendar.count", snap = 1u"d")

Progress:   0%[>                          ]  ETA: 9:01:51 ( 4.39  s/it)
Progress:  13%[===>                       ]  ETA: 0:01:19 (12.19 ms/it)
Progress:  20%[=====>                     ]  ETA: 0:00:50 ( 8.43 ms/it)
Progress:  27%[=======>                   ]  ETA: 0:00:38 ( 6.97 ms/it)
Progress:  32%[========>                  ]  ETA: 0:00:39 ( 7.77 ms/it)
Progress:  40%[==========>                ]  ETA: 0:00:30 ( 6.65 ms/it)
Progress:  47%[============>              ]  ETA: 0:00:24 ( 5.99 ms/it)
Progress:  53%[==============>            ]  ETA: 0:00:19 ( 5.52 ms/it)
Progress:  59%[================>          ]  ETA: 0:00:16 ( 5.16 ms/it)
Progress:  66%[=================>         ]  ETA: 0:00:12 ( 4.86 ms/it)
Progress:  72%[===================>       ]  ETA: 0:00:10 ( 4.61 ms/it)
Progress:  79%[=====================>     ]  ETA: 0:00:07 ( 4.41 ms/it)
Progress:  85%[=======================>   ]  ETA: 0:00:05 ( 4.23 ms/it)
Progress:  92%[========================>  ]  ETA: 0:00:03 ( 4.08 ms/it)
Progress:  98%[==========================>]  ETA: 0:00:01 ( 3.95 ms/it)
Progress: 100%[===========================] Time: 0:00:28 ( 3.91 ms/it)

The output data frame can be now used for making some plots. Here is a plot replicating Fig. 3.D from the paper showing leaf development over time.

visualize(r, :time, [:leaves_appeared, :leaves_mature, :leaves_dropped]; kind = :line)
time Oct Nov Dec Jan 2015 Feb Mar Apr May Jun Jul leaves_appeared leaves_mature leaves_dropped 0 5 10

Another plot replicating Fig. 4.D from the paper showing the area of green leaf taking account of leaf aging.

visualize(r, :time, :green_leaf_area; kind = :line)
time Oct Nov Dec Jan 2015 Feb Mar Apr May Jun Jul green_leaf_area 0 100 200 300 400 (cm²)

Similarly, dry biomass of organ types per plant can be visualized.

visualize(r, :time, [:leaf_mass, :bulb_mass, :total_mass]; kind = :line)
time Oct Nov Dec Jan 2015 Feb Mar Apr May Jun Jul leaf_mass bulb_mass total_mass 0 10 20 30 40 (g)

For more information about using the framework such as simulate() and visualize() functions, please refer to the Cropbox documentation.