...
Code Block |
---|
Variables: - fieldCapacity: How much water the field can hold - oldSoilWaterContent: Previous days soil water content (if present) - precipitation: How much rain was present during the day based on a farmers field location - evapotranspiration: How much water is transferred from the land to the atmosphere by evaporation if (oldSolWaterContent + precipitation > fieldCapacity) soilWaterContent = fieldCapacity - ET else soilWaterContent = (oldSoilWaterContent + precipitation) - ET |
...
Wilting Point
Code Block |
---|
Variables: - texture - organic matter - rooting depth fieldCapacityconst wiltingPoint_1 = ( -0.251024 * textureObject.sand) + (0.195487 * textureObject.clay) + ( 0.011006 * organicMatter) + 0.006005 * (textureObject.sand * organicMattertextureObject.clay) - 0.027013 * (textureObject.clay * organicMatter) + 0.452068 * (textureObject.sand * textureObject.clay) + 0.299031; fieldCapacity_2 const wiltingPoint_2 = fieldCapacitywiltingPoint_1 + (1.283 * Math.pow(fieldCapacity_1, 2) - 0.37414 * fieldCapacitywiltingPoint_1 - 0.01502); wilting fieldpoint capacity = fieldCapacitywiltingPoint_2 * rooting depth |
...
rootingDepth |
Field Capacity
Code Block |
---|
TheVariables: algorithm- usestexture OpenWeatherMap- APIorganic tomatter grab- therooting amountdepth of rainfieldCapacity_1 that occurred within the hour If no rain is present, OpenWeatherMap API does not give back the key 'rain' In the code, this behaviour defaults to 0 precipitation Added onto precipitation, are the irrigation logs: - The algorithm grabs the irrigation logs for the day, organized by crop - The log contains "hours" which is converted first to minutes - irrigation = (flow_rate_l/min) / minutes / area_used Now we sum the precipitation that was accumulated from the day and the irrigation calculated. |
Evapotranspiration
Code Block |
---|
Variables and Assumptions:
- Clear Sky Transmissivity = 1
- Albedo = 0.23
- Average Temperature = (max_temp - min_temp) / 2
- Elevation Data
- Wind Speed
- ExtraT Solar Radiation
- Solar Radiation
- Slope
- Psychometric Constant
- Saturated Vapour Pressure
- Actual Vapour Pressure
- RNL
- RNS
ET = 0.408 * slope * (rns - rnl) + psychometricConstant * (900 / (averageTemperature + 273)) * windSpeed * (saturatedVP - actualVP)) / (slope + psychometricConstant * (1 + 0.34 * windSpeed))
|
Elevation Data
Code Block |
---|
In the app, the algorithm calls the Google Maps API to get elevation data based on a farms field location |
Wind Speed
Code Block |
---|
In the app, the algorithm calls the Google Maps API to get wind speed data based on a farms field location |
Extra T Solar Radiation
Code Block |
---|
Variables:
- Latitude of Location (In Radian)
- Current day of the year
- corr Earth and Sun Distance: 1 + 0.0334 * Math.cos(0.01721 * dayOfYear - 0.0552)
- Solar Declination: 0.4093 * Math.sin((2 * Math.PI * (284 + dayOfYear)) / 365)
- Daylight Time Factor: Math.acos(-Math.tan(latitudeInRadian) * Math.tan(solarDecl))
ExtraTSolar = 4.921 * 24 / Math.PI * corrEarthSunDist * (Math.sin(latitudeInRadian) * Math.sin(solarDecl) * daylightTimeFactor + Math.cos(latitudeInRadian) * Math.cos(solarDecl)) * Math.sin(daylightTimeFactor) |
Solar Radiation
Code Block |
---|
Variables:
- weatherData = Weather Data based on Field
weatherData = 0.16 * extraTSolar * (weatherData['max_degrees'] - weatherData['min_degrees']) |
Slope
Code Block |
---|
Variables:
- Temperature = average temperature of the day
slope = (4098 * (0.6108 * Math.exp(17.27 * temperature / (temperature + 237.3)))) / Math.pow(temperature + 237.3, 2) |
Psychometric Constant
Code Block |
---|
const p = 101.3 * (Math.pow((293 - 0.0065 * elevation) / 293, 5.26));
psychometricConstant = (1.013e-03 * p) / (0.622 * (2.501 - 0.002361 * averageTemperature)); |
Saturated Vapour Pressure
Code Block |
---|
Variables:
- temperature = mean temperature of the day
Saturated Vapour Pressure = 0.6108 * Math.exp((17.27 * temperature) / (temperature + 237.3)) |
Actual Vapour Pressure
Code Block |
---|
Variables: - temperature = mean= (-0.251 * textureObject.sand) + (0.195 * textureObject.clay) + (0.011 * organicMatter) + 0.006 * (textureObject.sand * organicMatter) - 0.027 * (textureObject.clay * organicMatter) + 0.452 * (textureObject.sand * textureObject.clay) + 0.299 fieldCapacity_2 = fieldCapacity_1 + (1.283 * Math.pow(fieldCapacity_1, 2) - 0.374 * fieldCapacity_1 - 0.015); field capacity = fieldCapacity_2 * rooting depth |
Precipitation
Code Block |
---|
The algorithm uses OpenWeatherMap API to grab the amount of rain that occurred within the hour
If no rain is present, OpenWeatherMap API does not give back the key 'rain'
In the code, this behaviour defaults to 0 precipitation
Added onto precipitation, are the irrigation logs:
- The algorithm grabs the irrigation logs for the day, organized by crop
- The log contains "hours" which is converted first to minutes
- irrigation = (flow_rate_l/min) / minutes / area_used
Now we sum the precipitation that was accumulated from the day and the irrigation calculated. |
Evapotranspiration
Code Block |
---|
Variables and Assumptions:
- Clear Sky Transmissivity = 1
- Albedo = 0.23
- Average Temperature = (max_temp - min_temp) / 2
- Elevation Data
- Wind Speed
- ExtraT Solar Radiation
- Solar Radiation
- Slope
- Psychometric Constant
- Saturated Vapour Pressure
- Actual Vapour Pressure
- RNL
- RNS
ET = 0.408 * slope * (rns - rnl) + psychometricConstant * (900 / (averageTemperature + 273)) * windSpeed * (saturatedVP - actualVP)) / (slope + psychometricConstant * (1 + 0.34 * windSpeed))
|
Elevation Data
Code Block |
---|
In the app, the algorithm calls the Google Maps API to get elevation data based on a farms field location |
Wind Speed
Code Block |
---|
In the app, the algorithm calls the Google Maps API to get wind speed data based on a farms field location |
Extra T Solar Radiation
Code Block |
---|
Variables:
- Latitude of Location (In Radian)
- Current day of the year
- corr Earth and Sun Distance: 1 + 0.0334 * Math.cos(0.01721 * dayOfYear - 0.0552)
- Solar Declination: 0.4093 * Math.sin((2 * Math.PI * (284 + dayOfYear)) / 365)
- Daylight Time Factor: Math.acos(-Math.tan(latitudeInRadian) * Math.tan(solarDecl))
ExtraTSolar = 4.921 * 24 / Math.PI * corrEarthSunDist * (Math.sin(latitudeInRadian) * Math.sin(solarDecl) * daylightTimeFactor + Math.cos(latitudeInRadian) * Math.cos(solarDecl)) * Math.sin(daylightTimeFactor) |
Solar Radiation
Code Block |
---|
Variables:
- weatherData = Weather Data based on Field
weatherData = 0.16 * extraTSolar * (weatherData['max_degrees'] - weatherData['min_degrees']) |
Slope
Code Block |
---|
Variables: - Temperature = average temperature of the day - humidityslope = mean(4098 humidity of the day - Saturated Vapour Presure = See equation just above Actual Vapour Pressure = Saturated Vapour Pressure * humidity / 100 |
RNL
Code Block |
---|
Variables:
- min temperature
- max temperature
- solar radiation
- actual vapour pressure
- extraT
- tal = 1
RNL = 4.903e-09 * ((Math.pow(maxTemp + 273.16, 4) + Math.pow(minTemp + 273.16, 4)) / 2) * (0.34 - 0.14 * Math.sqrt(actualVapourPressure)) * (1.35 * solarRadiation / (extraT * tal) - 0.35) |
...
* (0.6108 * Math.exp(17.27 * temperature / (temperature + 237.3)))) / Math.pow(temperature + 237.3, 2) |
Psychometric Constant
Code Block |
---|
const p = 101.3 * (Math.pow((293 - 0.0065 * elevation) / 293, 5.26));
psychometricConstant = (1.013e-03 * p) / (0.622 * (2.501 - 0.002361 * averageTemperature)); |
Saturated Vapour Pressure
Code Block |
---|
Variables:
- temperature = mean temperature of the day
Saturated Vapour Pressure = 0.6108 * Math.exp((17.27 * temperature) / (temperature + 237.3)) |
Actual Vapour Pressure
Code Block |
---|
Variables:
- temperature = mean temperature of the day
- humidity = mean humidity of the day
- Saturated Vapour Presure = See equation just above
Actual Vapour Pressure = Saturated Vapour Pressure * humidity / 100 |
RNL
Code Block |
---|
Variables: - Solarmin Radiationtemperature - Albedomax temperature RNS- solar =radiation (1- albedo)actual *vapour solar radiation |
Nitrogen Balance
In Nitrogen Balance, it is very similar to Water Balance as there one daily job.
References:
Daily Calculation
Code Block |
---|
Nitrogen In: the amount of nitrogen that is put into the soil
Nitrogen Out: the amount of nitrogen that is dissipated
nitrogen balance = nitrogen in - nitrogen out |
Nitrogen In
Code Block |
---|
Variables:
Current Total Nitrogen: Crop Quantity * Nitrogen_Percent / 100
Current NH3 = Total NH3 in the crop
Current NH4 = Total NH4 in the crop
Nitrogen Credits = field nutrient credits in the soil
mineralization rate = mineralization rate of the crop
Total Nitrogen for Fertilizer = nitrogenCredits + ((currentTotalNitrogen - currentNH3 - currentNH4) * mineralizationRate) + currentNH4 + currentNH3; |
Nitrogen Out
Code Block |
---|
Variables: Quantity Harvested: How much harvested crops in a farm Refuse: % Refuse for each crop Protein Content: Crop Protein Factor to Convert From Protein to Nitrogen = 0.16 Moisture Factor = 1 Total Nitrogen In Crop = quantityHarvested * (1 - refuse) * proteinContent * factorToConvertFromProteinToNitrogen * moistureFactor;pressure - extraT - tal = 1 RNL = 4.903e-09 * ((Math.pow(maxTemp + 273.16, 4) + Math.pow(minTemp + 273.16, 4)) / 2) * (0.34 - 0.14 * Math.sqrt(actualVapourPressure)) * (1.35 * solarRadiation / (extraT * tal) - 0.35) |
RNS
Code Block |
---|
Variables:
- Solar Radiation
- Albedo
RNS = (1- albedo) * solar radiation |
Additional in-depth documentation (by Brandon Tai (Unlicensed) )
The water balance insight serves to help farmers observe the water content in the soil of their locations. A centerpiece of this algorithm is the texture of the soil, which is reported to LiteFarm through soil data logs. When creating a soil data log, the farmer will be prompted to select one of a fixed list of textures. Possible values to select include “Sand”, “Loamy Sand”, “Silt”, and “Clay Loam”. The water balance algorithm involves a handful of lengthy and specific calculations that will plug in the values of the sand content, the clay content, and the silt content of the soil. The chosen texture of a log will determine these three values. The values that are used are shown in the following table:
Texture | Sand % | Clay % |
---|---|---|
Sand | 88 | 5 |
Loamy Sand | 80 | 5 |
Sandy Loam | 65 | 10 |
Loam | 40 | 20 |
Silt Loam | 20 | 15 |
Silt | 10 | 5 |
Sandy Clay Loam | 60 | 25 |
Clay Loam | 30 | 35 |
Silty Clay Loam | 10 | 35 |
Sandy Clay | 10 | 45 |
Silty Clay | 50 | 40 |
Clay | 25 | 50 |
The silt percentage can be determined by calculating the amount that is neither sand nor clay. For example, sandy loam is 65% sand and 10% clay, so silt percentage is 100% - 65% - 10% = 25%.
Unfortunately, the texture of soil is not so clear cut and are not necessarily confined to these values alone. This can be demonstrated with the soil triangle, which can be found here.
...
To sum up the problem, there is a lack of granularity within the water balance algorithm due to the fact that textures are fixed to specific sand, clay, and silt values. To present an example that demonstrates this, consider a farmer who has measured the sand, clay, and silt content of their soil and determined the percentages to be 5, 90, and 5 respectively. According to the triangle figure, this is classified to be “clay”. However, when this soil data is reported to be “clay” in the LiteFarm log, the calculation will always use 25% sand and 50% clay, which differs pretty significantly to the values that the farmer measured.
This presents room for improvement for the water balance calculation: allow soil data logs to take input for more information, namely the specific clay and sand percentages of the soil. This will allow more accurate calculations and more granularity of information. There is a caveat, which is that more granular information will not work with the equations that exist in the code currently, and the insight algorithm will likely need to be redone from scratch, with the help of a specialist.
To sum the situation up, the current water balance calculation works properly with fixed texture values, and will provide an adequate approximation for the water balance, and aiming for a more accurate value may require an overhaul of the equations that currently exist.
Nitrogen Balance
In Nitrogen Balance, it is very similar to Water Balance as there one daily job.
References:
Daily Calculation
Code Block |
---|
Nitrogen In: the amount of nitrogen that is put into the soil
Nitrogen Out: the amount of nitrogen that is dissipated
nitrogen balance = nitrogen in - nitrogen out |
Nitrogen In
Code Block |
---|
Variables:
Current Total Nitrogen: Crop Quantity * Nitrogen_Percent / 100
Current NH3 = Total NH3 in the crop
Current NH4 = Total NH4 in the crop
Nitrogen Credits = field nutrient credits in the soil
mineralization rate = mineralization rate of the crop
Total Nitrogen for Fertilizer = nitrogenCredits + ((currentTotalNitrogen - currentNH3 - currentNH4) * mineralizationRate) + currentNH4 + currentNH3; |
Nitrogen Out
Code Block |
---|
Variables:
Quantity Harvested: How much harvested crops in a farm
Refuse: % Refuse for each crop
Protein Content: Crop Protein
Factor to Convert From Protein to Nitrogen = 0.16
Moisture Factor = 1
Total Nitrogen In Crop = quantityHarvested * (1 - refuse) * proteinContent * factorToConvertFromProteinToNitrogen * moistureFactor; |
Additional in-depth documentation (by Brandon Tai (Unlicensed) )
The nitrogen balance insight acts as an indicator to a farmer on the balance of nitrogen in the soil of a location on their farm, as the name entails. The algorithm for this insight is run daily for each farm and will save a nitrogen value for each location of a farm that resulted in a non-trivial value. The calculation can be summed up as simply being the difference between how much nitrogen is going into the soil (typically through the application of fertilizers) and how much nitrogen is being taken out (usually through harvests). As such, this can be broken down as the calculations of the nitrogen in value and the nitrogen out value.
Nitrogen In
There are three factors that play into the addition of nitrogen content into the soil:
Fertilizer (e.g. manure)
Cover crops
Legumes
Fertilizers is accounted for through fertilizer logs. The algorithm will take fertilizer logs that exist for a location and use the nitrogen percentage (denoted by n_percentage
in the fertilizer
table), ammonium content measured in ppm (denoted by nh4_n_ppm
in the fertilizer
table), and the mineralization rate (denoted by mineralization_rate
in the fertilizer
table). Usually, nitrate content is taken into account, but since most fertilizers have negligible amounts of nitrate, this value is set to zero in the code’s calculations.
After consulting with a specialist, an improvement that can be made to the fertilizer’s part of the nitrogen in calculation is calculating the dry weight of a given fertilizer when calculating currentTotalNitrogen
. Currently, the code calculates this value as quantity_kg * n_percentage / 100
instead of accounting for the moisture percentage of the fertilizer:
Code Block |
---|
quantity_kg * moisture_percentage / 100 * n_percentage / 100 |
Cover crops are defined as “plants that are planted to cover the soil rather than for the purpose of being harvested” (taken from Wikipedia). Once cover crops are finished growing, they will be crunched into the ground for the benefit of the soil. One such benefit is adding nitrogen content, and this value can be derived by calculating how much cover crop is being planted in a location (usually calculated upon location crop creation) and multiplying that value by some factor of the amount of nitrogen credits that the cover crop provides. Currently, LiteFarm does not support the tracking of cover crops and does not have nitrogen credit values assigned to the various crops in the database. A more accurate calculation can be achieved once a system has been designed for cover crop tracking in the future.
Legumes are subgroup of crops that possess a unique property of taking nitrogen from the air and into the soil in the duration between their seeding and their harvest. That is the say, when a legume is planted and harvested, some amount of nitrogen content will be restored to the soil. This is one method through which extra nitrogen content, known as nitrogen credits, can be gained (the other method being cover crops). Currently, any crop that has a non-zero nutrient credit value (denoted by nutrient_credits
in the crop
table) will factor into the nitrogen in calculation if it is at some point planted and harvested in a location.
Here is a list of crops that have non-zero nutrient credit values, from the seeded data only:
...
To sum up the nitrogen in calculation, there are three primary factors that contribute to the final value, most of which is being calculated properly. Fertilizer should take only take into account its dry weight when calculating the nitrogen quantity, but otherwise the calculations accounting for fertilizers have been verified to be accurate. Cover crops can be accounted for once a way to track them has been designed and implemented in LiteFarm. Lastly, legumes are being properly accounted for.
Nitrogen Out
Calculating nitrogen out involves less distinct factors as compared to calculating nitrogen in. In summary, when a crop is planted, grown, and harvested, it will remove some amount of nitrogen from the soil. Calculations are performed on harvest logs. As shown in the abridged version above, the size of a harvest, the refuse, and the protein content of a crop are plugged into an equation to return the nitrogen out value. The algorithm has been verified with the exception of the refuse factor. Due to time running out for the interview with the specialist, we are unable to ascertain the significance of refuse in general, let alone within the algorithm.
Finally, a thank you to the specialist we interviews, Sean Smukler, for taking the time to discuss the nitrogen balance algorithm and provide insight on how it can be improved.
Labour Happiness
Code Block |
---|
Variables: Shifts based on a persons farm - Mood (1-5) The algorithm parses each mood from 1-5 if the worker decided to not submit their task, it is not weighted in this system The algorithm uses a "weighted sum" determined on the duration of the workers shift For example: If a worker does two tasks: Harvesting: 5 mins Plowing: 20 mins Rating: 3/5 The rating is shifted more in favour of plowing (As the worker spent more time plowing) |
...