Manual del modelizador
Transcripción
Manual del modelizador
JUL 2015 Universidad de Valladolid – Instituto Universitario de Gestión Forestal Sostenible Felipe Bravo Oviedo Celia 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 inventarios forestales para distintas especies forestales. Con ellos se pueden ejecutar “escenarios selví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 permisos de modelizador pueden programar estos modelos, para lo que es necesario conocer los detalles 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 y modelos de árbol individual. Para cada caso hay que utilizar una plantilla diferente tal y como se detalla 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 un nuevo inventario con los resultados. En los diagramas 1 y 2 se puede ver la estructura de cada tipo 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 propio inventario pero necesarias para la ejecución del modelo. Se ejecuta una vez por cada pie mayor incluido en el escenario. 2. Supervivencia: Determina si un árbol sobrevive o no al paso del tiempo. Se ejecuta una vez por cada árbol incluido en el escenario. 3. Crecimiento: Modifica las propiedades (variables) del árbol nuevo después de la proyecció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 un tamaño inventariable. Se ejecuta una sola vez. 5. Posprocesado de parcelas: Permite al modelo el cálculo de variables derivadas similares a las que se calcularon en el apartado de inicialización, tras los cambios producidos 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 en la que se calculan las variables necesarias para la ejecución del modelo ( Initialize), otra función para hacer que la masa evolucione “year” años ( ApplyModel), y finalmente una función que permite determinar la forma en la que las variables de masa cambian tras una intervención silví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 parcela ID – identificador A_BASIMETRICA – en m2/ha H_DOMINANTE – en m N_PIES – en arboles/parcela N_PIESHA – en arboles/ha EDAD – en años D_MEDIO – diámetro medio en cm D_CUADRATICO – diámetro cuadrático medio en cm D_DOMINANTE – diámetro dominante en cm D_MAX – diámetro máximo en cm D_MIN – diámetro mínimo en cm H_MEDIA – altura media en m DM_COPA – diámetro medio de copa en m DG_COPA – diámetro cuadrático medio de copa en m I_REINEKE – índice de densidad de rodal I_HART – índice de Hart FCC – fracción de cabida cubierta % SI .- índice de sitio en m Variables adicionales : VAR_1 VAR_2 VAR_3 VAR_4 VAR_5 VAR_6 VAR_7 VAR_8 VAR_9 VAR_10 Tabla 2. Variables de árbol ID– identificador ID_INVENTARIO– identificador ESTADILLO– identificador de parcela ID_PARCELA– identificador de parcela interno ARBOL – identificador de árbol NUMEROINDIVIDUOS – numero de arboles a los que representa el registro (para clases) ESPECIE – según código IFN DIAMETRO_1– diámetro 1 en cm INCLINACION – inclinación REC – rectitud del fuste RAM – ramosidad del fuste CONICIDAD – conicidad del fuste PUDRICION – nivel de pudrición CLAS_PIE – clasificación madera en pie CLASE_SOCIOLOGICA – Clase sociológica VCC – volumen en cm3 VSC– volumen sin corteza en cm3 ALTURA_TOC – altura del tocón ANCHO_CM_1 – ancho de copa en m ANCHO_CM_2 RADIO_C_1 – radio de copa en m RADIO_C_2 RADIO_C_3 RADIO_C_4 LCW – máxima anchura de copa en m C_MORFICO – coeficiente mórfico 8 Manual del modelizador Tabla 2. Variables de árbol DIAMETRO_2– diámetro 2 en cm CALIDAD – Calidad FORMA - Forma de cubicación ALTURA– altura total en m PARAMESP – parámetros especiales OBSERVACIONES – observaciones DAP – valor medio de diámetro 1 y 2 CORTEZA_1 – espesor en mm CORTEZA_2 – espesor en mm CORTEZA – espesor medio en mm CIRCUNFERENCIA – en cm EXPAN – factor de expansión a la hectárea ESBELTEZ – 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 corteza VLE – volumen de leñas BAL – área basimétrica de los arboles mas gruesos que el individuo en m2/ha CR – índice de copa viva SECCION_COPA_MAXIMA – sección copa máx. EDAD_BASE– edad del árbol en la base DIAMETRO_MIN - Diámetro mínimo en cm DIAMETRO_4 – Diámetro a 4 metros en cm FCV – fracción de copa viva ALTURA_BC – altura a la base de la copa en m ALTURA_MAC– altura al máximo ancho de la copa en m ALTURA_RM – altura a la primera rama viva en m ALTURA_VV – altura al primer verticilo vivo en m C_FORMA - cociente de forma PERC_DURAMEN - %duramen Variables adicionales no definidas VAR_1 VAR_2 VAR_3 VAR_4 VAR_5 VAR_6 VAR_7 VAR_8 VAR_9 VAR_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, NewTreeDistribution y Survives). AddTree, 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*100.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 IC = 0.439; = 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*(1oldPlot.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 newPlot.A_BASIMETRICA = Math.PI * Math.Pow(newPlot.D_CUADRATICO.Value/2,2); = 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 newPlot.N_PIESHA = Math.PI * Math.Pow(newPlot.D_CUADRATICO.Value/2,2); = 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/eses/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