Tamm.java

/*
 * Copyright (C) 2020 INRAE AgroClim
 *
 * This file is part of Indicators.
 *
 * Indicators is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Indicators is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indicators. If not, see <http://www.gnu.org/licenses/>.
 */
package fr.inrae.agroclim.indicators.model.indicator;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import fr.inrae.agroclim.indicators.exception.IndicatorsException;
import fr.inrae.agroclim.indicators.model.Knowledge;
import fr.inrae.agroclim.indicators.model.Parameter;
import fr.inrae.agroclim.indicators.model.data.DailyData;
import fr.inrae.agroclim.indicators.model.data.Resource;
import fr.inrae.agroclim.indicators.model.data.Variable;
import fr.inrae.agroclim.indicators.model.data.climate.ClimaticResource;
import fr.inrae.agroclim.indicators.util.Doublet;
import jakarta.xml.bind.annotation.XmlType;
import lombok.Getter;
import lombok.Setter;

/**
 * Risk of Monilia during a period.
 *
 * Fonction de calcul du %age de fleurs de cerisier moniliees (Monilia laxa)
 * établie par Tamm et al. (1995) à l'aide de mesures en chambres climatiques
 * (in vitro).
 *
 * https://dx.doi.org/10.1016/j.eja.2019.125960
 *
 * Last changed : $Date$
 *
 * @author $Author$
 * @version $Revision$
 */
@XmlType(propOrder = {"e", "gamma1", "gamma2", "imax", "m", "ro1", "ro2",
        "tMax", "tMin", "threshold"})
public final class Tamm extends SimpleIndicator implements Detailable {

    /**
     * UUID for Serializable.
     */
    private static final long serialVersionUID = 6030595237342422019L;

    /**
     * Valeur par défaut issue de Tamm et al., 1995 : 31.
     */
    @Getter
    @Setter
    private Double tMax = 31.;

    /**
     * Valeur par défaut issue de Tamm et al., 1995 : 0.
     */
    @Getter
    @Setter
    private Double tMin = 0.;

    /**
     * Valeur par défaut issue de Tamm et al., 1995 : 0.9.
     */
    @Getter
    @Setter
    private Double m = 0.9;

    /**
     * Valeur par défaut issue de Tamm et al., 1995 : 0.874.
     */
    @Getter
    @Setter
    private Double imax = 0.79;

    /**
     * Valeur par défaut issue de Tamm et al., 1995 : 1.816.
     */
    @Getter
    @Setter
    private Double gamma1 = 1.709;

    /**
     * Valeur par défaut issue de Tamm et al., 1995 : 1.736.
     */
    @Getter
    @Setter
    private Double gamma2 = 1.47221;

    /**
     * Valeur par défaut issue de Tamm et al., 1995 : 0.728.
     */
    @Getter
    @Setter
    private Double ro1 = 1.5751;

    /**
     * Valeur par défaut issue de Tamm et al., 1995 : 0.918.
     */
    @Getter
    @Setter
    private Double ro2 = 1.9652;

    /**
     * Facteur pluvio/(pluvio+E) pour forcer le modèle à 0 pour pluvio = 0.
     */
    @Getter
    @Setter
    private double e = 0.029;

    /**
     * Seuil infectieux.
     */
    @Getter
    @Setter
    private double threshold = 0.50;

    /**
     * @param data daily data
     * @return weighted risk for a day.
     */
    public double computeDailyValue(final DailyData data) {
        final double rain = data.getValue(Variable.RAIN);
        final double tmean = data.getValue(Variable.TMEAN);
        final double phi = (tmean - tMin) / (tMax - tMin);
        final double intermI0 = Math.pow(phi, gamma2);
        final double i0 = gamma1 * intermI0 * (1 - phi);
        final double intermR = Math.pow(phi, ro2);
        final double r = ro1 * intermR * (1 - phi);
        final double term1 = 1 - Math.pow(i0, 1 - m);
        final double term2 = Math.exp(-r * rain);
        final double partA = rain / (rain + e);
        final double partB = Math.pow(1 - term1 * term2, 1 / (1 - m));
        final double flowerRisk = partA * imax * partB;
        if (flowerRisk < threshold) {
            return 0;
        }
        return flowerRisk;
    }

    @Override
    public double computeSingleValue(final Resource<? extends DailyData> data) throws IndicatorsException {
        return data.getData().stream() //
                .map(this::computeDailyValue) //
                .reduce((v1, v2) -> v1 + v2).orElseThrow();
    }

    @Override
    public List<Doublet<Parameter, Number>> getParameterDefaults() {
        // no substitution
        return List.of();
    }

    /**
     * @return no parameters for the indicator.
     */
    @Override
    public List<Parameter> getParameters() {
        return List.of();
    }

    @Override
    public Map<String, Double> getParametersValues() {
        // no substitution
        return Map.of();
    }

    @Override
    public Set<Variable> getVariables() {
        final Set<Variable> variables = new HashSet<>();
        variables.add(Variable.RAIN);
        variables.add(Variable.TMEAN);
        return variables;
    }

    @Override
    public boolean isComputable(final Resource<? extends DailyData> res) {
        if (res == null) {
            throw new IllegalArgumentException("resource must no be null!");
        }
        if (!(res instanceof ClimaticResource)) {
            throw new IllegalArgumentException(getClass().getName()
                    + " handles only ClimaticResource, not "
                    + res.getClass().getName());
        }
        return true;
    }

    @Override
    public void setParametersFromKnowledge(final Knowledge knowledge) {
        // no substitution
    }

    @Override
    public void setParametersValues(final Map<String, Double> values) {
        // no substitution
    }

    @Override
    public String toStringTree(final String indent) {
        return toStringTreeBase(indent);
    }

    @Override
    public void removeParameter(final Parameter param) {
        // no substitution: nothing to do
    }
}