Tutorial Model Developers

From ILMS-Wiki
(Difference between revisions)
Jump to: navigation, search
(Debugging und Profiling von Modellen in JAMS)
(Debugging)
Line 509: Line 509:
 
In order to reconstruct the functioning of existing (JAMS/J2000) components and those which have been developed or to facilitate error search in the process of development,  you have the debugging function is at your disposal. You can select positions in the source code by using breakpoints where the compiler is supposed to stop the model run. Hence it is possible to view the values of the designated variables at this time. Moreover, the functioning of existing or developed component can be reconstructed line by line.  
 
In order to reconstruct the functioning of existing (JAMS/J2000) components and those which have been developed or to facilitate error search in the process of development,  you have the debugging function is at your disposal. You can select positions in the source code by using breakpoints where the compiler is supposed to stop the model run. Hence it is possible to view the values of the designated variables at this time. Moreover, the functioning of existing or developed component can be reconstructed line by line.  
 
Before starting the debug mode of the model, you have to set breakpoints at the positions which are interesting for you. Therefore, click on the red point in the menu bar and afterwards on the position in the source code. In the following image, two breakpoints were set:  
 
Before starting the debug mode of the model, you have to set breakpoints at the positions which are interesting for you. Therefore, click on the red point in the menu bar and afterwards on the position in the source code. In the following image, two breakpoints were set:  
 +
 
[[Bild:Haltepunkte_setzen.png]]  
 
[[Bild:Haltepunkte_setzen.png]]  
  
Verfügt Ihre Komponente über eine Main-Procedure, können sie diese direkt debuggen. Ist Ihre Komponente Bestandteil eines komplexen Modells, so muss das komplette Modell debuggt werden. Klicken Sie dazu mit der rechten Maustaste auf das zu startende Projekt (das Projekt indem die Komponente enthalten ist) und wälhen Sie '''debug'''.
+
If your component has a main procedure, you can start the debugging directly. If your component is part of a complex model, you have to debug the entire model. In this case, right-click on the project which is supposed to start (the project which contains the component) and select '''debug'''.
  
 
[[Bild:Debug_aufrufen.png]]  
 
[[Bild:Debug_aufrufen.png]]  

Revision as of 10:07, 13 June 2011

Contents

JAMS Komponenten-API

JAMS Datentypen

Die folgenden JAMS-Datentyper stehen zur Verfügung

  • Attribute.Boolean
  • Attribute.BooleanArray
  • Attribute.Calendar
  • Attribute.DirName
  • Attribute.Document
  • Attribute.Double
  • Attribute.DoubleArray
  • Attribute.Entity
  • Attribute.EntityCollection
  • Attribute.FileName
  • Attribute.Float
  • Attribute.FloatArray
  • Attribute.Geometry
  • Attribute.Interger
  • Attribute.IntegerArray
  • Attribute.Long
  • Attribute.LongArray
  • Attribute.Object
  • Attribute.String
  • Attribute.StringArray
  • Attribute.TimeIntervall

JAMS Komponenten

The main building block of any JAMS model is named component. A component is a JAVA class that implements three different methods (i.e. init, run and cleanup) that are triggered at according runtime stages that a JAMS model iterates through. As indicated by their name, these methods are invoked at the beginning, during and at the end of a component's lifetime. While the init and clean methods are executed only once, the run method can be invoked several times, depending on the component's purpose and configuration.

Communication with the framework and other components is handled by arbitrary public attributes that fulfil two conditions: (i) they are of a valid JAMS data type and (ii) they are marked by special JAVA annotations, i.e. syntactic meta-information. These attribute annotations are used to define their I/O type (read, write), their default values, physical unit and boundaries (if numeric), and a short text describing the attribute's purpose. This information is used both by the runtime system to setup and interlink attributes and by GUIs that provide support during model design.

JAMS Kontextkomponenten

A JAMS context is a special, compound-type component that can nest other components and contexts, named children. A context serves two main purposes: (i) it controls the execution of its children, and (ii) serves as a data store for its children, allowing for flexible data exchange between them. Depending on the purpose of a given context, it might invoke its children multiple times over a number of iterations (Figure left), only once if some predefined condition is satisfied (Figure center) or only once in a sequence (Figure right). As contexts are specialized components, they can be nested in other contexts, allowing the creation of complex component hierarchies and execution control structures. The following figure shows context examples.

Bild:Context_examples.png

Input/Output von JAMS-Daten

Data I/O in JAMS can be handled in two different ways. As a first option, a component can be used to read or write data from any data source, e.g. files or databases. In this case, the component developer has to take care of requesting all necessary information via the component's input data (e.g. file names), accessing the data source, and provide potential results (e.g. data read from a file) as output data. An advantage of this approach is a high flexibility in accessing external information. On the others side, such components usually need to be adapted to certain models and data formats which limits their reusability.

The second option to handle data exchange in JAMS is the usage of the DataStore software interface. This interface can be used to read/write data from/to an external data repository, which might be a file, a database system or a network resource. It provides functions to read and write datasets, each represented by an ordered list of data objects. The data objects are described by metadata, containing e.g. their type, boundaries, and meaning. The following figure gives an overview of the JAMS DataStore architecture, showing two DataStore types for reading (InputDataStore) and writing (OutputDataStore) data. In the case of InputDataStore, methods can be used to ask for (hasNext) and retrieve (getNext) available data (DataSet). Accordingly, OutputDataStore provides methods to write data to the underlying storage device.


Bild:I_O_System.png

The supported data types reflect the available JAMS data types and include numerical types, string and calendar types, and spatial geometries. In order to represent the latter, JAMS makes use of the Simple Feature Access standard as defined by the Open Geospatial Consortium.

To access data from a DataStore, its ultimate data source and software to access it needs to be identified and parameterized. This is done by special XML documents. Each of them defines one DataStore and serves the following purposes:

(1) identify and parameterize an I/O software component that can ultimately access a certain type of data source,

(2) define one or more data sources to be accessed by that component, and in the case of an InputDataStore,

(3) define how retrieved data will be formatted and presented.

The I/O software components can be provided by the user and are made available to JAMS via a plug-in mechanism. As an example, this could be a JAVA class which is able to read ASCII-formatted files containing time series information. Parameters could be the file name and the read buffer size.

Development of JAMS Components

Installation of a Development Environment

You can download the development environment NetBeans at http://netbeans.org for free. The latest version is NetBeans IDE 6.9.1. Select Java SE, JavaFX, Java or All as NetBeans IDE Download package. In addition, the Java Development Kit (JDK) is required which can be downloaded at http://www.oracle.com/technetwork/java/javase/downloads/index.html.

Your NetBeans developer environment looks like this after starting it:

Bild:Bild1_1.png

Loading JAMS/J2000 sources from the Internet

The Java sources from JAMS/J2000 and various standard components, including all required libraries, can be downloaded at http://www.geoinf.uni-jena.de/5580.0.html using the link JAMS-2 16-src.tgz.

Bild:Quellen_1.png

The following source folders are included:

Bild:Quellordner.png

Setting up Projects in NetBeans

The projects are to be integrated according to the following order:

Step 1 - Integrating the JAMS Project

The JAMS/J2000 sources have to be loaded into the Netbeans. First, the JAMS project is set up. In order to do so, open NetBeans and select New Project under the menu item File.

Bild:Neues_Projekt.png

The following window appears:

Bild:Projekt_laden1.png

Since you want to integrate an existing source code, select Java Project with Existing Source. Click on Next. In the second step, you have to enter a name and a project folder. A project folder is entered as a default but it can be modified. Click on Next.

Bild:Projekt_laden2_2.png

By clicking on the button Add Folder you should load the following sources into the project:

  1. JAMSapi
  2. JAMScommon
  3. JAMSExplorer
  4. JAMSMain
  5. JAMSRemote
  6. JAMSstarter
  7. JAMSui

Bild:Projekt_laden3_1.png

Click on Finish.

In the next step, the required libraries for the project have to be loaded. You can find them in the downloaded source folder under JAMS-2_x-src\ext. (x stands for the version which is currently downloaded but changes over time) Right-click on the JAMS project in the generated JAMS project. Select item Properties.

Bild:Projekt_laden3_2.png

Select the item Libraries in the right part of the window Project Properties - JAMS under Categories. By using the button Add JAR/Folder the required libraries can be loaded. Load all libraries which are available in the downloaded source folder JAMS-2_x-src\ext.

Bild:Bibliotheken_laden.png

The JAMS project has been set up.

Bild:JAMS_Projekt.png

If you want to compile the project, right-click on the JAMS project and select the item Build and Clean. The JAMS.jar is being generated. In the bottom left window of your NetBeans developer environment you can check whether the process has been successful and where the JAMS.jar has been saved.

Bild:Build_Clean.png

Step 2 - Integrating the J2000 Project

Integrate the project J2000 as new project with existing sources as described in step 1. The sources are in directory ...\JAMS-x-src\J2K\src which has been downloaded.

Bild:J2000_Quellen.png

Load the generated JAMS.jar which has been generated in step 1 as a library.

Bild:Bibliotheken_laden_J2000.png

Compile the new project by right-clicking on the project J2000 and selecting the menu item Build and Clean. In the bottom right window of your NetBeans developer environment you should read BUILD SUCCESSFUL.

Step 3 - Integrating the JAMSComponents Project

Integrate the project JAMSComponents as new project with existing sources as described in step 1. The sources are in directory ...\JAMS-x-src\JAMSComponents\src which has been downloaded.

Load the JAMS.jar which has been generated in step 1 as library as well as all libraries (*.jar - files) from folder ...\JAMS-x-src\ext. Bild:Bibliotheken_laden_JAMSComponents.png

Compile the new project by right-clicking on the project J2000 and selecting the menu item Build and Clean. In the bottom right window of your NetBeans developer environment you should read BUILD SUCCESSFUL.

Launching Process Components from the NetBeans

Every process component which has a main procedure can be launched directly from the NetBeans. Jede Prozesskomponente welche über eine Main-Procedure vefügt, kann aus dem NetBeans direkt gestartet werden. As an example, we will launch the JAMSLauncher directly which is described in detail in the tutorial for basic users.

Main Classes

Right-click on the project JAMS. Choose the menu item Properties. Click on run in the left part of the window. Enter as Main Class the class which should be launched during project run. By single-clicking on the button Browse... all Main Classes which are available in the project are shown; these are all components which have a main procedure. Choose jamsui.launcher.JAMSui as an example. Click on Set Main Class. The following window should appear:

Bild:Main_classes.png

Click on OK. Launch the project JAMS by (left-)clicking on the project, the choosing the item run. The JAMSLauncher starts. In this way, various classes with a main procedure can be started.

Development of New Process Components in JAVA

Structure of a Component

Every JAMS process component is structured like this:

1. package name. It is automatically generated by NetBeans.

package org.unijena.j2k.interception;

2. Import of libraries/classes which are required for the process components.

import jams.data.*;
import jams.model.*;
...

3. Starting the process component. Every process component starts with a component description. In this way, new metadata of the component can be stored in the source code. It should include information on the component title, the author, version and modification date. In addition, a short description of the component is required.

@JAMSComponentDescription(
       title="...",
       author="...",
       description="...",
       version="...",
       date="..."    
       )
public class NewClass {

4. Set of variables with public and private variables. The public variables can be accessed from other classes within a model. For every public variable remarks in the form of Annotations are included in the source code.

The access type (AccessType) JAMSVarDescription.AccessType.READ (variables are only read), JAMSVarDescription.AccessType.WRITE (variablen are only written) or JAMSVarDescription.AccessType.READWRITE (variables are read and written) can be chosesn.

In addition, a short description as well as a measurement unit (if possible) and an upper and lower boundary of the variable is required.

@JAMSVarDescription(
           access =... ,
           description = "...",
           unit = "...",
           lowerBound= ...,
           upperBound =...            
           )
public JAMSDouble  var1;

@JAMSVarDescription(
           access =... ,
           description = "...",
           unit = "...",
           lowerBound= ...,
           upperBound =...            
           )
public JAMSInteger var2;
 
@JAMSVarDescription(
           access =... ,
           description = "...",
           unit = "...",
           lowerBound= ...,
           upperBound =...            
           )
public JAMSString  var3;
...

private JAMSDoublr var4;
...

Every JAMS- or J2000-class has the procedures init(), run() and cleanup() available.

5. The init procedure is passed during the initialization of the model.

public void init() throws JAMSEntity.NoSuchAttributeException{
...       
}

6. The run procedure is passed during the model run.

public void run() throws JAMSEntity.NoSuchAttributeException{
...
}

7. The cleanup procedure is passed at the end of a model run.

public void cleanup() throws JAMSEntity.NoSuchAttributeException{
...
}

8. Private procedures and functions. These are only available within the process component.

private double funktion1(double var){
       ...
       return ...;
   }


9. End of process component.

}

Example of a Complete Process Component

1. Package name

package org.unijena.j2k.potET;

2. Imported libraries

import java.io.*;
import jams.data.*;
import jams.model.*;

3. Starting the process component. Every process component starts with a component description. In this way, new metadata of the component can be stored in the source code. It should include information on the component title, the author, version and modification date. In addition, a short description of the component is required.

@JAMSComponentDescription(
       title=""CalcDailyETP_PenmanMonteith"",
       author="Peter Krause",
       description="Calculates potential ETP according Penman-Monteith",
       version="1.0_0",
       date="2011-04-11"    
       )
public class Penman extends JAMSComponent {

4. Set of variables with public and private variables. The public variables can be accessed from other classes within a model. For every public variable remarks in the form of Annotations are included in the source code.

The access type (AccessType) JAMSVarDescription.AccessType.READ (variables are only read), JAMSVarDescription.AccessType.WRITE (variablen are only written) or JAMSVarDescription.AccessType.READWRITE (variables are read and written) can be chosesn.

In addition, a short description as well as a measurement unit (if possible) and an upper and lower boundary of the variable is required.


   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "Current time")
   public JAMSCalendar time;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "temporal resolution [d | h | m]")
   public JAMSString tempRes;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "state variable wind")
   public JAMSDouble wind;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "state variable mean temperature")
   public JAMSDouble tmean;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "state variable relative humidity")
   public JAMSDouble rhum;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "state variable net radiation")
   public JAMSDouble netRad;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "attribute elevation")
   public JAMSDouble elevation;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "attribute area")
   public JAMSDouble area;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.WRITE,
   description = "potential ET [mm/ timeUnit]")
   public JAMSDouble potET;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.WRITE,
   description = "actual ET [mm/ timeUnit]")
   public JAMSDouble actET;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   description = "et calibration parameter")
   public JAMSDouble et_cal;

   @JAMSVarDescription(access = JAMSVarDescription.AccessType.READ,
   defaultValue = "0")
   public JAMSInteger dataCaching;

   private File cacheFile;
   transient private ObjectOutputStream writer;
   transient private ObjectInputStream reader;
   public final double CP = 1.031E-3;   //konstanter Parameter  
   public final double RSS = 150;       //konstanter Parameter 


5. The init procedure is passed during the initialization of the model.

   public void init() throws JAMSEntity.NoSuchAttributeException, IOException {
       cacheFile = new File(getModel().getWorkspace().getTempDirectory(), this.getInstanceName() + ".cache");
       if (!cacheFile.exists() && (dataCaching.getValue() == 1)) {
           getModel().getRuntime().sendHalt(this.getInstanceName() + ": dataCaching is true but no cache file available!");
       }

       if (dataCaching.getValue() == 1) {
           reader = new ObjectInputStream(new BufferedInputStream(new FileInputStream(cacheFile)));//new FileInputStream(cacheFile));
       } else if (dataCaching.getValue() == 0) {
           writer = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(cacheFile)));
       }
   }

6. The run procedure is passed during the model run.

   public void run() throws JAMSEntity.NoSuchAttributeException, IOException {

       if (dataCaching.getValue() == 1) {
           this.potET.setValue(reader.readDouble());
           this.actET.setValue(0.0);
       } else {
           double netRad = this.netRad.getValue();
           double temperature = this.tmean.getValue();
           double rhum = this.rhum.getValue();
           double wind = this.wind.getValue();
           double elevation = this.elevation.getValue();
           double area = this.area.getValue();

           double abs_temp = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_absTemp(temperature, "degC");
           double delta_s = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_slopeOfSaturationPressureCurve(temperature);
           double pz = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_atmosphericPressure(elevation, abs_temp);
           double est = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_saturationVapourPressure(temperature);
           double ea = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_vapourPressure(rhum, est);

           double latH = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_latentHeatOfVaporization(temperature);
           double psy = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_psyConst(pz, latH);

           double G = this.calc_groundHeatFlux(netRad);
           double vT = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_VirtualTemperature(abs_temp, pz, ea);
           double pa = org.unijena.j2k.physicalCalculations.ClimatologicalVariables.calc_AirDensityAtConstantPressure(vT, pz);

           double tempFactor = 0;
           double pET = 0;
           double aET = 0;


           if (this.tempRes.getValue().equals("d")) {
               tempFactor = 86400;
           } else if (this.tempRes.getValue().equals("h")) {
               tempFactor = 3600;
           } else if (this.tempRes.getValue().equals("m")) {
               tempFactor = 86400;
           }
           double Letp = 0;
           Letp = this.calcPM(delta_s, netRad, G, pa, CP, est, ea, psy, tempFactor, wind);

           pET = Letp / latH;
           aET = 0;

           //converting mm to litres
           pET = pET * area;

           //aggregation to monthly values
           if (this.time != null) {
               if (this.tempRes.getValue().equals("m")) {
                   int daysInMonth = this.time.getActualMaximum(time.DATE);
                   pET = pET * daysInMonth;
               }
           }
           //avoiding negative potETPs
           if (pET < 0) {
               pET = 0;
           }

           this.potET.setValue(pET*et_cal.getValue());
           this.actET.setValue(aET);
           
           if (dataCaching.getValue() == 0) {
               writer.writeDouble(pET*et_cal.getValue());
           }
       }
   }

7. The cleanup procedure is passed at the end of a model run.

   public void cleanup() throws IOException {
       if (dataCaching.getValue() == 0) {
           writer.flush();
           writer.close();
       } else if (dataCaching.getValue() == 1) {
           reader.close();
       }
   }

8. Private procedures and functions. These are only available within the process component.

private double calcPM(double ds, double netRad, double G, double pa, double CP, double est, double ea, double psy, double tempFactor, double  wind){
       double fu = (0.27 + 0.2333 * wind);
       double Letp = (ds * (netRad - G) + (pa * CP * (est - ea) * fu)) / (ds + psy);
       return Letp;
   }
   
private double calc_groundHeatFlux(double netRad) {
       double g = 0.1 * netRad;
       return g;
   }

9. End of process component.

}

Entwicklung und Implementierung von JAMS-Kontextkomponenten

Integrating New Process Components into JAMS or J2000 Libraries

In order to add your generated process component to the existing JAMS or J2000 components, generate a new *.jar of the project where the component is located. Right-click on the project and select the items Build and Clean. Now integrate this *.jar as a library, where you will find your new component.

Integrating and Testing New (Context) Components

In order to integrate new (context) components into existing models or to use them for new models, the tool JUICE is available for designing models. In section 1.2 Application of JUICE for Model Design and Model Configuration in the Tutorial Advanced Users all tasks for the integration of components are explained. By using the JUICE the model can be started and new components can be tested.

Debugging and Profiling of Models in JAMS

Debugging

In order to reconstruct the functioning of existing (JAMS/J2000) components and those which have been developed or to facilitate error search in the process of development, you have the debugging function is at your disposal. You can select positions in the source code by using breakpoints where the compiler is supposed to stop the model run. Hence it is possible to view the values of the designated variables at this time. Moreover, the functioning of existing or developed component can be reconstructed line by line. Before starting the debug mode of the model, you have to set breakpoints at the positions which are interesting for you. Therefore, click on the red point in the menu bar and afterwards on the position in the source code. In the following image, two breakpoints were set:

Bild:Haltepunkte_setzen.png

If your component has a main procedure, you can start the debugging directly. If your component is part of a complex model, you have to debug the entire model. In this case, right-click on the project which is supposed to start (the project which contains the component) and select debug.

Bild:Debug_aufrufen.png

Der Modelllauf wird an dem von Ihnen markierten Punkt angehalten. In unteren Fenster sehen Sie die aktuelle Belegung der Modellvariablen. Im linken oberen Fenster die Position im Quellcode.

Bild:Debug_1.png

Nun können Sie die Arbeitsweise des Quellcodes schrittweise nachvollziehen. Dazu stehen Ihnen die folgenden Optionen, welche in des Netbeans-Menüleiste unter dem Punkt Debug oder teilweise direkt in der Menüleiste zu finden sind, zur Verfügung:

  • Finish Debug Session - Beeindet den Debug-Modus
  • Continue - Springt zum nächsten Haltepunkt
  • Step Over - Geht zur nächsten Anweisung
  • Step Into - Geht falls möglich in die Anweisung. Anderenfall zur nächsten Anweisung
  • Step Out

Bild:Debug_Menue.png

Mit den Funktionen Step Over und Step Into können Sie schrittweise die Arbeitsweise des Quellcodes verfolgen. Die grün hinterlegte Zeile ist die aktuelle Position, welche ausgewertet wird.

Bild:Debug_2.png

Durch Betätigung des Buttons Continue' können Sie zum nächsten gesetzten Haltepunkt (rot hinterlegte Zeile) springen.Bild:Debug_3.png

Profiling im Netbeans

Netbeans bietet die Möglichkeit, die Modelllaufzeiten, d.h. welcher Teil des Modells wieviel Zeit beansprucht, nachzuvollziehen. Damit können zeitintensive Implementierungen identifiziert werden.

Klicken Sie dazu mit der rechten Maustaste auf das Projekt, welches analysiert werden soll. Wählen sie unter Eigenschaften (Properties) im Punkt Run die Main-Klasse, auf der das Profiling durchgeführt werden soll.

Wählen Sie nun den Punkt Profiling.

Bild:profile_1.png

Das folgende Fenster wird geöffnet. Nun können sie zwischen der Analyse des komletten Quellcodes oder von Quellcodeabschnitte wählen. Folgende Optionen stehen Ihnen zum Filtern zur Verfügung:

  • Profile all classes
  • Profile only project classes
  • Exclude Java core classes
  • Quick Filter

Klicken Sie auf run

Bild:profile_2.png

Die Laufzeitanalyse wird durchgeführt. Das folgende Fenster wird angezeigt.

Bild:profile_4.png

Das Ergebnis der Analyse wird Ihnen im Netbeans angezeigt. Im linken Fenster sind allgemeine Informationen zu Ihren Profiling angezeigt. Im rechten oberen Fenster sind die Laufzeiten der einzelne Funktionen innerhalb Ihrer gewählten Main-Klasse angezeigt.

Bild:profile_5.png

Personal tools