1.3.82
 
Loading...
Searching...
No Matches
Nitrogen Model

Overview

The nitrogen model is an optional feature in the PlantArchitecture plugin that simulates nitrogen uptake, allocation, and stress effects on plant growth. When enabled, the model mechanistically tracks nitrogen in a three-level pool structure (root, available, and per-leaf pools), simulates rate-limited nitrogen accumulation in leaves, implements age-based nitrogen remobilization from old to young leaves, and calculates a nitrogen stress factor that other plugins can use to modify photosynthesis and growth rates.

Important architectural note: Nitrogen is tracked on an area basis (g N/m²) rather than a mass basis (g N/g DW). This design choice:

  • Eliminates the need for an SLA parameter (simpler model with 7 parameters instead of 8)
  • Provides direct compatibility with PROSPECT leaf optical models
  • Aligns with physiological reality where leaf N content per area remains relatively constant as leaves thicken (LMA increases) while mass-based concentration decreases
  • Matches remote sensing approaches which measure area-based nitrogen
  • Simplifies calculations by working directly with leaf area without biomass conversion

The model integrates loosely with other plugins through a single output variable (nitrogen_stress_factor, range 0-1) written to plant object data. The Photosynthesis plugin or other growth models can optionally read this stress factor and use it to scale parameters like Vcmax/Jmax or growth rates.

Key features of the nitrogen model include:

  • Area-based nitrogen tracking (g N/m²) for simplicity and PROSPECT compatibility
  • Three-level pool structure: root pool → available pool → per-leaf pools
  • Rate-limited nitrogen accumulation in leaves (prevents instantaneous uptake)
  • Age-based nitrogen remobilization from old leaves (>70% lifespan) to young leaves (<50% lifespan)
  • 70% remobilization efficiency with stress-dependent acceleration
  • Fruit nitrogen removal during fruit growth
  • Single output: nitrogen stress factor (0-1) for use by other plugins
  • Only 7 user-configurable parameters

Enabling the Nitrogen Model

The nitrogen model is disabled by default. To enable it, call the PlantArchitecture::enableNitrogenModel() method before or after building plant instances:

PlantArchitecture plantarchitecture(&context);
// Enable the nitrogen model
plantarchitecture.enableNitrogenModel();
// Load and build plants
plantarchitecture.loadPlantModelFromLibrary("bean");
uint plantID = plantarchitecture.buildPlantInstanceFromLibrary(nullorigin, 0);

The model can be disabled at any time using PlantArchitecture::disableNitrogenModel(). Once enabled, the model automatically operates during each call to PlantArchitecture::advanceTime().

Model Parameters

The behavior of the nitrogen model is controlled by only 7 parameters stored in the NitrogenParameters structure. Parameters can be set for individual plants or groups of plants using PlantArchitecture::setPlantNitrogenParameters().

Parameter Descriptions

Parameters in the NitrogenParameters structure are organized into several categories. Default values are based on typical crop plants (bean, tomato).

Category Parameter Default Value Units Description
Leaf Nitrogen Content (Area Basis)
target_leaf_N_area 1.5 g N/m² Target leaf nitrogen content per unit area for unstressed plants. Typical range: 1.0-2.5 g N/m².
minimum_leaf_N_area 0.5 g N/m² Minimum viable leaf nitrogen content per area below which leaves cannot function.
Allocation
root_allocation_fraction 0.15 - Fraction of applied nitrogen allocated to root pool (0-1). Remainder goes to available pool.
Rate Limiting
max_N_accumulation_rate 0.1 g N/m²/day Maximum rate at which leaves can accumulate nitrogen per unit area per day. Prevents instantaneous uptake.
Remobilization
leaf_remobilization_efficiency 0.70 - Fraction of leaf nitrogen that can be remobilized from old leaves (0-1). Typical range: 0.6-0.8.
remobilization_age_threshold 0.70 - Fraction of leaf lifespan at which remobilization begins (0-1). Leaves older than this donate N to younger leaves.
Fruit Nitrogen
fruit_N_area 1.0 g N/m² Nitrogen content per unit fruit surface area. Used to calculate N demand during fruit growth.

Setting Parameters

Parameters are set using the PlantArchitecture::setPlantNitrogenParameters() method. Default parameter values are used unless explicitly modified:

PlantArchitecture plantarchitecture(&context);
plantarchitecture.enableNitrogenModel();
// Create custom parameter set (all units are area-based)
NitrogenParameters nitrogen_params;
nitrogen_params.target_leaf_N_area = 2.0f; // Higher target (g N/m²)
nitrogen_params.minimum_leaf_N_area = 0.7f; // Higher minimum tolerance (g N/m²)
nitrogen_params.max_N_accumulation_rate = 0.15f; // Faster N uptake (g N/m²/day)
nitrogen_params.fruit_N_area = 1.5f; // Higher fruit N demand (g N/m²)
// Load and build plant
plantarchitecture.loadPlantModelFromLibrary("tomato");
uint plantID = plantarchitecture.buildPlantInstanceFromLibrary(nullorigin, 0);
// Apply parameters to plant
plantarchitecture.setPlantNitrogenParameters(plantID, nitrogen_params);

You can also set parameters for multiple plants at once:

std::vector<uint> plantIDs = plantarchitecture.buildPlantCanopyFromLibrary(
make_vec3(0,0,0), make_vec2(0.5, 0.5), make_int2(10, 10), 30);
plantarchitecture.setPlantNitrogenParameters(plantIDs, nitrogen_params);

Initialization

After enabling the nitrogen model and setting parameters, you should initialize nitrogen pools based on desired initial leaf nitrogen content per area:

// Initialize all plants with 1.2 g N/m² initial leaf nitrogen content
plantarchitecture.initializeNitrogenPools(1.2f);
// Or initialize a specific plant
plantarchitecture.initializePlantNitrogenPools(plantID, 1.2f);

The initialization method:

  1. Clears existing nitrogen pools (root, available, per-leaf)
  2. Sets each leaf's nitrogen content to the specified value (g N/m²)
  3. Calculates total nitrogen by multiplying content × leaf area
  4. Records the initial nitrogen as cumulative uptake for tracking purposes

Note: Because the model uses area-based nitrogen, initialization does not require knowledge of leaf biomass or SLA. This significantly simplifies the initialization process.

Nitrogen Application

Nitrogen is applied directly to plants using the PlantArchitecture::addPlantNitrogen() method. This immediately updates plant nitrogen pools:

// Apply 0.5 g N to a single plant
plantarchitecture.addPlantNitrogen(plantID, 0.5f);
// Apply 0.5 g N to each plant in a canopy
plantarchitecture.addPlantNitrogen(plantIDs, 0.5f);

When nitrogen is applied:

  1. It is immediately split between root pool (15% by default) and available pool (85% by default)
  2. The split fraction is controlled by root_allocation_fraction parameter
  3. Cumulative nitrogen uptake is tracked for each plant
  4. No scheduling or delayed uptake - application is instantaneous

In a typical simulation, you would apply nitrogen at specific times during the growing season:

for (int day = 100; day <= 250; day++) {
context.setDate(make_Date(day, 2024));
// Apply nitrogen on specific days
if (day == 115) {
plantarchitecture.addPlantNitrogen(plantIDs, 0.5f); // First application
}
if (day == 145) {
plantarchitecture.addPlantNitrogen(plantIDs, 0.75f); // Second application
}
// Model runs automatically in advanceTime
plantarchitecture.advanceTime(1.0f);
}

Nitrogen Dynamics

When PlantArchitecture::advanceTime() is called, the nitrogen model performs several operations in sequence:

1. Leaf Nitrogen Accumulation

Nitrogen from the available pool is transferred to individual leaves in a rate-limited manner:

  • Each leaf's current nitrogen content per area (g N/m²) is compared to the target
  • Nitrogen demand per area is calculated: demand = target - current (g N/m²)
  • Transfer rate is limited by max_N_accumulation_rate (g N/m²/day)
  • Total nitrogen transfer is calculated: (limited demand) × leaf area
  • Transfer is also supply-limited by the available pool
  • Leaves with nitrogen below target gradually accumulate nitrogen over time

This rate-limiting prevents instantaneous equilibration and creates realistic lag times between nitrogen application and full uptake.

Key simplification: No biomass calculation or SLA parameter needed. The model works directly with leaf areas.

2. Nitrogen Remobilization (Critical Feature)

Nitrogen is redistributed from old leaves to young leaves based on age:

Source leaves (age ≥ 70% of lifespan):

  • Classified as nitrogen donors
  • Calculate remobilizable nitrogen per area: (current_N_area - minimum_N_area) × 70% efficiency
  • Cannot mobilize nitrogen below the minimum content threshold
  • Remobilization is accelerated by up to 30% under nitrogen stress
  • Total remobilizable N = remobilizable N per area × leaf area

Sink leaves (age < 50% of lifespan):

  • Classified as nitrogen receivers
  • Young expanding leaves have priority for remobilized nitrogen
  • Receive nitrogen in proportion to their demand (target_N_area - current_N_area)

Physiological basis:

  • Research shows ~50% of young leaf nitrogen comes from remobilization during normal growth
  • Typical remobilization efficiency: 70-80% of leaf nitrogen
  • Area-based tracking naturally handles the fact that thicker (higher LMA) older leaves have more total nitrogen to remobilize even if mass-based concentration is similar
  • Nitrogen deficiency accelerates senescence by 5-20 days

This remobilization mechanism reproduces the characteristic symptom of nitrogen deficiency where older leaves yellow first while younger leaves remain green.

3. Fruit Nitrogen Removal

When fruit grows (detected by increase in fruit scale factor), nitrogen is deducted from the available pool:

  • Fruit area increment is calculated from scale factor changes
  • Nitrogen demand = area increment × fruit_N_area (g N/m²)
  • Per-plant fruit demand is aggregated across all fruiting buds before withdrawal
  • Nitrogen is removed from available pool first (supply-limited)
  • This reduces nitrogen available for leaves, potentially creating competition

Fruit nitrogen removal is only active when plants are fruiting.

Leaf-to-fruit translocation: when the available pool cannot fully cover fruit demand, the remaining shortfall is satisfied by translocation from leaves. Old leaves (age ≥ remobilization_age_threshold) donate first; if their reserve is exhausted, younger leaves contribute as a fallback. The per-leaf reserve uses the same formula as leaf-to-leaf remobilization — (current_N_area - minimum_leaf_N_area) × leaf_remobilization_efficiency — so the minimum_leaf_N_area floor is respected. Withdrawal is distributed across source leaves proportionally to per-leaf availability. No stress-acceleration multiplier is applied here because translocation magnitude is governed by fruit demand rather than stress signaling. If even leaves cannot cover the demand, the residual shortfall is silently absorbed (mirroring the supply-limited treatment of the available pool).

4. Nitrogen Stress Factor Calculation

After nitrogen dynamics, the model calculates a single output metric:

  • Area-weighted average nitrogen content per area is calculated: Σ(N_area × area) / Σ(area)
  • Stress factor = min(1.0, avg_N_area / target_N_area)
  • Stress factor is clamped to [0, 1] range
  • Written to plant object data with label "nitrogen_stress_factor"

Stress factor interpretation:

  • 1.0 = No nitrogen stress (plants at or above target nitrogen)
  • 0.5 = Moderate nitrogen stress (50% of target nitrogen)
  • 0.0 = Severe nitrogen stress (no nitrogen available)

Integration with Other Plugins

The nitrogen model outputs a single variable that other plugins can optionally read:

Photosynthesis Integration

The Photosynthesis plugin can read the nitrogen stress factor and scale photosynthetic parameters:

// In PhotosynthesisModel::run()
float N_stress = 1.0f; // Default: no stress
if (context->doesObjectDataExist(objID, "nitrogen_stress_factor")) {
context->getObjectData(objID, "nitrogen_stress_factor", N_stress);
N_stress = std::clamp(N_stress, 0.0f, 1.0f);
}
// Scale photosynthetic parameters
coeffs.Vcmax *= N_stress;
coeffs.Jmax *= N_stress;

This integration is optional - the Photosynthesis plugin works with or without the nitrogen model.

PROSPECT Leaf Optics Integration

Direct compatibility: The PROSPECT model requires area-based nitrogen (Leaf Nitrogen Density), which is exactly what this model provides.

The nitrogen model writes object data "leaf_nitrogen_gN_m2" (g N/m \(^2\)) for each leaf, and LeafOptics::run() reads that object data directly, so no manual conversion is required:

// PlantArchitecture writes "leaf_nitrogen_gN_m2" object data when the nitrogen model is enabled.
LeafOptics leafoptics(&context);
leafoptics.run( plantarch.getAllLeafUUIDs(), params );

Internally the conversion to the PROSPECT chlorophyll input is \(C_{ab}\,(\mu g/cm^2) = N_{area}\,(g/m^2)\times 100\times f_{photosynthetic}\times k_{N\rightarrow C_{ab}}\).

Why this matters: PROSPECT and other leaf optical models work with area-based properties because reflectance is determined by the amount of absorbing material per unit leaf area, not per unit mass. Using area-based nitrogen eliminates the need for SLA-based conversions that introduce additional uncertainty.

Growth Integration

PlantArchitecture growth calculations can read the stress factor:

// In shoot elongation calculation
float N_stress = 1.0f;
if (context_ptr->doesObjectDataExist(shoot_objID, "nitrogen_stress_factor")) {
context_ptr->getObjectData(shoot_objID, "nitrogen_stress_factor", N_stress);
}
// Apply to growth rate
float adjusted_elongation = base_elongation_rate * N_stress;

Complete Usage Example

This example shows a complete simulation with the nitrogen model:

#include "Context.h"
using namespace helios;
int main() {
Context context;
// Build plant canopy
PlantArchitecture plantarch(&context);
plantarch.loadPlantModelFromLibrary("bean");
std::vector<uint> plantIDs = plantarch.buildPlantCanopyFromLibrary(
make_vec3(0,0,0), make_vec2(0.3, 0.3), make_int2(10, 10), 30);
// Enable and configure nitrogen model (area-based parameters)
plantarch.enableNitrogenModel();
N_params.target_leaf_N_area = 1.8f; // 1.8 g N/m²
N_params.minimum_leaf_N_area = 0.6f; // 0.6 g N/m²
N_params.root_allocation_fraction = 0.15f; // 15% to roots
N_params.max_N_accumulation_rate = 0.12f; // 0.12 g N/m²/day
plantarch.setPlantNitrogenParameters(plantIDs, N_params);
// Initialize nitrogen pools (area basis - no SLA needed!)
plantarch.initializeNitrogenPools(1.2f); // 1.2 g N/m² initial
// Setup photosynthesis (automatically uses N stress factor if available)
PhotosynthesisModel photosynthesis(&context);
photosynthesis.setFarquharCoefficientsFromLibrary("bean");
// Time loop
for (int day = 100; day <= 250; day++) {
context.setDate(make_Date(day, 2024));
// Apply nitrogen on specific days
if (day == 115) {
plantarch.addPlantNitrogen(plantIDs, 0.5f); // 0.5 g N per plant
}
if (day == 145) {
plantarch.addPlantNitrogen(plantIDs, 0.75f); // 0.75 g N per plant
}
// PlantArchitecture accumulates N in leaves and updates stress factor
plantarch.advanceTime(1.0f);
// Photosynthesis reads stress factor and scales Vcmax/Jmax
photosynthesis.run();
// Optional: query nitrogen status
std::vector<uint> plant_objIDs = plantarch.getAllPlantObjectIDs(plantIDs[0]);
if (!plant_objIDs.empty()) {
float N_stress;
context.getObjectData(plant_objIDs[0], "nitrogen_stress_factor", N_stress);
std::cout << "Day " << day << " N stress factor: " << N_stress << std::endl;
}
}
return 0;
}

Design Principles

The nitrogen model follows several key design principles:

1. Simplicity Through Area-Based Tracking

  • Only 7 parameters (compared to 8 in mass-based approach)
  • No SLA parameter needed (eliminated)
  • ~450 lines of code
  • No biomass calculations or unit conversions
  • Always numerically stable
  • Direct compatibility with leaf optical models

2. Physiological Realism

Why area-based nitrogen is more realistic:

Research indicates that as leaves mature and thicken (LMA increases 2-3×), the nitrogen content per area (g N/m²) remains relatively constant, while mass-based concentration (g N/g DW) decreases. This occurs because:

  1. Leaf thickening is primarily due to structural tissue (cell walls, lignin)
  2. Photosynthetic machinery (which contains most leaf N) scales with leaf area, not mass
  3. Rubisco content is determined by light capture, which depends on leaf area

By tracking nitrogen on an area basis, the model:

  • Naturally captures the constancy of N_area as leaves thicken
  • Eliminates the artificial need to track changing LMA or SLA
  • Aligns with how nitrogen actually functions in leaves (per unit light-capturing area)

3. Integration with PlantArchitecture

  • Follows the same pattern as CarbohydrateModel
  • Part of PlantArchitecture plugin, not a separate plugin
  • Shares data structures (PlantInstance, Shoot, Phytomer)
  • Integrates seamlessly with advanceTime() loop

4. Loose Coupling

  • Single output variable: nitrogen stress factor
  • Other plugins decide whether and how to use it
  • No direct dependencies on Photosynthesis or other plugins
  • Model can be enabled/disabled independently

5. Fail-Fast Philosophy

  • Clear error messages using helios_runtime_error()
  • No silent fallbacks or default behaviors that hide issues
  • Validates inputs (negative amounts, missing plants, etc.)
  • Errors are caught immediately with actionable messages

Model Limitations

The nitrogen model is intentionally simplified and has several limitations:

  1. No soil nitrogen pool: Nitrogen is applied directly to plants, not through a soil model
  2. No root nitrogen uptake rate: Root pool is passive; uptake is instantaneous
  3. No nitrogen effects on growth built-in: Growth modifications must be implemented in calling code
  4. Static LMA assumption: Model assumes N_area is relatively constant; does not track LMA dynamics
  5. No temperature effects: All rates are constant regardless of temperature
  6. No nitrogen loss mechanisms: No volatilization, leaching, or denitrification
  7. Simplified remobilization: Age-based classification; no explicit senescence tracking

These limitations were intentional design choices to keep the model simple and maintainable while capturing the most important nitrogen dynamics.

References

The nitrogen model implementation is based on published research:

  1. Area-based vs. mass-based nitrogen: Research shows that reflectance spectra are directly sensitive to area-based content rather than mass-based content. The relationship is: N_mass (g/g) = N_area (g/m²) / LMA (g/m²).
  2. Remobilization efficiency (70%): Masclaux-Daubresse et al. (2010). "Nitrogen uptake, assimilation and remobilization in plants: challenges for sustainable and productive agriculture." Annals of Botany 105(7): 1141-1157.
  3. Age-based remobilization patterns: Hirel et al. (2007). "The challenge of improving nitrogen use efficiency in crop plants: towards a more central role for genetic variability and quantitative genetics within integrated approaches." Journal of Experimental Botany 58(9): 2369-2387.
  4. Nitrogen stress effects on photosynthesis: Evans (1989). "Photosynthesis and nitrogen relationships in leaves of C3 plants." Oecologia 78(1): 9-19.
  5. LMA dynamics and nitrogen: Poorter et al. (2009). "Causes and consequences of variation in leaf mass per area (LMA): a meta-analysis." New Phytologist 182(3): 565-588.
  6. PROSPECT and area-based nitrogen: N-PROSPECT models use Leaf Nitrogen Density (LND, µg/cm²) as input, which is equivalent to area-based nitrogen content.

Troubleshooting

No Nitrogen Stress Factor Output

Problem: The nitrogen_stress_factor object data does not exist or cannot be read.

Solutions:

Nitrogen Uptake Too Fast/Slow

Problem: Leaves reach target nitrogen too quickly or too slowly after application.

Solutions:

  • Adjust max_N_accumulation_rate parameter (increase for faster, decrease for slower)
  • Units are g N/m²/day (note: area-based, not total per day)
  • Typical range: 0.05-0.20 g N/m²/day depending on species and growth rate
  • Consider the time step (dt) used in advanceTime() - smaller steps = smoother accumulation

No Nitrogen Remobilization

Problem: Old leaves are not donating nitrogen to young leaves.

Solutions:

  • Check that plants have leaves of different ages (remobilization requires age gradient)
  • Verify max_leaf_lifespan is set correctly in PlantInstance (not zero or infinite)
  • Ensure remobilization_age_threshold is less than 1.0 (default 0.7)
  • Old leaves must have nitrogen above minimum_leaf_N_area to donate

Parameter Unit Confusion

Problem: Unclear about units or how to convert from literature values.

Solutions:

  • All nitrogen parameters use area basis (g N/m²), not mass basis (g N/g DW)
  • To convert from mass basis: N_area (g/m²) = N_mass (g/g) × LMA (g/m²)
  • Typical crop leaf N_area: 1.0-2.5 g N/m² (equivalent to 2-5% N at LMA = 40-50 g/m²)
  • For PROSPECT: multiply by 100 to get µg/cm² (1 g/m² = 100 µg/cm²)