How To Component

From CSDMS
Revision as of 12:13, 5 September 2008 by Huttone (talk | contribs) (Changed sub-heading formats)

Subsidence Port

Create a port that interacts with a subsidence component. For now, I declare two port methods. One to initialize (or reinitalize) the component, and one the get deflections (in meters). <geshi lang=bash>bocca create port subsidePort</geshi> This is done by editing the appropriate SIDL file. The best way to do this is with the bocca edit command. A normal array is able to access the underlying data as well as its length and rank. It is more like a FORTRAN array where a raw array is more like a c pointer. <geshi lang=bash>bocca edit port subsidePort</geshi> A snippet from the sidl file that defines the port. <geshi lang=java> interface subsidePort extends gov.cca.Port {

  // DO-NOT-DELETE bocca.splicer.begin(csdms.subsidePort.methods)
  // Insert-UserCode-Here {csdms.subsidePort.methods} (Insert your port methods here)
  // Read init data to set constants. If file is empty, use defaults. int init( in string file );
  // Get the deflection (in meters) due to the applied loads.
  int get_deflection_in_m( inout rarray<double,1> dz(len) , in rarray<double,1> x(len) , in rarray<double,1> load(len) , in int len ); 

} </geshi> Note the way that arrays are handled in the SIDL language. I'll add a section about this later. These are called "raw arrays". The SIDL language also has "normal" arrays, which are declared as `array`.

Subsidence Component

Create a component that wraps an existing subsidence model. The model predicts the subsidence of Earth's crust due to vertical loading. <geshi lang=bash> bocca create component subside --language=c --provides=subsidePort </geshi> This will create a component that has a single port that provides data to the caller. The way that the new component implements this functionality is defined in the appropriate Impl file. Once again use the edit command, <geshi lang=bash> bocca edit component subside --impl </geshi> without the impl option, bocca would edit the component's SIDL file. Allowed options are: sidl (the default), impl, and head. To go to a particular function, you can specify it at the end. For instance, in this case use, <geshi lang=bash> bocca edit component subside --impl get_deflection_in_m </geshi> Remove the code between the DO-DELETE tags and add your own code. Just to get things going I've added the following: <geshi lang=c> {

  int32_t status = 0;
  if ( dz && x && load && len>0 )
  {
     int32_t i;
     for ( i=0 ; i<len ; i++ )
        dz[i] = load[i]*5.; status = 0;
  }
  else
     status = -1;
  return status;

} </geshi>

The Driver Component

Although we've created a component, it isn't much good by itself. Let's now create a component that calls subside. This will be the project's driver component. <geshi lang=bash> bocca create component subsideDriver --go=start --uses=subsidePort --language=c bocca edit component subsideDriver --impl go </geshi> This function will have a significant amount of auto-generated code. You'll have to scroll down to the next 'Insert-UserCode-Here' section. Remove the code in the 'REMOVE ME' block. I've added the following, <geshi lang=c> if ( bocca_status==0 ) {

  int32_t i;
  int32_t len = 100;   // Number of grid nodes
  double  dx  = 1000.; // Grid spacing in meters
  double* x   = (double*)malloc( sizeof(double)*len );
  double* v   = (double*)malloc( sizeof(double)*len );
  double* dz  = (double*)malloc( sizeof(double)*len );
  if ( x && v && dz )
  { /* Set the initial data and get the deflection */
     for ( i=0 ; i<len ; i++ )
     {
        x[i]  = i*dx;
        v[i]  = 0.;
        dz[i] = 0.;
     }
     v[len/2] = 1.;
     bocca_status = csdms_subsidePort_get_deflection_in_m( subsidePort , dz , x , v , len , &throwaway_excpt );
     if ( bocca_status==0 )
     { /* Print the result */
        for ( i=0 ; i<len ; i++ )
           fprintf( stdout , "%f ; %f ; %f\n" , x[i] , dz[i] , v[i] );
        fflush( stdout );
     }
  }
  else
     bocca_status = -1;
  if ( x  ) free( x  );
  if ( v  ) free( v  );
  if ( dz ) free( dz );

} </geshi> After all of those changes we are now ready to configure and build the project. First run configure in the top-level directory, <geshi lang=bash> ./configure --prefix=/path/to/install/dir </geshi> This configure script is the same as any other (use configure --help for a full list of options). By default, configure will install in ./install. Now make and install the project, make all install If you don't want to install the project (it can take a lot of time), a simple make should set things up to run some tests. If everything compiled without errors, you can run a test using the ccafe gui, <geshi lang=bash> utils/run-gui.sh </geshi> Connect the two components and click 'start'. The deflections should print to the screen.

Link to External Libraries

Now change the above implementation to use functions from external libraries. To implement the subsidence model, we'll need two functions: `get_flexure_parameter`, and `subside_point_load_1d`. These are both part of the sedflux installation. On my machine, I've installed sedflux in `/usr/local/ew/2.0.38`.

The new implementation code for the subside component becomes <geshi lang=c>

   {
       /* DO-NOT-DELETE splicer.begin(csdms.subside.get_deflection_in_m) */
       int32_t status = 0;
       if ( dz && x && load && len>0 )
       {
            int32_t i;
            double alpha = get_flexure_parameter( 10000. , 7e10 , 1 );
            fprintf( stderr , "alpha = %f\n" , alpha );
            fflush( stderr );
            for ( i=0 ; i<len ; i++ ) dz[i] = 0.;
            for ( i=0 ; i<len ; i++ )
            if ( load[i]>0 )
                subside_point_load_1d( dz , x , len , load[i] , x[i] , alpha );
            status = 0;
        }
        else
            status = -1;
        return status;
        /* DO-NOT-DELETE splicer.end(csdms.subside.get_deflection_in_m) */
   }

</geshi> In addition, include the header file `sed.h` at the beginning of the file. <geshi lang=c>

   /* Insert-UserCode-Here {csdms.subside._includes} (includes and arbitrary code) */
   #include <sed.h>

</geshi> The compiler needs to know where the libraries are that define these new functions. This is done by setting `INCLUDES` and `LIBS` in the appropriate make.vars.user file. In this case, edit `components/csdms.subside/make.vars.user`, <geshi lang=bash>

    INCLUDES = -I/usr/local/ew/2.0.38/include/ew-2.0 -I/usr/local/gtk/include/glib-2.0 -I/usr/local/gtk/lib/glib-2.0/include
    # Library paths and names, binaries
    LIBS = -L/usr/local/gnu/lib -L/usr/local/ew/2.0.38/lib -L/usr/local/gtk/lib -lsedflux -lutils -lm -lglib-2.0 -lintl -liconv -lsedflux-2.0

</geshi> Once again, in the top-level directory, <geshi lang=bash> make all install ; utils/run-gui.sh </geshi>

Editing Configure.in

There should is a better way to do the above. The above requires the user to edit by hand the `LIBS` and `INCLUDES` variables to match their particular setup. This can be done automatically by configure. First edit configure.in, <geshi lang=bash>

   # insert user-autoconf-here
    PKG_CONFIG=pkg-config
    SEDFLUX_CFLAGS=`$PKG_CONFIG --cflags sedflux`
    SEDFLUX_LIBS=`$PKG_CONFIG --libs sedflux`
    AC_SUBST(SEDFLUX_CFLAGS)
    AC_SUBST(SEDFLUX_LIBS)

</geshi> If `pkg-config` is installed and `PKG_CONFIG_PATH` contains the directory that holds sedflux.pc, the sedflux include and lib flags will be set correctly. So that these variables are given to make, they are set in make.project.in <geshi lang=bash>

   # Insert configure output variables here
   SEDFLUX_CFLAGS = @SEDFLUX_CFLAG@
   SEDFLUX_LIBS = @SEDFLUX_LIBS@

</geshi> Now we use these variables in the component's make.vars.user file (components/csdms.subside/make.vars.user, in this case), <geshi lang=bash>

   # Include path directives, including paths to Fortran modules
   INCLUDES = $(SEDFLUX_CFLAGS)
   # Library paths and names, binaries
   LIBS = $(SEDFLUX_LIBS) -lsubside

</geshi> Note that libsubside is the particular sedflux library that contains the symbols used in our example.

Download An Existing Project

I've uploaded a version of the above test to the downloads section. After downloading and unpacking it, go to the project's top level directory (`cd csdms`, in this case). Because the new machine may be configured differently than the one I used to create the project, the project must be configured, <geshi lang=bash>

   bocca config -u

</geshi> Now things should be set to build and test the project, <geshi lang=bash>

   make clean all install ; ./utils/run-gui.sh

</geshi>

Create a River Component

Create a port for a river component, <geshi lang=bash>

   bocca create port riverPort

</geshi> <geshi lang=java>

   interface riverPort extends gov.cca.Port
   {
       int get_width ( out double w );
       int get_depth ( out double d );
       int get_velocity ( out double v );
       int get_discharge( out double q );
       int pop_event ( );
   }

</geshi>

Create a river component, <geshi lang=bash>

   bocca create component river --provides=riverPort --language=c

</geshi> This particular river component will use the sedflux type `Sed_hydro_file` to read in either a sedflux river file or a HydroTrend file and then provide river data as needed. To begin with, provide the component with some extra data to remember, namely the `Sed_hydro_file` info. This is done in the component's header file, <geshi lang=bash>

   bocca edit component river --header

</geshi> <geshi lang=c>

   struct csdms_river__data {
       /* DO-NOT-DELETE splicer.begin(csdms.river._data) */
       /* Bocca generated code. bocca.protected.begin(csdms.river._data) */
       /* Handle to framework services object */
       gov_cca_Services d_services;
       /* Bocca generated code. bocca.protected.end(csdms.river._data) */
       /* Put other private data members here... */
       Sed_hydro_file hydro_file;
       /* DO-NOT-DELETE splicer.end(csdms.river._data) */
   }; 

</geshi> Don't forget to include `sed.h`, <geshi lang=c>

   /* Insert-Code-Here {csdms.river._includes} (include files) */ #include <sed.h>

</geshi> Write the implementation of the port methods. <geshi lang=bash>

    bocca config CLASSNAME --var=VARUSER --value=VALUE

</geshi>