[guia para la programación de modelos en · pdf fileprogramacion de modelos ... fcc...
TRANSCRIPT
JUL 2015Universidad de Valladolid – Instituto Universitario de Gestión Forestal Sostenible
Felipe Bravo OviedoCelia Herrero de Aza Cristóbal Ordóñez
[GUIA PARA LA PROGRAMACIÓN DE MODELOS EN SIMANFOR]Guia de uso de simanfor para modelizadores: detalles necesarios para la introducción exitosa de modelos en la plataforma
Manual del modelizador
Índice
Introducción.............................................................................................................................................3
Proceso de ejecución de modelos........................................................................................................3
Caso de modelos de árbol individual..........................................................................................3
Caso de modelos de masa...........................................................................................................5
Programacion de Modelos.....................................................................................................................6
Programación de modelos de árbol individual...........................................................................9
Ejemplo de programación de un modelo de árbol individual..................................................12
Programación de un modelo de masa......................................................................................17
Ejemplo de programación de un modelo de masa..................................................................18
Detalles técnicos de SiManFor..................................................................................................21
Lenguaje de programación...............................................................................................21
Estructura de los modelos.................................................................................................22
Biblioteca de clases...........................................................................................................22
Navegación.........................................................................................................................23
Seguridad............................................................................................................................23
Programación con C#.NET...............................................................................................23
Ayuda de bibliotecas de clases........................................................................................23
Resumen de características técnicas de los modelos de crecimiento........................24
2
Manual del modelizador
Introducción
SiManFor es una herramienta que permite ejecutar modelos de crecimiento sobre inventariosforestales para distintas especies forestales. Con ellos se pueden ejecutar “escenariosselvícolas” que además de simular crecimiento permiten incluir intervenciones selvícolas,análisis de mortalidad y regeneración, factores de competencia, etc. Los usuarios con permisosde modelizador pueden programar estos modelos, para lo que es necesario conocer losdetalles técnicos de los mismos y la forma en que son cargados y ejecutados por la aplicación.En este manual vamos a detallar los conceptos y tareas necesarios para realizar esta labor.
En SiManFor se pueden programar dos tipos de modelo diferentes: modelos de masa ymodelos de árbol individual. Para cada caso hay que utilizar una plantilla diferente tal y como sedetalla a continuación.
Proceso de ejecución de modelos
La ejecución de los modelos pasa por varias fases en las que se indica a la plataforma quéárboles (o qué variable de parcela, si el modelo es de masa) debe procesar para obtener unnuevo inventario con los resultados. En los diagramas 1 y 2 se puede ver la estructura de cadatipo de modelo y las funciones asociadas a cada fase (parte derecha).
Caso de modelos de árbol individual
A continuación se detalla el significado de las fases por las que pasan los modelos:
1. Inicialización: Permite al modelo calcular las variables no disponibles en el propioinventario pero necesarias para la ejecución del modelo. Se ejecuta una vez por cadapie mayor incluido en el escenario.
2. Supervivencia: Determina si un árbol sobrevive o no al paso del tiempo. Se ejecuta unavez por cada árbol incluido en el escenario.
3. Crecimiento: Modifica las propiedades (variables) del árbol nuevo después de laproyección temporal. Se ejecuta una vez por cada árbol vivo del escenario.
4. Masa Incorporada: Realiza la inclusión de nuevos árboles al escenario al alcanzar untamaño inventariable. Se ejecuta una sola vez.
5. Posprocesado de parcelas: Permite al modelo el cálculo de variables derivadassimilares a las que se calcularon en el apartado de inicialización, tras los cambiosproducidos en variables de árbol y parcela en las fases anteriores.
3
Manual del modelizador
Diagrama 1 - Estructura de un modelo de árbol individual en SiManFor
4
Manual del modelizador
Caso de modelos de masa
El diagrama 2 muestra el flujo de ejecución de un modelo de masa. Hay una primera función enla que se calculan las variables necesarias para la ejecución del modelo (Initialize), otrafunción para hacer que la masa evolucione “year” años (ApplyModel), y finalmente una funciónque permite determinar la forma en la que las variables de masa cambian tras una intervenciónsilvícola (ApplyCutDown).
Diagrama 2 - Estructura de un modelo de masa en SiManFor
5
Manual del modelizador
Programacion de Modelos
Los modelos que se programan en SiManFor siguen el estándar de C#, y es necesario iniciarlos
con una serie de declaraciones que indican las librerías que se van a utilizar:
using System;
using System.Collections.Generic;
using Simanfor.Core.EngineModels;
En el caso de modelos de masa también hay que incluir la siguiente librería:
using Simanfor.Entities.Enumerations;
A continuación debe indicarse el espacio de trabajo y la plantilla, que son inherentes a cada tipo
de modelo y no se pueden modificar por el modelizador. En el caso de modelos de árbol
individual son los siguientes:
namespace EngineTest
{
public class Template : ModelBase
{
/// Todas las funciones y procedimientos del modelo irán entre estas llaves
}
}
Mientras que para un modelo de masa son:
namespace EngineTest
{
public class MassModelTemplate : MassModelBase
{
/// Todas las funciones y procedimientos del modelo irán entre estas llaves
}
}
Las variables que se pueden emplear en un modelo en SiManFor son de dos tipos: las definidas
por el modelizador o las dependientes de la base de datos almacenada en SiManFor. Para el
caso de variables definidas por el modelizador es necesario indicar el “tipo” de variable; por
ejemplo para una variable “numero real” cuyo nombre sea “diametroMinimo” y su valor inicial
sea “0” la instrucción sería:
double diametroMinimo = 0;
Si las variables dependen de los inventarios almacenados en SiManFor no será necesario
declararlas para poder utilizarlas, pero hay que seguir escrupulosamente los
6
Manual del modelizador
convencionalismos asociados a la programación orientada a objetos. Estas variables poseen un
modelo de datos similar al “SiManFor Data Model” (SDM), que es el formato en el que se
“suben” los datos a la plataforma, pero distinto en cuanto a las entidades (variables) con las
que puede trabajar. Esta versión del SDM es bastante más reducida, dado que para la
ejecución de un modelo sólo se utilizan dos tablas (una con información a nivel de árbol y otra a
nivel de parcela) en las que se agrupan varias de las tablas originales.
Para ello hay que fijarse en las propiedades asociadas al objeto (variable) y tener en cuenta de
qué tabla de datos depende: “Parcela” o “PieMayor”. Además, dependiendo de la función con
la que se esté trabajando, la forma de acceder a la base de datos es diferente ya que las
variables que se pueden utilizar deben estar declaradas en el encabezado (esta declaración
depende de la función y no puede ser modificada por el modelizador). Por ejemplo, para una
variable de la base de datos “Parcela” asignada al objeto “plot”, la forma de referirse a las
variables que contiene es la siguiente (ej. de altura dominante):
public override void CalculateInitialInventory(Parcela plot)
/// En la funcién está declarada “plot” como variable de entrada de la tabla “Parcela”
{
plot.SI = plot.H_DOMINANTE.Value/10;
/// asignamos a SI (variable de parcela) el valor de H_DOMINANTE dividido por 10
}
Si queremos utilizar o cambiar variables de árbol en una función como la anterior (que no
declara un objeto de la base de datos “PieMayor”) podemos acceder a ellas con un bucle que
recorre todos los pies mayores que están en el objeto que sí se ha declarado: ”plot”. Esto lo
podemos hacer de la siguiente forma:
public override void CalculateInitialInventory(Parcela plot)
/// En la funcién está declarada “plot” como variable de entrada de la tabla “Parcela”
{
foreach (PieMayor tree in plot.PiesMayores)
{
tree.ALTURA = 27; // asigna el valor 27 a todos los “tree” que estén dentro de “plot”
}
}
Si la función elegida tiene declarada la clase “PieMayor”, la forma de referirse a la variable es
más sencilla:
public override double Survives(double years, Parcela plot, PieMayor tree)
{
double treeSurvival = 1.0F; /// declaramos una variable de usuario
tree.ALTURA = plot.H_DOMINANTE.Value + Math.Normal(1,-5)
7
Manual del modelizador
/// asignamos a la variable de árbol ALTURA el valor de la altura dominante
return treeSurvival;
}
Además se puede acceder a otras propiedades de los objetos (variables) como por ejemplo
“HasValue”, que es de tipo lógico e indica si hay un valor almacenado en la misma. La
instrucción que lo permite es de la forma: tree.ALTURA.HasValue
Los inventarios con los que trabajan los modelos son creados internamente por la aplicación
durante la ejecución del mismo y pueden proceder de dos fuentes distintas:
1. Inventario original, tras aplicar los criterios de filtrado que el usuario haya seleccionado.
2. Inventario resultante de aplicar un modelo o una corta previamente.
En ninguno de los dos casos el modelo de crecimiento trabaja con los datos del SDM, sino que
siempre lo hace con una versión especial de inventarios que se usa para almacenar
temporalmente los datos que se van generando en cada paso.
A continuación se listan todas las variables almacenadas en la base de datos (inventarios de
SiManFor). Los nombres deben respetarse en la programación, siguiendo las indicaciones
precedentes para referirse a ellas. En la tabla 1 se muestran las variables de parcela y las de
árbol en la tabla 2.
Tabla 1. Variables de parcela
ID_INVENTARIO – identificador ESTADILLO – identificador de parcelaID – identificadorA_BASIMETRICA – en m2/haH_DOMINANTE – en mN_PIES – en arboles/parcelaN_PIESHA – en arboles/haEDAD – en añosD_MEDIO – diámetro medio en cmD_CUADRATICO – diámetro cuadrático medio en cmD_DOMINANTE – diámetro dominante en cm
D_MAX – diámetro máximo en cmD_MIN – diámetro mínimo en cmH_MEDIA – altura media en mDM_COPA – diámetro medio de copa en mDG_COPA – diámetro cuadrático medio de copaen mI_REINEKE – índice de densidad de rodalI_HART – índice de HartFCC – fracción de cabida cubierta %SI .- índice de sitio en m
Variables adicionales :VAR_1VAR_2VAR_3VAR_4VAR_5VAR_6VAR_7VAR_8VAR_9VAR_10
Tabla 2. Variables de árbol
ID– identificadorID_INVENTARIO– identificadorESTADILLO– identificador de parcelaID_PARCELA– identificador de parcela internoARBOL – identificador de árbolNUMEROINDIVIDUOS – numero de arboles a losque representa el registro (para clases)ESPECIE – según código IFNDIAMETRO_1– diámetro 1 en cm
INCLINACION – inclinación REC – rectitud del fusteRAM – ramosidad del fusteCONICIDAD – conicidad del fustePUDRICION – nivel de pudriciónCLAS_PIE – clasificación madera en pieCLASE_SOCIOLOGICA – Clase sociológicaVCC – volumen en cm3
VSC– volumen sin corteza en cm3
ALTURA_TOC – altura del tocónANCHO_CM_1 – ancho de copa en mANCHO_CM_2RADIO_C_1 – radio de copa en mRADIO_C_2RADIO_C_3RADIO_C_4LCW – máxima anchura de copa en mC_MORFICO – coeficiente mórfico
8
Manual del modelizador
Tabla 2. Variables de árbol
DIAMETRO_2– diámetro 2 en cmCALIDAD – Calidad FORMA - Forma de cubicaciónALTURA– altura total en mPARAMESP – parámetros especialesOBSERVACIONES – observaciones DAP – valor medio de diámetro 1 y 2CORTEZA_1 – espesor en mmCORTEZA_2 – espesor en mmCORTEZA – espesor medio en mmCIRCUNFERENCIA – en cmEXPAN – factor de expansión a la hectáreaESBELTEZ – relación altura/diámetro (m/cm)SEC_NORMAL – en m2
EDAD130 – edad del árbol tomada a 1,3 m del suelo
IAVC – Incremento Anual de volumen con cortezaVLE – volumen de leñasBAL – área basimétrica de los arboles masgruesos que el individuo en m2/haCR – índice de copa vivaSECCION_COPA_MAXIMA – sección copa máx.EDAD_BASE– edad del árbol en la baseDIAMETRO_MIN - Diámetro mínimo en cmDIAMETRO_4 – Diámetro a 4 metros en cmFCV – fracción de copa vivaALTURA_BC – altura a la base de la copa en mALTURA_MAC– altura al máximo ancho de lacopa en mALTURA_RM – altura a la primera rama viva en mALTURA_VV – altura al primer verticilo vivo en m
C_FORMA - cociente de formaPERC_DURAMEN - %duramen
Variables adicionales no definidasVAR_1VAR_2VAR_3VAR_4VAR_5VAR_6VAR_7VAR_8VAR_9VAR_10
En cada conjunto de datos se han incluido otras 10 variables, denominadas VAR_1, VAR_2, ...
VAR_10. Estas pueden ser empleadas por los modelizadores en caso de que su modelo incluya
variables no definidas en la base de datos. Estas variables no se pueden cambiar de nombre,
pero se puede modificar el output para que el usuario final pueda identificarlas (se detallará
cómo en el manual de uso personalizado de “outputs”).
Asimismo es necesario indicar en el modelo qué valores hay que almacenar en estas variables.
Por ejemplo, si queremos utilizar una variable de biomasa (biomasa del fuste), podemos
calcular su valor en el modelo, y almacenarlo en una de las variables adicionales (ej. VAR_1).
Sabemos que cuando en el output vemos VAR_1, su valor hará referencia a la biomasa del
fuste.
Si queremos facilitar la lectura al usuario final también podremos modificar el output de forma
que se cambie la etiqueta de la columna y en lugar de VAR_1 aparezca “BiomasaFuste”.
Programación de modelos de árbol individual
Para incluir un modelo de árbol individual es aconsejable utilizar el modelo que se puede
descargar de la plataforma. Además se puede descargar un ejemplo de modelo completo que
puede ayudar a la programación de nuevos modelos.
Es importante tener en cuenta que el flujo de ejecución de un modelo de árbol individual es
como indica el diagrama 1. El orden de esta imagen es el que regula el funcionamiento del
escenario, independientemente del orden en el que aparezca en la plantilla del modelo.
A partir de la lectura del inventario hay una función en la que se calculan las variables de
inicialización necesarias para la ejecución del modelo (CalculateInitialInventory). Por
9
Manual del modelizador
ejemplo, se puede calcular la altura a la que la copa es más ancha o a la base de la copa, si se
necesitan para el modelo, ya que son variables que no se miden habitualmente en los
inventarios forestales. También es posible calcular para todos los individuos variables que, en
algunos inventarios se miden únicamente para algunos pies, como la altura total. Para ello se
pueden utilizar propiedades de la variable que indican si tiene almacenado algún valor o no.
/// <summary>
/// Procedimiento que permite la inicialización de variables de parcelas necesarias
/// para la ejecución del modelo. Solo se ejecuta en el primer nodo.
/// Variables que deben permanecer constantes como el índice de sitio deben calcularse
/// solo en este apartado del modelo
/// </summary>
/// <param name="plot"></param>
public override void CalculateInitialInventory(Parcela plot)
{
/// funcion que permite el acceso ordenado a la base de datos
/// (p.ej. para calcular el BAL mas rapidamente)
IList<PieMayor> piesOrdenados = base.Sort(plot.PiesMayores, new
PieMayorSortingCriteria.DescendingByField("DAP"));
foreach (PieMayor tree in plot.PiesMayores)
{
/// se calculan las variables de árbol en este bucle
}
/// se calculan las variables de parcela en este apartado
}
A continuación tenemos las funciones que desarrollan el modelo (Grow, AddTree,
NewTreeDistribution y Survives). En la primera se calcula el crecimiento:
/// <summary>
/// Procedimiento que permite modificar las propiedades del árbol durante su crecimiento después de
"years" años
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="oldTree"></param>
/// <param name="newTree"></param>
public override void Grow(double years, Parcela plot, PieMayor oldTree, PieMayor newTree)
{
/// Ecuaciones que permiten programar como cambian las variable en “years” años
}
Según la declaración del encabezado es una función pública (public), en la que se sobreescribe
el código por defecto (override) y no devuelve ningún valor (void). Todas las modificaciones que
ejecuta la función se hacen sobre el inventario temporal.
10
Manual del modelizador
La masa incorporada se calcula en la siguiente:
/// <summary>
/// Procedimiento que permite añadir nuevos árboles a una parcela después de "years" años
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <returns>Area basimetrica a distribuir o 0 si no hay masa incorporada</returns>
public override double? AddTree(double years, Parcela plot)
{
return 0.0F;
}
Esta función devuelve un valor real entero (double?) que se utilizará en la siguente función, que
calcula la distribución de la masa incorporada, como variable de entrada
(AreaBasimetricaIncorporada):
/// <summary>
/// Expresa como se ha de distribuir la masa incorporada entre los árboles existentes.
/// La implementación por defecto la distribuye de forma uniforme.
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="AreaBasimetricaIncorporada"></param>
/// <returns></returns>
public override Distribution[] NewTreeDistribution(double years, Parcela plot, double
AreaBasimetricaIncorporada)
{
/// Hay que definir una matriz (distribution) que pertenece a la clase Distribution
/// que tiene 3 propiedades: diametro menor (.diametroMenor), diametro mayor (.diametroMayor)
/// y área basimetrica que se añadira al rango diamétrico (.AreaBasimetricaToAdd)
return distribution;
}
Esta función devuelve una matriz (distribution) que usa la plataforma internamente para añadir
árboles resultado del ingrowth. Finalmete tenemos una función para calcular la supervivencia:
/// <summary>
/// Función que indica si el árbol sobrevive o no después de "years" años
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="tree"></param>
/// <returns>Devuelve el porcentaje de árboles que sobreviven</returns>
public override double Survives(double years, Parcela plot, PieMayor tree)
{
/// Ecuaciones que calculan el % de árboles que sobreviven tras “years” años
return survive;
}
11
Manual del modelizador
Esta función devuelve una variable real que utiliza SiManFor para calcular el porcentaje de
árboles que sobrevive. Cuando ese valor es menor de 1, se duplica el registro completo; al
original se le cambia el valor de la variable EXPAN, asignandole el valor EXPAN*survive, y al registro
duplicado EXPAN*(1-survive) y a variable ESTADO el valor “M”.
La parte final de un modelo de árbol individual es el recálculo de variables una vez que el árbol
ha crecido, y teniendo en cuenta los incorporados y la mortalidad (ProcessPlot).
/// <summary>
/// Procedimiento que realiza los cálculos sobre una parcela
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="trees"></param>
public override void ProcessPlot(double years, Parcela plot, PieMayor[] trees)
{
/// Instrucción que permite el acceso ordenado a la base de datos
IList<PieMayor> piesOrdenados = base.Sort(plot.PiesMayores, new
PieMayorSortingCriteria.DescendingByField("DAP"));
foreach(PieMayor tree in piesOrdenados)
{
/// se calculan las variables de árbol en este bucle
}
/// se calculan las variables de parcela en este apartado
}
Ejemplo de programación de un modelo de árbol individual
En este ejemplo se desarrolla el modelo de árbol individual para pino silvestre en Castilla y
León, IBERO Ps 2010.
using System;
using System.Collections.Generic;
using Simanfor.Core.EngineModels;
namespace EngineTest
{
/// <summary>
/// Todas las funciones y procedimientos son opcionales. Si se elimina cualquiera de ellas, se usará un
/// procedimiento o funcion por defecto que no modifica el estado del inventario.
/// Modelo IBERO, 2010, Pinus sylvestris (Castilla y Leon)
/// </summary>
public class Template : ModelBase
{
/// declaracion de variables publicas
/// como es una variable incluida en la base de datos,
/// primero se pone el conjunto al que pertenece y luego el nombre
12
Manual del modelizador
En este modelo, el volumen de cada árbol se calcula por integración, para lo que es necesario
definir la función de perfil al margen de las funciones predeterminadas. La variable a integrar
será el radio al cuadrado. Para este proposito definimos dos funciones, una con corteza
(r2_conCorteza) y otra sin corteza (r2_sinCorteza), que tienen como variable de entrada la altura
relativa (HR) y van a devolver el radio al cuadrado (Math.Pow(r,2)). También es necesario declarar
la tabla que almacena la variable (currentTree) y el conjunto al que pertenece (PieMayor).
public PieMayor currentTree;
/// Funciones de perfil utilizadas en el cálculo de volumenes
public double r2_conCorteza(double HR)
{
double r=(1 + 0.4959 * Math.Exp(-14.2598 * HR)) *0.8474 * currentTree.DAP.Value / 200 * Math.Pow(
(1 - HR), 0.6312 - 0.6361 * (1 - HR));
return Math.Pow(r,2);
}
public double r2_sinCorteza(double HR)
{
double r=(1 + 0.3485 * Math.Exp(-23.9191 * HR)) * 0.7966 * currentTree.DAP.Value / 200 *Math.Pow(
(1 - HR), 0.6094 - 0.7086 * (1 - HR));
return Math.Pow(r,2);
}
La primera función va a modificar el índice de sitio a nivel de parcela, y las variables a nivel de
árbol a través de un bucle (foreach). Además se ha preparado un acceso ordenado a los árboles,
con la instrucción “Ilist”, de mayor a menor diámetro, para que el cálculo del BAL (área
basimétrica de los árboles mayores que el objetivo) sea más rápido. En este bucle que recorre
todos los individuos de la parcela, se utiliza la propiedad .HasValue para calcular los valores que
no están presentes de distintas alturas. También se calcula el volumen con y sin corteza,
integrando las funciones definidas anteriormente con la instrucción “IntegralBySimpson”.
/// <summary>
/// Procedimiento que permite la inicialización de variables de parcelas necesarias para la ejecución
del modelo
/// Solo se ejecuta en el primer nodo.
/// Variables que deben permanecer constantes como el índice de sitio deben calcularse solo en este
apartado del modelo
/// </summary>
/// <param name="plot"></param>
public override void CalculateInitialInventory(Parcela plot)
{
IList<PieMayor> piesOrdenados = base.Sort(plot.PiesMayores, new
PieMayorSortingCriteria.DescendingByField("DAP"));
double bal = 0; double old_sec_normal = 100000; double old_bal=0; //double sec_normal =
tree.SEC_NORMAL;
foreach (PieMayor tree in plot.PiesMayores)
13
Manual del modelizador
{
if (old_sec_normal>tree.SEC_NORMAL) {tree.BAL=bal;old_bal=bal;}
else {tree.BAL=old_bal;}
bal +=tree.SEC_NORMAL.Value*tree.EXPAN.Value/10000;
old_sec_normal = tree.SEC_NORMAL.Value;
if (!tree.ALTURA.HasValue)
{
tree.ALTURA = (13 + (27.0392 + 1.4853 * plot.H_DOMINANTE.Value * 10 - 0.1437 *
plot.D_CUADRATICO.Value * 10) * Math.Exp(-8.0048 / Math.Sqrt(tree.DAP.Value * 10)) ) / 10;
}
if (!tree.ALTURA_MAC.HasValue)
{
tree.ALTURA_MAC = tree.ALTURA.Value / (1 + Math.Exp((double)(-0.0012*tree.ALTURA.Value*10-
0.0102*tree.BAL.Value-0.0168*plot.A_BASIMETRICA.Value )));
}
if (!tree.ALTURA_BC.HasValue)
{
tree.ALTURA_BC = tree.ALTURA_MAC.Value / (1+Math.Exp((double)
(1.2425*(plot.A_BASIMETRICA.Value/(tree.ALTURA.Value*10)) + 0.0047*(plot.A_BASIMETRICA.Value) -
0.5725*Math.Log(plot.A_BASIMETRICA.Value)-0.0082*tree.BAL.Value)));
}
tree.CR=1-tree.ALTURA_BC.Value/tree.ALTURA.Value;
if (!tree.LCW.HasValue)
{
tree.LCW=(1/10.0F)*(0.2518*tree.DAP.Value*10)*Math.Pow(tree.CR.Value,
(0.2386+0.0046*(tree.ALTURA.Value-tree.ALTURA_BC.Value)*10));
}
tree.VAR_1 = tree.COORD_X;//añadido para tener coordenadas
tree.VAR_2 = tree.COORD_Y;//añadido para tener coordenadas
currentTree = tree;
tree.VCC=Math.PI*tree.ALTURA.Value*IntegralBySimpson(0,1,0.01,r2_conCorteza); //Integración
--> r2_conCorteza sobre HR en los limites 0 -> 1
tree.VSC=Math.PI*tree.ALTURA.Value*IntegralBySimpson(0,1,0.01,r2_sinCorteza); //Integración
--> r2_sinCorteza sobre HR en los limites 0 -> 1
currentTree=null;
}
plot.SI = (plot.H_DOMINANTE.Value * 0.8534446)/Math.Pow((1- Math.Exp((double) (-0.270 *
plot.EDAD.Value/10))),2.2779);
}
Función que calcula el tanto por uno de supervivencia.
/// <summary>
/// Función que indica si el árbol sobrevive o no después de "years" años
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="tree"></param>
/// <returns>Devuelve el porcentaje de árboles que sobreviven</returns>
public override double Survives(double years, Parcela plot, PieMayor tree)
{
double cvDAP = Math.Sqrt(Math.Pow(plot.D_CUADRATICO.Value,2) - Math.Pow(plot.D_MEDIO.Value,2)) /
plot.D_MEDIO.Value;
14
Manual del modelizador
return (1 / (1 + Math.Exp(-6.8548 + (9.792 / tree.DAP.Value) + 0.121 * tree.BAL.Value * cvDAP +
0.037 * plot.SI.Value)));
}
Función que calcula el crecimiento en diámetro y en altura:
/// <summary>
/// Procedimiento que permite modificar las propiedades del árbol durante su crecimiento después de
"years" años
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="oldTree"></param>
/// <param name="newTree"></param>
public override void Grow(double years, Parcela plot, PieMayor oldTree, PieMayor newTree)
{
double DBHG5 = Math.Exp(-0.37110 + 0.2525*Math.Log(oldTree.DAP.Value*10) + 0.7090 *
Math.Log((oldTree.CR.Value + 0.2) / 1.2) + 0.9087 * Math.Log(plot.SI.Value) - 0.1545 *
Math.Sqrt(plot.A_BASIMETRICA.Value) - 0.0004 * (oldTree.BAL.Value * oldTree.BAL.Value /
Math.Log(oldTree.DAP.Value*10)));
newTree.DAP+=DBHG5/10;
double HTG5 = Math.Exp(3.1222-0.4939*Math.Log(oldTree.DAP.Value*10)+1.3763*Math.Log(plot.SI.Value)-
0.0061*oldTree.BAL.Value+0.1876*Math.Log(oldTree.CR.Value));
newTree.ALTURA+=HTG5/100;
}
Función que calcula el área basimétrica que se incorpora a la masa. Por defecto se distribuirá
de forma uniforme en todas las clases diamétricas, pero es posible indicar una distribución
particular adecuada para el modelo con la función siguiente.
/// <summary>
/// Procedimiento que permite añadir nuevos árboles a una parcela después de "years" años
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <returns>Area basimetrica a distribuir o 0 si no hay masa incorporada</returns>
public override double? AddTree(double years, Parcela plot)
{
double result = 1 / (1 + Math.Exp(8.2739 - 0.3022 * plot.D_CUADRATICO.Value));
if (result >= 0.43F)
{
double BA_Added=5.7855 - 0.1703 * plot.D_CUADRATICO.Value;
if (BA_Added<0)
{
return 0.0F;
}
return BA_Added;
}
return 0.0F;
}
15
Manual del modelizador
Función que distribuye la masa incorporada (en área basimétrica) que se ha calculado en la
función anterior (si no se incluye esta función la distribución será uniforme a lo largo de toda la
distribución diamétrica).
/// <summary>
/// Expresa como se ha de distribuir la masa incorporada entre los árboles existentes.
/// La implementación por defecto la distribuye de forma uniforme.
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="AreaBasimetricaIncorporada"></param>
/// <returns></returns>
public override Distribution[] NewTreeDistribution(double years, Parcela plot, double
AreaBasimetricaIncorporada)
{
Distribution[] distribution = new Distribution[3];
double percentAreaBasimetrica = AreaBasimetricaIncorporada / plot.A_BASIMETRICA.Value;
distribution[0] = new Distribution();
distribution[0].diametroMenor = 0.0;
distribution[0].diametroMayor = 12.5;
distribution[0].AreaBasimetricaToAdd = 0.0384 * AreaBasimetricaIncorporada;
distribution[1] = new Distribution();
distribution[1].diametroMenor = 12.5;
distribution[1].diametroMayor = 22.5;
distribution[1].AreaBasimetricaToAdd = 0.2718 * AreaBasimetricaIncorporada;
distribution[2] = new Distribution();
distribution[2].diametroMenor = 22.5;
distribution[2].diametroMayor = double.MaxValue;
distribution[2].AreaBasimetricaToAdd = 0.6898 * AreaBasimetricaIncorporada;
return distribution;
}
Función que realiza calculos de variables para las que no hay ecuaciones de crecimiento, como
la altura a la base de la copa o los volumenes. Es una función complementaria a
CalculateInitialInventory ya que se realizan cálculos de variables similares, pero después de
haber crecido y teniendo en cuenta la masa incorporada y la supervivencia.
/// <summary>
/// Procedimiento que realiza los cálculos sobre una parcela.
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="trees"></param>
public override void ProcessPlot(double years, Parcela plot, PieMayor[] trees)
{
IList<PieMayor> piesOrdenados = base.Sort(plot.PiesMayores, new
PieMayorSortingCriteria.DescendingByField("DAP"));
double bal = 0; double old_sec_normal = 100000; double old_bal=0;
foreach(PieMayor tree in piesOrdenados)
16
Manual del modelizador
{
if (!tree.ESTADO.HasValue || String.IsNullOrEmpty(tree.ESTADO.ToString()))
{
if (old_sec_normal>tree.SEC_NORMAL) {tree.BAL=bal;old_bal=bal;}
else {tree.BAL=old_bal;}
bal +=tree.SEC_NORMAL.Value*tree.EXPAN.Value/10000;
old_sec_normal = tree.SEC_NORMAL.Value;
tree.ALTURA = (13 + (27.0392 + 1.4853 * plot.H_DOMINANTE.Value * 10 - 0.1437 *
plot.D_CUADRATICO.Value * 10) * Math.Exp(-8.0048 / Math.Sqrt(tree.DAP.Value * 10))) / 10;
tree.ALTURA_MAC = tree.ALTURA.Value / (1 + Math.Exp((double)(-0.0012 * tree.ALTURA.Value * 10 -
0.0102 * tree.BAL.Value - 0.0168 * plot.A_BASIMETRICA.Value)));
tree.ALTURA_BC = tree.ALTURA_MAC.Value / (1 + Math.Exp((double)(1.2425 *
(plot.A_BASIMETRICA.Value / (tree.ALTURA.Value * 10)) + 0.0047 * (plot.A_BASIMETRICA.Value) - 0.5725 *
Math.Log(plot.A_BASIMETRICA.Value) - 0.0082 * tree.BAL.Value tree.CR = 1 - tree.ALTURA_BC.Value
/ tree.ALTURA.Value;
tree.LCW = (1 / 10.0F) * (0.2518 * tree.DAP.Value * 10) * Math.Pow(tree.CR.Value, (0.2386 +
0.0046 * (tree.ALTURA.Value - tree.ALTURA_BC.Value) * 10));
currentTree = tree;
tree.VCC = Math.PI * tree.ALTURA.Value * IntegralBySimpson(0, 1, 0.01, r2_conCorteza);
//Integración --> r2_conCorteza sobre HR en los limites 0 -> 1
tree.VSC = Math.PI * tree.ALTURA.Value * IntegralBySimpson(0, 1, 0.01, r2_sinCorteza);
//Integración --> r2_sinCorteza sobre HR en los limites 0 -> 1
currentTree = null;
}
else{tree.BAL=0; tree.CR=0; tree.LCW = 0; tree.ALTURA_MAC = 0; tree.ALTURA_BC = 0;}
}
}
}
}
Programación de un modelo de masa
La programación de modelos de masa es más sencilla, ya que como hemos visto solo consta
de tres funciones, pero también es aconsejable utilizar el modelo alojado en la ayuda de la
plataforma. También se dispone de un ejemplo de modelo completo que puede orientar en la
programación de nuevos modelos.
Hay una primera función en la que se calculan las variables necesarias para la ejecución del
modelo (Initialize),
/// <summary>
/// Función que
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="tree"></param>
public override void Initialize(Parcela plot)
{
/// Ecuaciones para definir las variables de masa necesarias para la
17
Manual del modelizador
/// ejecucion del modelo.también se definen las variables en el
/// estado inicial que no puedan deducirse del inventario
}
otra función para hacer que la masa evolucione “years” años (ApplyModel),
/// <summary>
/// Función que
/// </summary>
/// <param name="oldPlot"></param>
/// <param name="newPlot"></param>
/// years: numero de años que esta creciendo la masa
public override void ApplyModel(Parcela oldPlot, Parcela newPlot, int years)
{
/// Ecuaciones que explican como varian las variables de masa en "years" años
}
y finalmente una función que permite determinar la forma en la que cambian las variables de
masa tras una intervención silvícola (ApplyCutDown).
/// <summary>
/// Función que
/// </summary>
/// <param name="oldPlot"></param>
/// <param name="newPlot"></param>
/// cutDownType values: ( PercentOfTrees, Volume, Area )
/// trimType values: ( ByTallest, BySmallest, Systematic )
/// value: (% de corta)
public override void ApplyCutDown(Parcela oldPlot, Parcela newPlot, CutDownType cutDownType, TrimType
trimType, float value)
{
/// Ecuaciones que explican como varian las variables de masa al cortar un
/// "value" % de masa. Se puede indicar como varía para distintos variables:
/// corta por % de pies, de area basimetrica o de volumen.
/// tambien se puede definir la variación según sean cortas por lo bajo,
/// por lo alto o sistematicas
}
Ejemplo de programación de un modelo de masa
using System;
using System.Collections.Generic;
using Simanfor.Core.EngineModels;
using Simanfor.Entities.Enumerations;
namespace EngineTest
{
public class MassModelTemplate : MassModelBase
{
/// -------------------------------------------------------------------------------------
/// Modelo para masas de pino silvestre basado en los artículos:
18
Manual del modelizador
/// * del Río Gaztelurrutia, M; Montero, G.; "Modelo de simulación de claras en masas de
/// Pinus sylvestris L." monografias inia: Forestal n. 3
/// -------------------------------------------------------------------------------------
/// <summary>
/// Función que calcula variables necesarias para la ejecución del modelo
/// </summary>
/// <param name="years"></param>
/// <param name="plot"></param>
/// <param name="tree"></param>
public override void Initialize(Parcela plot)
{
double parA, parB, parC, IC;
parA = 0.8534446;
parB = -0.27;
parC = 0.439;
IC = parA * plot.H_DOMINANTE.Value/10 / Math.Pow( 1- Math.Exp((double) parB *
plot.EDAD.Value/10) , 1/parC);
plot.SI = 10 * IC ;
plot.VAR_1 = IC;
double parB0, parB1,parB2, parB3;
parB0 = 1.42706;
parB1 = 0.388317;
parB2 = -30.691629;
parB3 = 1.034549;
plot.VCC = Math.Exp((double) parB0 + parB1 * IC + parB2 / plot.EDAD.Value + parB3 *
Math.Log(plot.A_BASIMETRICA.Value) );
}
/// <summary>
/// Función que hace evolucionar la masa “year” años
/// </summary>
/// <param name="oldPlot"></param>
/// <param name="newPlot"></param>
/// years: numero de años que esta creciendo la masa
public override void ApplyModel(Parcela oldPlot, Parcela newPlot, int years)
{
newPlot.SI = oldPlot.SI.Value;
newPlot.EDAD = oldPlot.EDAD.Value + years;
newPlot.N_PIESHA= oldPlot.N_PIESHA.Value;
newPlot.VAR_1 = oldPlot.VAR_1.Value;
// H_DOMINANTE:
double IC = oldPlot.SI.Value/10;
double parA17, parB17, parC17, parA29, parB29, parC29;
parA17 = 1.9962;
parB17 = 0.2642;
parC17 = 0.46;
parA29 = 3.1827;
parB29 = 0.3431;
parC29 = 0.3536;
double H0_17 = 10 * parA17 * Math.Pow( 1 - Math.Exp((double) -1 * parB17 * newPlot.EDAD.Value /
10 ) , 1/parC17);
19
Manual del modelizador
double H0_29 = 10 * parA29 * Math.Pow( 1 - Math.Exp((double) -1 * parB29 * newPlot.EDAD.Value /
10 ) , 1/parC29);
newPlot.VAR_2 = H0_17;
newPlot.VAR_3 = H0_29;
newPlot.H_DOMINANTE = H0_17 + (H0_29 - H0_17) * (IC - 1.7) / 1.2;
// AREA_BASIMETRICA:
double parA0, parA1, parB0, parB1, parA2, parB2, parB3;
parA0 = 5.103222;
parB0 = 1.42706;
parB1 = 0.388317;
parB2 = -30.691629;
parB3 = 1.034549;
newPlot.A_BASIMETRICA = Math.Pow( oldPlot.A_BASIMETRICA.Value, oldPlot.EDAD.Value /
newPlot.EDAD.Value) * Math.Exp(parA0 * (1-oldPlot.EDAD.Value / newPlot.EDAD.Value));
// VOLUMEN
newPlot.VCC = Math.Exp((double) parB0 + parB1 * IC + parB2 / newPlot.EDAD.Value + parB3 *
( Math.Log(oldPlot.A_BASIMETRICA.Value)*oldPlot.EDAD.Value/newPlot.EDAD.Value + parA0*(1-
oldPlot.EDAD.Value/newPlot.EDAD.Value) ) );
//Altura media
parA0 = -1.155649;
parA1 = 0.976772;
newPlot.H_MEDIA = parA0 + parA1 * newPlot.H_DOMINANTE;
//MORTALIDAD NATURAL
parA0 = -2.349350607;
parA1 = 0.000000099;
parA2 = 4.873897659;
newPlot.N_PIESHA = Math.Pow( Math.Pow(oldPlot.N_PIESHA.Value,parA0) + parA1 *
( Math.Pow( newPlot.EDAD.Value/100 , parA2 ) - Math.Pow( oldPlot.EDAD.Value/100 , parA2 ) ), 1 /
parA0);
// D_CUADRATICO:
double SEC_NORMAL = newPlot.A_BASIMETRICA.Value * 10000 / newPlot.N_PIESHA.Value ;
newPlot.D_CUADRATICO = 2*Math.Sqrt(SEC_NORMAL/Math.PI) ;
}
/// <summary>
/// Función que explica el cambio en las variables tras aplicar una clara
/// </summary>
/// <param name="oldPlot"></param>
/// <param name="newPlot"></param>
/// cutDownType values: ( PercentOfTrees, Volume, Area )
/// trimType values: ( ByTallest, BySmallest, Systematic )
/// value: (% de corta)
public override void ApplyCutDown(Parcela oldPlot, Parcela newPlot, CutDownType cutDownType, TrimType
trimType, float value)
{
newPlot.VAR_9 = value;
double parA17, parB17, parC17, parA29, parB29, parC29;
parA17 = 1.9962;
parB17 = 0.2642;
parC17 = 0.46;
parA29 = 3.1827;
parB29 = 0.3431;
20
Manual del modelizador
parC29 = 0.3536;
double H0_17 = 10 * parA17 * Math.Pow( 1 - Math.Exp((double) -1 * parB17 * newPlot.EDAD.Value /
10 ) , 1/parC17);
double H0_29 = 10 * parA29 * Math.Pow( 1 - Math.Exp((double) -1 * parB29 * newPlot.EDAD.Value /
10 ) , 1/parC29);
double parA0, parB0, parB1, parB2, parB3;
parA0 = 5.103222;
parB0 = 1.42706;
parB1 = 0.388317;
parB2 = -30.691629;
parB3 = 1.034549;
double IC = oldPlot.SI.Value/10;
double parC0,parC1,parC2,SEC_NORMAL,tpuN,tpuBA;
switch (cutDownType)
{
case CutDownType.PercentOfTrees:
parC0 = 0.531019;
parC1 = 0.989792;
parC2 = 0.517850;
tpuN = value/100;
newPlot.N_PIESHA = oldPlot.N_PIESHA.Value - tpuN*oldPlot.N_PIESHA.Value;
newPlot.D_CUADRATICO = parC0 + parC1*oldPlot.D_CUADRATICO +
parC2*oldPlot.D_CUADRATICO.Value*Math.Pow(1-tpuN,2);
SEC_NORMAL = Math.PI * Math.Pow(newPlot.D_CUADRATICO.Value/2,2);
newPlot.A_BASIMETRICA = SEC_NORMAL * newPlot.N_PIESHA.Value / 10000;
newPlot.VCC = Math.Exp((double) parB0 + parB1 * IC + parB2 / newPlot.EDAD.Value + parB3 *
Math.Log(newPlot.A_BASIMETRICA.Value) );
break;
case CutDownType.Volume:
break;
case CutDownType.Area:
parC0 = 0.144915;
parC1 = 0.969819;
parC2 = 0.678010;
tpuBA = value/100;
newPlot.A_BASIMETRICA = oldPlot.A_BASIMETRICA.Value - tpuBA*oldPlot.A_BASIMETRICA.Value;
newPlot.D_CUADRATICO = Math.Pow(parC0 + parC1*Math.Pow(oldPlot.D_CUADRATICO.Value,0.5) +
parC2*(1-tpuBA) ,2 );
SEC_NORMAL = Math.PI * Math.Pow(newPlot.D_CUADRATICO.Value/2,2);
newPlot.N_PIESHA = newPlot.A_BASIMETRICA.Value * 10000 / SEC_NORMAL;
newPlot.VCC = Math.Exp((double) parB0 + parB1 * IC + parB2 / newPlot.EDAD.Value + parB3 *
Math.Log(newPlot.A_BASIMETRICA.Value) );
break;
}
}
}
}
21
Manual del modelizador
Detalles técnicos de SiManFor
Lenguaje de programación
El lenguaje de programación de la plataforma es VisualBasic.NET, aunque esto no afecta a los
usuarios modelizadores, ya que el lenguaje de programación de los modelos es C#. Este
lenguaje es muy simple y permite un aprendizaje rápido en caso de que no se haya usado
antes.
Al final de este documento existe una relación de enlaces sobre la iniciación a la programación
de C# y los detalles técnicos del propio lenguaje.
Estructura de los modelos
Los modelos de crecimiento no son más que clases (estructuras) que agrupan un conjunto de
funciones y procedimientos, que son las que contienen los algoritmos para cada fase. Existe
una función o procedimiento que contiene las ecuaciones o algoritmos que se deben aplicar en
cada una de las fases indicadas anteriormente.
Es importante saber que el motor de SiManFor es el que se encarga de ejecutar cada una de
ellas en cada momento, y que su posición en el código del modelo no tiene relación alguna. Es
decir, aunque se recomienda un orden concreto de aparición y definición de cada función o
procedimiento, no es estrictamente necesario mantenerlo.
En la siguiente sección se puede ver el código de un modelo simple, totalmente operativo y con
comentarios en el código para que se comprendan los detalles técnicos del mismo.
Biblioteca de clases
Asociado al lenguaje de programación, existe un conjunto de clases que se pueden usar para
definir el tipo de las variables que se usen: números enteros o reales, cadenas de texto,
vectores y matrices, etc.
Además, existe una clase denominada Math que agrupa todas las funciones matemáticas y
constantes numéricas comunes: número pi y e, funciones de logaritmos, medias aritméticas,
trigonométricas, etc.
Al final de este documento existe una relación de enlaces desde los que se pueden consultar
22
Manual del modelizador
los detalles de las bibliotecas de clases y las funciones que contiene la clase Math. En estos
enlaces también se pueden consultar ejemplos prácticos para entender el funcionamiento y uso
de los mismos.
Navegación
Las variables usadas en los modelos permiten desplazarse a las distintas entidades del
inventario. Por ejemplo, dado un pie mayor, se puede obtener la parcela o el inventario al que
pertenece a través de sus propiedades. También se puede recorrer todos los árboles que
contiene una parcela a partir de la misma.
Al final de este documento encontrará el diagrama de entidades en el que se indican las
propiedades de cada entidad del inventario (el inventario propiamente, las parcelas y los pies
mayores), así como qué propiedades permiten navegar hasta otras entidades.
Seguridad
Los modelos se ejecutan en el servidor bajo una plataforma de seguridad que no permite el
acceso a áreas sensibles del sistema como el sistema de archivos o el Registro de
Configuración del sistema. Aunque la biblioteca estándar de clases que se usa en los modelos
permite usar clases que accedan a estas áreas sensibles, dicha plataforma de seguridad
bloquea los accesos, por lo que se garantiza la seguridad del servidor.
Es recomendable que los modelizadores eviten el uso de aquellas clases que pretendan
acceder o modificar partes del sistema, y se restrinjan a usar los tipos básicos de la biblioteca
de clases y sus operaciones sobre las mismas.
Programación con C#.NET
El siguiente enlace muestra la sección de Introducción a C#.NET, de MSDN Library. Esta
sección incluye los principios del lenguaje, su sintaxis y ejemplos de cómo programar de forma
general en este lenguaje.
Introducción a C#.NET (.NET Framework) MSDN Library (Español)
https://msdn.microsoft.com/es-es/library/kx37x362.aspx
También puede resultar de interés este manual de C#
23
Manual del modelizador
Ayuda de bibliotecas de clases
Los siguientes enlaces muestran las secciones de documentación de la biblioteca estándar de
clases y la documentación de Math (también accesible desde el primer enlace),
respectivamente. Las clases indicadas en dicha documentación pueden usarse en los modelos,
aunque se deben tener en cuenta las limitaciones de seguridad impuestas por el motor de
ejecución de modelos.
Biblioteca estándar de clases de .NET Framework MSDN Library (Español)
http://msdn.microsoft.com/es-es/library/ms229335.aspx
Referencia de la clase Math MSDN Library (Español) http://msdn.microsoft.com/es-
es/library/system.math.aspx
Resumen de características técnicas de los modelos de crecimiento
Lenguaje de programación: C#.NET 2008
Versión de .NET Framework: Microsoft .NET Framework 3.5 SP1
Zona de seguridad de ejecución: Zona de Internet (privilegios mínimos)
Ensamblados incluidos: mscorlib
Simanfor.Core.EngineModels
Espacios de nombres por defecto: System
System.Collections.Generics
Simanfor.Core.EngineModels
Tipos base usados por defecto: System.Double
System.Object
System.String
System.Collections.Generics.Idictionary<>
System.Collections.Generics.Ilist<>
Tipos propios usados por defecto: Simanfor.Core.EngineModels.PieMayor
Simanfor.Core.EngineModels.Parcelas
24