Languages: 🇺🇸 English | 🇧🇷 Português (Brasil)
Fertpy is a domain-driven, rule-based agronomic calculation engine for soil correction and nutrient dose computation.
It provides a structured computational framework to transform agronomic technical bulletins into executable, deterministic calculation models using YAML-based rule definitions.
Fertpy does not provide agronomic recommendations or consultancy.
It performs reproducible calculations based strictly on predefined technical criteria.
Fertpy is a library for agronomic modeling and calculation, designed to transform technical bulletins into structured, reproducible, and traceable computational models.
The library enables:
- Representing agronomic recommendations (technical bulletins) as structured models
- Performing deterministic calculations for:
- Soil correction (e.g., liming)
- Nutrient recommendations (N, P₂O₅, K₂O)
- Fertilizer formulation (blends and commercial sources)
- Optimizing formulations based on:
- Minimum input usage
- Minimum cost
- Working with both simple and compound sources (e.g., commercial NPK)
- Ensuring transparency, traceability, and consistency in calculations
The project follows a modular architecture with a clear separation of responsibilities:
core/domain/→ Entities and business rules (recommendations, fertilizers, criteria, etc.)core/engine/→ Agronomic calculation and formulation enginecore/analysis/→ Auxiliary analyses (diagnostics, proportions, limiting nutrients)core/factories/→ Creation of domain objects (fertilizers, soil amendments)
nutrientes/→ Calculation interfaces by nutrient (N, P, K)correcao_solo/→ Soil correction models (e.g., liming)formulacao/→ Fertilization and liming formulation interface
infra/loaders/→ Data loading (YAML)infra/parsing/→ Parsing and interpretation of agronomic rulesservices/→ Validations and auxiliary rules
knowledge/→ Structured definitions of agronomic bulletins- Recommendation rules by crop
- Nutrient and amendment sources
- Versioned technical parameters
cli/→ Command-line interface for running calculationsutils/→ Shared utilities
Fertpy was built based on:
- Clear separation between domain, infrastructure, and knowledge
- Explicit modeling of agronomic rules
- Extensibility for new crops, nutrients, and sources
- Reproducibility of calculations
- Interface independence (API, CLI, etc.)
fertpy/
├── core/ # Domain and calculation engine
├── formulacao/ # Fertilization and liming formulation
├── nutrientes/ # Nutrient-based calculations
├── correcao_solo/ # Soil correction
├── infra/ # Loading and parsing
├── knowledge/ # Agronomic knowledge base (YAML)
├── cli/ # Command-line interface
├── services/ # Validations
└── utils/ # Utilities
Current knowledge models are derived from Brazilian agronomic technical bulletins.
While the calculation engine itself is framework-agnostic, the implemented knowledge artifacts are primarily valid within Brazilian agronomic contexts.
Future extensions may include regional parameterization and additional technical sources.
- Domain-Driven Design (DDD)
- Deterministic rule-based modeling
- YAML as knowledge representation layer
- Hybrid architecture:
- N-dimensional rule-based models
- Fixed algebraic formula-based models (e.g., liming)
- Strict separation between the calculation engine and agronomic criteria
- Extensibility for new crops and technical bulletins
- Traceability of technical sources
- Deterministic reproducibility of results
- Python >= 3.11
- pip
To install the latest stable release:
pip install fertpyTo verify the installation:
python -c "import fertpy; print(fertpy.__version__)"If you intend to contribute or modify the project:
git clone https://github.com/lhpeguin/fertpy.git
cd fertpypython -m venv venv
# Linux / macOS:
source venv/bin/activate
# Windows:
venv\Scripts\activateStandard installation (library usage)
pip install -e .Development installation (with tests)
pip install -e .[dev]This also installs the development dependencies, including 'pytest', required to run the test suite.
Fertpy includes an automated test suite implemented with pytest, ensuring the deterministic consistency of agronomic models and formulation algorithms.
The tests cover:
- Soil correction calculation (Liming)
- Nitrogen (N) calculation
- Phosphorus (P) calculation
- Potassium (K) calculation
- Fertilizer formulation across multiple scenarios
- Liming formulation with different sources
- Calculation of nutrients supplied by combinations
- Recommendations based on source combinations
- Error handling and input validation
- Edge cases and validation scenarios
- Stability in reading and interpreting YAML artifacts
The test suite acts as a mechanism for:
- Regression prevention
- Verification of architectural integrity
- Ensuring scientific reproducibility of calculations
- Cross-validation between agronomic rules and declarative data (YAML)
After installing the project dependencies:
pytestAlternatively:
python -m pytestAll tests must pass successfully before submitting structural changes or expanding the knowledge base.
This example demonstrates the complete workflow of using fertpy, including:
- Agronomic calculation (liming + macronutrients)
- Nutritional demand generation
- Fertilizer formulation with:
- Simple sources (on-farm blending)
- Compound sources (commercial NPK)
- Planting and topdressing strategy
from fertpy import Calagem, Nitrogenio, Fosforo, Potassio
# Liming
calagem = Calagem("milho")
resultado_calagem = calagem.calcular(v_atual=55, ctc=70)
print(f"\nLime requirement: {resultado_calagem['dose']} t/ha\n")
# Primary macronutrients
n = Nitrogenio("milho", "graos")
p = Fosforo("milho", "graos")
k = Potassio("milho", "graos")
n_resultado = n.calcular("media_baixa", 12.1)
p_resultado = p.calcular(26, 12.1)
k_resultado = k.calcular(3, 12.1)
def print_nutrient_calculation(r):
print(
f"Nutrient: {r.nutriente}\n"
f"Dose: {r.dose} {r.unidade}\n"
f"Class: {r.classe.nome}\n"
f"Bulletin: {r.fonte['documento']} "
f"({r.fonte['instituicao']}, {r.fonte['ano']})\n"
)
if r.fracionamento:
print("Split application:")
print(f" At planting: {r.fracionamento['plantio']} {r.unidade}")
print(f" Topdressing: {r.fracionamento['cobertura']} {r.unidade}\n")
# Output
print_nutrient_calculation(n_resultado)
print_nutrient_calculation(p_resultado)
print_nutrient_calculation(k_resultado)From the calculated recommendations, we consolidate the nutrient demand:
recomendacao_n = n_resultado
fracionamento_n = n_resultado.fracionamento
demanda_total = {
"N": recomendacao_n.dose,
"P2O5": p_resultado.dose,
"K2O": k_resultado.dose
}
demanda_plantio = {
"N": fracionamento_n["plantio"] if fracionamento_n else recomendacao_n.dose,
"P2O5": p_resultado.dose,
"K2O": k_resultado.dose
}
demanda_cobertura = {
"N": fracionamento_n["cobertura"] if fracionamento_n else 0
}from fertpy import FormulacaoAdubacao
formulador = FormulacaoAdubacao()
def print_formulation(resultado, titulo):
print(f"\n=== {titulo} ===\n")
for fonte, dose in resultado["doses"].items():
print(f"- {fonte}: {dose:.2f} {resultado['unidade']}")
if resultado.get("custo_total") is not None:
print(f"\nTotal cost: R$ {resultado['custo_total']:.2f}")Optimized combination of basic fertilizers:
resultado_simples = formulador.calcular(
demanda=demanda_total,
fontes=["ureia", "map", "kcl"],
# Source prices (R$/ton)
precos={
"ureia": 2500,
"map": 3200,
"kcl": 2800
},
solver="custo_minimo"
)
print_formulation(resultado_simples, "Formulation with simple sources (blend)")Selection of the best commercial formulation based on demand:
# Commercial compound fertilizers (NPK)
# Format: "name:N=%,P2O5=%,K2O=%"
fontes_npk = [
"NPK_04_14_08:N=4,P2O5=14,K2O=8",
"NPK_08_28_16:N=8,P2O5=28,K2O=16",
"NPK_20_05_20:N=20,P2O5=5,K2O=20",
"NPK_12_24_20:N=12,P2O5=24,K2O=20"
]
# Source prices (R$/ton)
precos_npk = {
"NPK_04_14_08": 2500,
"NPK_08_28_16": 2600,
"NPK_20_05_20": 3000,
"NPK_12_24_20": 3000
}
resultado_npk = formulador.calcular(
demanda=demanda_plantio,
entradas_fontes=fontes_npk,
precos=precos_npk,
solver="custo_minimo",
modo="fonte_unica", # selects a single commercial formulation
tolerancia=0.1 # demand tolerance (10%)
)
print_formulation(resultado_npk, "Compound source (commercial NPK)")Fertilization split according to management strategy:
- Planting: application of N, P, and K at sowing
- Topdressing: supplemental Nitrogen application during the crop cycle
# Source prices (R$/ton)
precos = {
"ureia": 2500,
"map": 3200,
"kcl": 2800
}
# Planting fertilization (full NPK)
resultado_plantio = formulador.calcular(
demanda=demanda_plantio,
entradas_fontes=["ureia", "map", "kcl"],
precos=precos,
solver="custo_minimo"
)
# Topdressing fertilization (Nitrogen only)
resultado_cobertura = formulador.calcular(
demanda=demanda_cobertura,
entradas_fontes=["ureia"],
precos={"ureia": 2500},
solver="custo_minimo"
)
print_formulation(resultado_plantio, "Planting fertilization")
print_formulation(resultado_cobertura, "Topdressing fertilization")Optimized selection of soil amendments based on cost and acidity neutralization efficiency.
from fertpy import FormulacaoCalagem
formulador = FormulacaoCalagem()
# Amendment prices (R$/ton)
precos = {
"calcario_agricola": 150,
"calcario_calcinado": 180
}
resultado = formulador.calcular(
cultura="milho",
v_atual=40,
ctc=10,
entradas_fontes=["calcario_agricola", "calcario_calcinado"],
precos=precos,
solver="custo_minimo"
)
def print_limimg_formulation(resultado):
print("\n=== Liming Formulation ===\n")
for fonte, dose in resultado["doses"].items():
print(f"- {fonte}")
print(f" Dose: {dose:.2f} {resultado['unidade']}")
if "custo_por_fonte" in resultado:
custo = resultado["custo_por_fonte"].get(fonte)
if custo is not None:
print(f" Cost: R$ {custo:.2f}")
print()
if "custo_total" in resultado:
print(f"Total cost: R$ {resultado['custo_total']:.2f}\n")
print_limimg_formulation(resultado)- The
quantidade_minimasolver (default) selects the combination that meets the demand with the lowest total amount of inputs - The
custo_minimosolver selects the most economical combination based on the provided prices - The
modo="mistura"(default) allows combining multiple sources (simple or compound) to optimize the result - The
modo="fonte_unica"forces the selection of a single source (ideal for commercial NPK formulas) - The separation between planting and topdressing enables greater agronomic precision in management
- The model is extensible to micronutrients and new sources
The formulation considers tolerance limits to avoid excess or deficiency of nutrients:
toleranciadefines the maximum percentage of allowed excess per nutrient- Default value: 5%
tol_sup(upper tolerance) can be manually defined to control excess more strictly or flexiblytol_inf(lower tolerance) allows controlled deficit in the formulation
By default:
tol_sup = toleranciatol_inf = 0%(does not allow nutrient deficiency)
Depending on the configuration, the result may vary significantly:
- With
solver="quantidade_minima", the focus is on reducing the total applied volume - With
solver="custo_minimo", the focus is on reducing total cost - With
modo="mistura", the system can combine multiple sources to optimize the result - With
modo="fonte_unica", the system selects only a single formulation
Practical example:
Optimized blend (lowest cost):
npk_08_28_16: 11.76 kg/ha
npk_20_05_20: 25.88 kg/ha
npk_16_12_8: 461.76 kg/ha
Total cost: R$ 1493.53/ha
Single source (operationally simpler):
npk_16_12_8: 500.00 kg/ha
Total cost: R$ 1500.00/ha
Fertpy adopts a declarative knowledge-based approach, in which agronomic rules are defined in YAML files.
This model allows a complete separation between:
- Agronomic rules (data)
- Calculation logic (code)
The rules used by the calculation engine are organized in:
src/fertpy/knowledge/
Example structure:
knowledge/
└── boletim_100/
├── adubacao/
│ ├── fontes.yaml
│ └── milho/
│ ├── graos/
│ │ ├── nitrogenio.yaml
│ │ ├── fosforo.yaml
│ │ └── potassio.yaml
│ └── silagem/
└── calagem/
├── fontes.yaml
└── milho.yaml
Characteristics:
- Hierarchical organization by crop and purpose
- Data derived from agronomic bulletins
- Separation by nutrient and usage context
- Structure optimized for consumption by the calculation engine
System validation uses a complementary structure in:
tests/knowledge/
Example:
knowledge/
├── nitrogenio.yaml
├── fosforo.yaml
├── potassio.yaml
├── calagem.yaml
├── formulacao_cenarios_adubacao.yaml
├── formulacao_cenarios_calagem.yaml
├── nutrientes_fornecidos.yaml
└── recomendacao_combinacao.yaml
Characteristics:
- Scenario-oriented organization for testing
- Each file defines:
- Inputs
- Expected outputs
- Structure decoupled from the internal organization of the library
- Focus on functional validation and reproducibility
This separation between production knowledge and validation knowledge enables:
- Clear traceability of agronomic rules
- Independent validation of the implementation
- Ease of creating new test scenarios
- Reduced coupling between data and code
- Greater scientific reliability of results
Fundamental principle
Every agronomic rule must be:
- Declarative (defined in YAML)
- Traceable to a technical source
- Testable through reproducible scenarios
Current Version: v0.3.0 — Introduction of the Formulation Engine, initial CLI, and Nitrogen splitting
Fertpy now incorporates a complete input formulation system, maintaining existing agronomic calculations while adding source selection, optimization by quantity or cost, nitrogen splitting between planting and topdressing, and a command-line interface (CLI).
The project is under active development, with a focus on expanding the knowledge base, improving the formulation engine, and consolidating the domain-oriented architecture.
- Support for maize (corn) crop
- Soil correction calculation (liming)
- Calculation of Nitrogen (N), Phosphorus (P₂O₅), and Potassium (K₂O) doses
- Optimized fertilization and liming calculations considering multiple sources
- Optimization by minimum quantity or lowest cost
- Fertilizer formulation with multiple sources
- Liming formulation with multiple soil amendments
- Optimized calculation of the best source to be used, considering:
- Minimum applied quantity
- Minimum cost (including price and freight)
- Automatic selection of the most suitable source for each scenario
- Calculation of nutrients supplied by each source
- Identification of limiting nutrient in the formulation
- Nitrogen splitting between planting and topdressing, allowing staged application according to crop needs
- Diagnosis of the generated solution
- Execution of formulations via terminal
- Commands for:
- Formulation (
formular)
- Formulation (
- Structured system for arguments and parameter validation
- Support for technical sources (fertilizers and soil amendments) via YAML
- Organization of technical knowledge by domain (fertilization and soil correction)
- Specialized loaders for fertilization, soil correction, and sources
- Modular structure for multiple purposes (e.g., grain, silage)
- Scenario-based automated test suite
- YAML-driven tests for deterministic validation
- Coverage of:
- Nutrient calculations
- Formulation (scenarios and errors)
- Combined recommendations
- Explicit separation between agronomic domain, loading infrastructure, calculation engine, and CLI
- Domain-Driven Design (DDD) architecture
- Introduction of factories for creating fertilizers and soil amendments
- Decoupled formulation engine with support for optimization strategies
- Directory-structured knowledge base
- Declarative representation of agronomic knowledge
- Deterministic and auditable evaluation
- Support for N-dimensional criteria
- Introduction of fertilization and liming formulation engine
- Support for multiple input sources
- Optimization by minimum cost and minimum quantity
- Implementation of nutrient supply calculations
- Identification of limiting nutrient
- Nitrogen splitting into planting/topdressing
- Creation of formulation diagnostic system
- Implementation of a complete CLI
- Introduction of factories for domain objects
- Expansion of the knowledge base with technical sources
- Evolution of the test suite with YAML-based scenarios
- Support for a single crop (maize/corn)
- Support only for resin extraction method in soil analysis
- Technical recommendations based on Boletim 100 (IAC), specific to the state of São Paulo
- Optimization model limited to deterministic criteria (no advanced heuristics)
Fertpy’s development is organized into evolutionary cycles focused on:
- Agronomic expansion
- Technical robustness
- Explainability
- Structural scalability
- Rule- and formula-based engine
- Support for corn (maize)
- Liming calculations
- Nitrogen (N), Phosphorus (P), and Potassium (K)
- Declarative YAML-based knowledge structure
- Complete reorganization of the knowledge structure
- Specialized loaders
- Clear separation between domain and infrastructure
- Introduction of services and utils layers
- Foundation prepared for multiple technical bulletins
Focus: transition from calculation to formulation.
- Introduction of fertilization and liming formulation engine
- Support for multiple fertilizer and soil amendment sources
- Optimization based on:
- Minimum quantity
- Minimum cost (price + freight)
- Calculation of nutrients supplied by each source
- Identification of limiting nutrient
- Formulation diagnostic system
- Implementation of CLI interface (
formularandnutrientes) - Introduction of factories for domain objects
- Expansion of the knowledge base with technical sources
- Evolution of the test suite with YAML-based scenarios
Focus: expansion of technical scope
- Explicit parameterization of inputs
- Expansion to new crops (soybean, beans, wheat, etc.)
- Nutritional expansion (secondary macronutrients and micronutrients)
- Improved structured error handling
- Consolidation and refinement of features introduced in the formulation engine
Focus: scientific auditability, technical transparency.
- Complete metadata for calculation and formulation
- Detailed tracking of solver decisions
- Internal trace/debug system
- Explain mode (
explain()) for interpreting recommendations - Agronomic justification for source selection
Focus: support for long-cycle production systems.
- Support for perennial crops (coffee, citrus, pastures, etc.)
- Modeling recommendations by phenological stage
- Fertilization and soil correction adjustments for continuous systems
- Structure for nutrient management across multiple cycles
- Adaptation of the formulation engine for perennial crop demands
Focus: stability, standardization, and project maturity.
- Refactoring and standardization of the public API
- Review and simplification of internal interfaces
- Increased test coverage and quality
- Standardization of errors and validation messages
- Improved technical documentation and usage examples
- Ensuring consistency across modules (domain, formulation, and CLI)
- Preparation for stable versioning (semantics and compatibility)
- Internationalization (other countries and agronomic systems)
- Graphical interface (web or desktop)
- Integration with real field data
- Hybrid models (deterministic + statistical)
This project is licensed under the Apache License 2.0.
See AUTHORS.md
Contributions are welcome and encouraged. The goal of Fertpy is to evolve as a reliable and reproducible foundation for agronomic calculations.
Before submitting a pull request, make sure to:
- Maintain the separation between domain and infrastructure
- Preserve the deterministic nature of the calculation logic
- Ensure that knowledge artifacts are traceable to technical sources
- Use the Conventional Commits standard
You can contribute in different ways:
- Refactoring while maintaining compatibility
- Performance optimizations
- Bug fixes
- Adding new crops
- New nutrient recommendations
- Adjustments based on technical bulletins
- New formulation algorithms
- Improvements to existing solvers (
custo_minimo,quantidade_minima) - New decision strategies
Files in tests/knowledge/ represent validated agronomic knowledge.
When adding or modifying a YAML:
- Use traceable data (bulletins, technical literature, institutions)
- Maintain structural consistency with existing files
- Avoid implicit rules in code — prioritize declarative definitions
To ensure scientific consistency and avoid regressions:
- Add a new YAML file in
tests/knowledge/ - Create or update the corresponding test in
tests/ - Ensure that:
- The scenario is reproducible
- Expected values are explicit
- Edge cases are considered when applicable
Before submitting any contribution:
pytestFertpy is an open-source software library that performs deterministic agronomic calculations based on structured technical criteria and published agronomic references.
The results generated by the software are intended for informational and technical purposes only and do not constitute agronomic recommendations, professional consulting, or decision-making services.
The interpretation and application of the results should consider factors specific to each situation, including soil conditions, cropping systems, management practices, analytical methods used, and applicable regional recommendations.
The analysis and interpretation of the results should be carried out by legally qualified professionals, in accordance with the professional responsibilities established by regulatory bodies (such as licensed agronomists or agricultural technicians).
Users are fully responsible for the interpretation of the results and for any decisions or actions taken based on the information produced by the software.
The authors and contributors of the project shall not be held responsible for any losses, damages, or consequences resulting from the direct or indirect use of this tool.