@@ -0,0 +1,94 @@
|
||||
"""Динамические нормы по уравнениям GfE (Германия)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
USP_DYNAMIC_KEY = "usp"
|
||||
NEL_DYNAMIC_KEY = "nel"
|
||||
|
||||
# GfE 2001 Milchkühe — Erhaltung + Milch (Standardmilch / FCM)
|
||||
NEL_MAINTENANCE_COEFF = 0.293 # MJ NEL / (kg LM)^0,75 / Tag
|
||||
NEL_PER_KG_MILK_MJ = 3.3 # MJ NEL / kg Milch (FCM)
|
||||
|
||||
|
||||
def gfe_usp_min_g(
|
||||
mass_kg: float | None,
|
||||
milk_yield_kg: float | None = None,
|
||||
) -> float | None:
|
||||
"""
|
||||
Минимальная суточная потребность в усвояемом протеине (уСП / nXP), г/сут.
|
||||
|
||||
GfE: 0,09 × (масса^0,75) × 6,25 + удой × 85 г.
|
||||
"""
|
||||
if mass_kg is None or mass_kg <= 0:
|
||||
return None
|
||||
maintenance = 0.09 * (mass_kg**0.75) * 6.25
|
||||
milk = max(float(milk_yield_kg or 0), 0.0) * 85.0
|
||||
return maintenance + milk
|
||||
|
||||
|
||||
def gfe_nel_min_mj(
|
||||
mass_kg: float | None,
|
||||
milk_yield_kg: float | None = None,
|
||||
) -> float | None:
|
||||
"""
|
||||
Минимальная суточная потребность в ЧЭЛ (NEL), МДж/сут.
|
||||
|
||||
GfE 2001: 0,293 × LM^0,75 + удой × 3,3 (MJ NEL на кг молока).
|
||||
"""
|
||||
if mass_kg is None or mass_kg <= 0:
|
||||
return None
|
||||
maintenance = NEL_MAINTENANCE_COEFF * (mass_kg**0.75)
|
||||
milk = max(float(milk_yield_kg or 0), 0.0) * NEL_PER_KG_MILK_MJ
|
||||
return maintenance + milk
|
||||
|
||||
|
||||
def preview_dynamic_norms(
|
||||
mass_kg: float | None,
|
||||
milk_yield_kg: float | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Расчётные min по GfE для UI (без учёта сохранённых норм)."""
|
||||
_, dynamic = apply_dynamic_norms(
|
||||
{},
|
||||
mass_kg=mass_kg,
|
||||
milk_yield_kg=milk_yield_kg,
|
||||
)
|
||||
return dynamic
|
||||
|
||||
|
||||
def apply_dynamic_norms(
|
||||
stored: dict[str, dict[str, float | None]],
|
||||
*,
|
||||
mass_kg: float | None,
|
||||
milk_yield_kg: float | None,
|
||||
ration_type: str | None = None,
|
||||
force_dynamic: bool = False,
|
||||
) -> tuple[dict[str, dict[str, float | None]], dict[str, Any]]:
|
||||
"""
|
||||
Заполняет нормы по GfE, если в БД min не задан.
|
||||
force_dynamic=True — пересчитать min уСП/ЧЭЛ по массе и удою даже при нормах в БД
|
||||
(автоготовка с явными mass_kg / milk_yield_kg).
|
||||
Возвращает (resolved_norms, dynamic_meta).
|
||||
"""
|
||||
del ration_type
|
||||
resolved: dict[str, dict[str, float | None]] = {
|
||||
key: {"min": bounds.get("min"), "max": bounds.get("max")}
|
||||
for key, bounds in stored.items()
|
||||
}
|
||||
dynamic: dict[str, Any] = {}
|
||||
|
||||
for key, compute, formula in (
|
||||
(USP_DYNAMIC_KEY, gfe_usp_min_g, "0.09×масса^0.75×6.25 + удой×85"),
|
||||
(NEL_DYNAMIC_KEY, gfe_nel_min_mj, "0.293×масса^0.75 + удой×3.3"),
|
||||
):
|
||||
bounds = resolved.get(key, {"min": None, "max": None})
|
||||
if force_dynamic or bounds.get("min") is None:
|
||||
computed = compute(mass_kg, milk_yield_kg)
|
||||
if computed is not None:
|
||||
entry = dict(bounds)
|
||||
entry["min"] = computed
|
||||
resolved[key] = entry
|
||||
dynamic[key] = {"min": computed, "formula": formula}
|
||||
|
||||
return resolved, dynamic
|
||||
Reference in New Issue
Block a user