import numpy as np
from hestia_earth.calculation.abstract_model import Model
from hestia_earth.calculation.utils import most_relevant_measurement, residue_nitrogen, summation, primary_product
from hestia_earth.calculation.data.constants.nox import NOX_FACTORS_BY_CLIMATE_ZONE, DRY_MATTER_FACTOR_TO_NOX
from hestia_earth.calculation.data.constants.generic import ATOMIC_WEIGHT_CONVERSIONS
from hestia_earth.utils.api import download_hestia
MODEL_KEY = 'noxToAirFertilizerAndExcretaStehfestBouwman2006'
[docs]class NOXToAirFertilizerAndExcretaStehfestBouwman2006(Model):
def __init__(self):
# Define model tier
self.tier = 2
self.term = download_hestia(MODEL_KEY)
# Define model requirements
self.nitrogenContent = None
self.eco_ClimateZone = None
self.crop_name = None
self.inorgN_total = None
self.orgN_total = None
self.excretaN_total = None
self.res_nitrogen = None
self.N_total = None
# Instantiate variables
self.noxToAirInorganicFertilizer = None
self.noxToAirOrganicFertilizer = None
self.noxToAirExcreta = None
self.noxToAirCropResidueDecomposition = None
self.noxToAirAllOrigins = None
# Define model coefficients
self.crop_nox_n = NOX_FACTORS_BY_CLIMATE_ZONE
self.conv_non_no = ATOMIC_WEIGHT_CONVERSIONS['Conv_Mol_NON_NO']
self.drymatter_to_nox = DRY_MATTER_FACTOR_TO_NOX
def calculate_noxToAirInorganicFertilizer(self):
self.noxToAirInorganicFertilizer = \
self.calculate_noxToAirAllOrigins() * self.inorgN_total / self.N_total
return self.noxToAirInorganicFertilizer
def calculate_noxToAirOrganicFertilizer(self):
self.noxToAirOrganicFertilizer = self.calculate_noxToAirAllOrigins() * self.orgN_total / self.N_total
return self.noxToAirOrganicFertilizer
def calculate_noxToAirExcreta(self):
self.noxToAirExcreta = self.calculate_noxToAirAllOrigins() * self.excretaN_total / self.N_total
return self.noxToAirExcreta
def calculate_noxToAirCropResidueDecomposition(self):
self.noxToAirCropResidueDecomposition = \
self.calculate_noxToAirAllOrigins() * self.res_nitrogen / self.N_total
return self.noxToAirCropResidueDecomposition
def calculate_noxToAirAllOrigins(self):
self.noxToAirAllOrigins = min(
0.025 * self.N_total,
np.exp(-0.451 + 0.0061 * self.N_total + (0 if self.nitrogenContent / 1000000 < 0.0005 else
-1.0211 if self.nitrogenContent / 1000000 <= 0.002
else 0.7892) +
self.crop_nox_n[str(self.eco_ClimateZone)]) -
np.exp(-0.451 + (0 if self.nitrogenContent / 1000000 < 0.0005 else
-1.0211 if self.nitrogenContent / 1000000 <= 0.002 else 0.7892) +
self.crop_nox_n[str(self.eco_ClimateZone)])) * self.conv_non_no
return self.noxToAirAllOrigins
def get_total_n(self, cycle):
inputs = cycle["inputs"].evalues()
N_total = summation([sum(input["value"]) if "units" in input["term"] and input["term"]["units"] == "kg N"
else {} for input in inputs])
return [N_total]
def complete(self, completeness):
self.inorgN_total = 0 if self.inorgN_total == {} and completeness['fertilizer'] else self.inorgN_total
self.orgN_total = 0 if self.orgN_total == {} and completeness['fertilizer'] else self.orgN_total
self.excretaN_total = 0 if self.excretaN_total == {} and completeness['products'] else self.excretaN_total
self.res_nitrogen = 0 if self.res_nitrogen == {} and completeness['cropResidue'] else self.res_nitrogen
self.N_total = 0 if self.N_total == {} and completeness['fertilizer'] and completeness['products'] and\
completeness['cropResidue'] else self.N_total
def check_noxToAirInorganicFertilizer(self, cycle):
inputs = cycle['inputs'].evalues()
self.inorgN_total = summation([sum(input['value']) if 'units' in input['term'] and
input['term']['units'] == 'kg N' and
input['term']['termType'] == 'inorganicFertilizer'
else {} for input in inputs])
self.complete(cycle['dataCompleteness'])
return self.inorgN_total != {} and self.check_noxToAirAllOrigins(cycle) and self.N_total != 0
def check_noxToAirOrganicFertilizer(self, cycle):
inputs = cycle['inputs'].evalues()
self.orgN_total = summation([sum(input['value']) if 'units' in input['term'] and
input['term']['units'] == 'kg N' and
input['term']['termType'] == 'organicFertilizer' else {}
for input in inputs])
self.complete(cycle['dataCompleteness'])
return self.orgN_total != {} and self.check_noxToAirAllOrigins(cycle) and self.N_total != 0
def check_noxToAirCropResidueDecomposition(self, cycle):
self.res_nitrogen = residue_nitrogen(cycle['products'])
self.complete(cycle['dataCompleteness'])
return self.res_nitrogen != {} and self.check_noxToAirAllOrigins(cycle) and self.N_total != 0
def check_noxToAirExcreta(self, cycle):
products = cycle["products"].evalues()
self.excretaN_total = summation([sum(product["value"]) if "units" in product["term"] and
product["term"]["units"] == "kg N" and
product["term"]["termType"] == "animalProduct" else {}
for product in products])
self.complete(cycle['dataCompleteness'])
return self.excretaN_total != {} and self.check_noxToAirAllOrigins(cycle) and self.N_total != 0
def check_noxToAirAllOrigins(self, cycle):
self.eco_ClimateZone = most_relevant_measurement(cycle['site']['measurements']['eco-ClimateZone'],
cycle['endDate'])
self.nitrogenContent = most_relevant_measurement(cycle['site']['measurements']['soilTotalNitrogenContent'],
cycle['endDate'])
product = primary_product(cycle['products']) if cycle['products'] != {} else {}
self.crop_name = product['term']['name'] if product != {} else {}
self.N_total = summation(self.get_total_n(cycle) + [residue_nitrogen(cycle['products'])])
self.complete(cycle['dataCompleteness'])
return self.N_total != {} and self.nitrogenContent != {} and self.eco_ClimateZone != {} and self.crop_name != {}