Economics2 est une librairie de calculs financiers implémenté dans la plateforme Capsis qui a pour ambition d'être :
D'autres outils de calculs économiques sont disponibles dans la plateforme Capsis, notamment :
Pour évaluer les indicateurs financiers d'une simulation, l'utilisateur doit définir un scénario sylvicole et un scénario “économique”. Le scénario sylvicole repose essentiellement (uniquement) sur le modèle de croissance.
Un scénario économique est défini à l'aide de date (début et fin notamment), du choix d'un cas de figure, du choix d'une liste d'opération économique et du choix d'une liste de prix.
Les opérations économiques sont définies par :
Les modèles de croissance et les interveners peuvent directement produire des opérations économiques et ces dernières peuvent être modifiées si besoin avant de calculer les indicateurs économiques.
Afin de pouvoir calculer les recettes ou coûts renseignés par classe de diamètre, par essence et par catégorie de bois (ex. chauffage, bois d'oeuvre, …), l'utilisateur doit donner un prix par mètre cube, pour chaque combinaison de classe de diamètre, de catégorie de bois et d'essence. Le choix des bornes et du nombre de classes de diamètre est libre.
Afin de pouvoir calculer les recettes, l'utilisateur peut aussi utiliser une fonction de prix de type Prix=f(volume unitaire). Actuellement cette fonction est de type a*ln(VU)+b
Il est possible de charger un fichier contenant une partie des informations nécessaires aux calculs économiques. Ce fichier est divisé en trois parties : (1) une partie avec des mots clefs (keywords), (2) une liste d'opérations économiques, (3) une liste de prix par classe de diamètre et par essence.
L'utilisation du fichier d’entrée économique est facultative (mais conseillée) puisque les informations peuvent toutes être fixées à l'aide d'un script ou de l’interface graphique (Model Tool).
La première partie du fichier d'entrée permet de définir le fonds et/ou le taux d'actualisation. Le fonds ou le taux d'actualisation est de -1 s'il n'est pas connu. C'est aussi dans cette partie que l'on choisit d'utiliser soit la liste de prix soit une fonction de prix avec possibilité d'indiquer également ses paramètres.
Les opérations économiques sont définies dans le fichier d'entrée par
Une ligne de la liste de prix est composée de trois colonne avec : * la borne supérieur d’une classe de diamètre. Les arbres dont le diamètre égale la valeur de cette borne supérieur auront comme prix assigné celui de la classe suivante (intervalle = [borne inf, borne sup[) * un prix par m³ * un code pour l’espèce. Ce code doit nécessairement correspondre à un code de la map « speciesDictionnary » donné par le Xmodel. * un code pour la catégorie de bois. Ce code doit nécessairement correspondre à un code de la map « categoryPriceDictionnary » donné par le Xmodel. Pour les modules n'utilisant pas plusieurs catégories de bois, ce champs peut-être laissé vide.
discountRate = -1 land = 3000 type = DBH_CLASS #date frequency label type trigger income price 0 {} plantation TREE_NUMBER ON_DATE false 0.24 0 {} Protection TREE_NUMBER ON_DATE false 0.22 1 {} Regarnissage FIXED ON_DATE false 318 -1 {1,6,2} Degagement FIXED ON_FREQUENCY false 400 17 {} Elagage FIXED ON_DATE false 1520 -1 {} chasse FIXED YEARLY true 40 # dbh (upper limit) prix species categorie (optionnel) 9.50 0.00 1 12.7 1.20 1 15.9 7.70 1 19.1 14.4 1 22.3 21.0 1 25.5 27.2 1 28.6 27.5 1 31.8 37.8 1 35.0 42.7 1 38.2 46.8 1 41.2 50.1 1 44.6 51.9 1 47.7 53.2 1 500.0 53.8 1 500.0 10 1 1
Avec l'interface graphique, la première étape consiste à réaliser une simulation avec les outils compatibles (module de croissance et intervener). C'est cette étape qui définit finalement toute la pertinence des résultats financiers qui découleront par la suite! Ensuite, l'utilisateur définit le scénario économique à l'aide de la boite à outils ad hoc et peut ensuite observer les résultats à l'aide de différents extracteurs.
La première étape consiste à construire ou charger une simulation réalisée à l’aide des outils compatibles avec la librairie.
Ensuite, sélectionner la dernière étape (step) d’un projet. Economics2 cherchera les opérations économiques associée à toutes les étapes depuis cette dernière étape jusqu’à la première étape créée (root). Puis, clic droit sur la dernière étape et ouverture de la boite à outil « Define Economics2 scenario».
Pour définir le « scénario économique »:
Le premier outil à ouvrir une fois le scénario défini, est le « Economics2 Text Viewer » disponible dans la liste des « visualisateurs ». Cet outil montre sous forme de texte les paramètres et résultats principaux du scénarios. Les indicateurs affichés dépendent des choix effectué lors de la définition du scénario (notamment du choix du cas de figure). On y retrouve les dates du scénario, le taux d’actualisation donné ou calculé à l’aide de la valeur du fonds, une description des peuplements à certaines dates clefs (eg. Dates initiale et finale), des estimations de la valeur initiale de la forêt, des indicateurs de rentabilité (TIR, annuité, …) et la liste de tous les flux.
J’encourage tous les utilisateur à utiliser, au moins une fois, cette liste de flux pour vérifier/recalculer/bien comprendre les différents calculs effectués par Economics2.
Dans la liste des graphiques disponibles, on retrouve
Il est possible d'exporter la liste des opérations économique une fois que le scénario économique est défini. Il faut pour cela effectuer un clic droit sur une scène, choisir export puis choisir “Economics2 : export”. L'export produit une fichier csv séparé par des points virgules.
Deux constructeurs de EconomicScenario sont disponibles suivant que l'on préfère ou non utiliser un fichier d’entrée économiques. Il y a également deux méthodes evaluate() qui peuvent être utilisée en mode script. Celle comportant uniquement deux paramètres ne peut être utilisée que pour le cas de figure numéro 1.
public class SimpleEconomicScript { public static void main (String[] args) throws Exception { C4Script script = new C4Script ("gymnos"); //Create a virtual stand of picea (area=10000m2,siteIndex=29,age=17,NumberOfTreesPerHa=2500) GymnoInitialParameters i = new GymnoInitialParameters(10000, 29, 20, 2500); i.species = GymnoSpecies.PICEA_ABIES; //initialisation script.init(i); Step step; Intervener intervener; //Simulate an evolution of 50 years step = script.evolve (new GymnoEvolutionParameters (30)); GymnoModel m = ((GymnoModel)script.getModel()); //EconomicScenario es = new EconomicScenario (m.getProject (), m, 0.015, -1d); EconomicScenario es = new EconomicScenario (m.getProject (), m, -0.015, 1000); //clear-cut GymnoRDIThinner gymnoRDIthinner = new GymnoRDIThinner(1d,1d,0.8d,true); gymnoRDIthinner.setPrice(20); // 20 EUR/m3 intervener = gymnoRDIthinner; step = script.runIntervener (intervener,step); //frais de plantation EconomicOperation op=new EconomicOperation("plantation",Type.TREE_NUMBER, Trigger.ON_DATE, false, 0.24); //0.24 EUR/ha List<Integer> listOfDates = new ArrayList(); listOfDates.add(0); op.setValidityDates(listOfDates); es.addUserDefinedOperation(op); //Protection chevreuil op=new EconomicOperation("protection chevreuil",Type.TREE_NUMBER, Trigger.ON_DATE, false, 0.22); //0.24 EUR/ha listOfDates = new ArrayList(); listOfDates.add(0); op.setValidityDates(listOfDates); es.addUserDefinedOperation(op); //Regarnissage op=new EconomicOperation("Regarnissage 300",Type.FIXED, Trigger.ON_DATE, false, 300*1.06); //0.24 EUR/ha listOfDates = new ArrayList(); listOfDates.add(1); op.setValidityDates(listOfDates); es.addUserDefinedOperation(op); //Dégagement op=new EconomicOperation("Degagement",Type.FIXED, Trigger.ON_DATE, false, 400); //0.24 EUR/ha listOfDates = new ArrayList(); listOfDates.add(1); listOfDates.add(3); listOfDates.add(5); op.setValidityDates(listOfDates); es.addUserDefinedOperation(op); //Elagage de pénétration op=new EconomicOperation("Elagage",Type.FIXED, Trigger.ON_DATE, false, 8*190); //0.24 EUR/ha listOfDates = new ArrayList(); listOfDates.add(17); op.setValidityDates(listOfDates); es.addUserDefinedOperation(op); es.evaluate(0,50); // Save to reopen in gui mode (for demonstration) String out = script.getRootDir () + "/tmp/simpleEconomicScript.prj"; Engine.getInstance ().processSaveAsProject (script.getProject(), out); System.out.println ("Wrote project file: " + out); } }
public class Samsa2SimpleEconomicScript { public static void main (String args[]) throws Exception { // load inventory file String inventoryFileName = PathManager.getDir ("data") + "/samsara2/belgium/invSamsara/1IA.inv"; Samsa2InitialParameters initialParameters = new Samsa2InitialParameters(inventoryFileName); // load economic file String filename = PathManager.getDir ("data") + "/samsara2/economics2/economicOperations.txt"; //output economic files Date today = new Date(); SimpleDateFormat formater = new SimpleDateFormat("yyyyMMdd-HHmm"); String date = formater.format (today); String filenameStandTable = PathManager.getDir ("tmp") + "/standTable"+date+".csv"; String filenameEconomicResults = PathManager.getDir ("tmp") + "/economicResults"+date+".csv"; // Create a script with the samsara2 model C4Script script = new C4Script("samsara2"); // initial parameters script.init(initialParameters); Samsa2Model model = (Samsa2Model) script.getModel(); GScene scene; Intervener thinner; double targetGHA = 30; double thinningType = 0; double thinningRandomness = 0.2; int simulationLength = 10; //initialization Step step = script.getRoot(); int initialDate = step.getScene().getDate(); int simulationDate = initialDate; int evolutionStep = 5; //evolution while (simulationDate < initialDate + simulationLength) { scene = step.getScene(); thinner = new GymnoGHAThinner(targetGHA, thinningType, thinningRandomness, false); step = script.runIntervener(thinner, step); // Evolution step = script.evolve(step, new Samsa2EvolutionParameters(simulationDate + evolutionStep)); simulationDate += evolutionStep; } EconomicScenario es = model.getEconomicScenario(); es.getSettings().setFileName(filename); es.evaluate(20,30,20,EconomicScenario.EconomicCase.INFINITY_CYCLE_WITHOUT_LAND_OBSERVATION,step); // Save to reopen in gui mode (for demonstration) String out = script.getRootDir () + "/tmp/simpleSamsaraEconomicScript.prj"; Engine.getInstance ().processSaveAsProject (script.getProject(), out); System.out.println ("Wrote project file: " + out); // save economic results es.storeEconomicResults (filenameEconomicResults); script.closeProject(); // free memory } }
Economics2 est supposé compatible avec tous les modèles de croissance même si la librairie n'est actuellement uniquement connectée qu'avec des modèles de croissance “arbre” et “arbre à effectif”.
* Génériques (Décembre 2017)
* Pour Gymnos et modules compatibles
* Pour Samsara2 et modules compatibles
* Pour Pinuspinaster
La scène du module doit implémenter l’interface EconomicScene. Le code suivant doit être copié-collé/modifié dans la classe XScene du modèle.
private Collection<EconomicTree> harvestedEconomicTrees; // store harvested timber private List<EconomicOperation> economicOperations = new ArrayList<EconomicOperation>(); @Override public List<EconomicTree> getLivingEconomicTrees() { Collection trees = getTrees (); if (trees.size() != 0){ return new ArrayList<EconomicTree>((Collection<Samsa2Tree>) getTrees()); }else{ return new ArrayList<EconomicTree>(); } } @Override public List<EconomicTree> getHarvestedEconomicTrees(){ if(this.harvestedEconomicTrees==null) return new ArrayList<EconomicTree>(); return (List<EconomicTree>) this.harvestedEconomicTrees; } @Override public void setHarvestedEconomicTrees(List<EconomicTree> trees) {this.harvestedEconomicTrees=trees;} @Override public List<EconomicOperation> getEconomicOperations(){return economicOperations;} @Override public void addEconomicOperation(EconomicOperation op){ if(this.economicOperations == null) this.economicOperations = new ArrayList<EconomicOperation>(); this.economicOperations.add(op); } public Object clone() { try { XStand s = (XStand) super.clone(); //TODO s.economicOperations = null; s.harvestedEconomicTrees = null; return s; } catch (Exception e) { //TODO } }
La classe XModel doit implémenter l’interface EconomicModel. Le code suivant doit être copié-collé/modifié dans la classe Xmodel.
private EconomicScenario ecoScenario; @Override public EconomicScenario getEconomicScenario() { return ecoScenario; } @Override public void setEconomicScenario(EconomicScenario es) { this.ecoScenario=es; } @Override public Map<Integer, String> getSpeciesDictionnary(){ // TODO : construct a map with all possible species names and codes (see GymnoModel, QGModel, Samsa2Model for examples) return m; } @Override public Map<Integer, String> getCategoryPriceDictionnary(){ //TODO return a map with category code and names // for example (from walmodel) //Map<Integer, String> categDico = new HashMap<Integer, String>(); //categDico.put(1, "chauffage"); //categDico.put(null, "grume"); //return categDico; // // or for module using only one category of wood product return new HashMap<Integer, String>(); }
La classe arbre d’un module (si elle existe, ce n’est pas le cas pour un modèle peuplement) doit implémenter l’interface EconomicTree. Le code suivant doit être copier-coller/modifier dans cette classe.
@Override @Override public Map<Integer,Double> getEconomicVolume_m3() { //to do return a map with wood volume by categories //for example (fromwaltree): //Map<Integer,Double> volumes = new HashMap<Integer, Double>(); //if(getVolume_grume()>0) volumes.put(null, getVolume_grume()); //if(getSpecies().woodGrumeProportion()<1 & getVolume_woodFire()>0) volumes.put(1, getVolume_woodFire()); //return volumes; // or simply (for modules that do not use different wood categpory) Map<Integer,Double> volumes = new HashMap<Integer, Double>(); volumes.put(null, this.getCommercialVolumeM3()); return volumes; } } @Override public int getSpeciesValue() { return this.getSpecies().getValue(); }
Les intervener dont le modélisateur voudrait qu’il renvoie des informations économiques doivent enregistrer une instance d’une EconomicOperation et enregistrer une liste d’instances de EconomicTrees correspondant aux arbres récoltés dans la scène post-intervention.
Le modélisateur peut personnaliser le type d'opération économique et la façon de calculer le prix de l'opération. Le trigger doit nécessairement être égale à ON_INTERVENTION. Il est également possible d'ajouter plusieurs opérations différentes pour une même opération. Les arbres récoltés doivent être précisé également dans la scènes pour pouvoir totaliser le volume « économique » récolté (dans un souci de compatibilité avec les modèles peuplements).
Exemple du code utilisé à la fin de la méthode apply() de UnevenAgedManager:
if(model instanceof EconomicModel){ //1) construction de la liste des arbres abattus List<EconomicTree> harvestedTrees = new ArrayList<EconomicTree>(); for(SpatializedTree t : treesToRemove){ harvestedTrees.add((EconomicTree) t); } EconomicScene economicScene = (EconomicScene) stand; //2) enregistrement de cette liste economicScene.setHarvestedEconomicTrees(harvestedTrees); //3) création d’une opération économique (Trigger = ON_INTERVENTION !!!) EconomicOperation economicOperation = new EconomicOperation("Uneven-aged thinning", EconomicOperation.Type.DBH_CLASS, EconomicOperation.Trigger.ON_INTERVENTION, true, 0d, harvestedTrees, (EconomicScene) stand); //4) enregistrement de l’opération dans la scène economicScene.addEconomicOperation(economicOperation); }
bénéfice total :
$ Bt = \sum_i^n R_i - D_i $
bénéfice annuel moyen :
$ Bm = \frac{Bt}{n} $
revenu total :
$ Rt = \sum_i^n R_i $
revenu annuel moyen :
$ Rm = \frac{R}{n} $
volume total récolté :
$ Vt = \sum_i^n V_i $
volume annuel moyen récolté :
$ Vm = \frac{Vt}{n} $
valeur marchande :
$ VM = \sum_t VM_t $
bénéfice actualisé simple (net present value, $NPV$) :
$ NPV = \sum_{i}^{n} \frac{R_i - D_i}{(1+r)^i} $
bénéfice actualisé tenant compte de la valeur initiale de la forêt ($NPV'$) :
$ NPV' = - FEV_{initial} + \sum_{i}^{n} \frac{R_i - D_i}{(1+r)^i} - \frac{FEV_{final}}{(1+r)^n} $
Valeur de la forêt actualisée à l'année initiale (critère de Faustman, fonds + superficie, Forest Expectation Value, $FEV_{initial}$). Cette formule suppose une séquence infinie de flux financier. Elle ne peut donc pas être calculée pour le cas de figure numéro 3. Elle doit être adaptée pour le cas de figure 4. Pour les cas de figure 1 et 2 :
$ FEV_{initial} = NPV \frac{(1+r)^n}{(1+r)^n - 1} $
Pour le cas de figure 4 :
$ NPV_1 = sum_{i}^{j} \frac{R_i - D_i}{(1+r)^i} $
$ NPV_2 = sum_{i=j}^{n} \frac{R_i - D_i}{(1+r)^(i-j)} $
$ FEV = NPV_1 + NPV_2 \frac{(1+r)^(k-j)} {(1+r)^(k-j) - 1} / (1+r)^j $
Taux interne de rentabilité (Internal rate of return, IRR) :
C'est le taux $r$ telle que $NPV = 0$.
Taux interne de rentabilité en tenant compte de la valeur initiale de la forêt (Internal rate of return, IRR2) :
C'est le taux $r$ telle que $NPV2 = 0$.
Valeur en bloc à une année $a$ au prix de revient (capitalisation) :
$ VPR_a = FEV_{initial} (1+r)^{a} \sum_{i}^{n} (D_i - R_i) (1+r)^{a-i} $
Valeur en bloc à une année $a$, valeur d'attente (actualisation) :
$ VAT_a = FEV_{final} (1+r)^{a-n} \sum_{i}^{n} ( - D_i + R_i) (1+r)^{a-i} $
Rente selon Hannewinkel et al (2013, https://doi.org/10.1093/forestry/cpt043):
$ a = (\frac{VM_{final}}{(1+r)^n} - VM_{initial} + \sum_{i}^{n} \frac{R_i - D_i}{(1+r)^i}) \frac{r (1+r)^n}{(1+r)^i - 1} $
avec
Ce projet a été financé par l'Accord-Cadre de Recherche et de Vulgarisation Forestière (Service public de Wallonie, Belgique).
Gauthier Ligot
Université de Liège Gembloux Agro-Bio Tech, Gembloux, Belgium