How To Component

From CSDMS

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).

bocca create port subsidePort

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.

bocca edit port subsidePort

A snippet from the sidl file that defines the port.

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 ); 
}

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.

bocca create component subside --language=c --provides=subsidePort

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,

bocca edit component subside --impl

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,

bocca edit component subside --impl get_deflection_in_m

Remove the code between the DO-DELETE tags and add your own code. Just to get things going I've added the following:

{
   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;
}

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.

bocca create component subsideDriver --go=start --uses=subsidePort --language=c
bocca edit component subsideDriver --impl go

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,

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 );
}

After all of those changes we are now ready to configure and build the project. First run configure in the top-level directory,

./configure --prefix=/path/to/install/dir

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,

utils/run-gui.sh

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

    {
        /* 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) */
    }

In addition, include the header file `sed.h` at the beginning of the file.

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

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`,

     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

Once again, in the top-level directory,

make all install ; utils/run-gui.sh

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,

    # 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)

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

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

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

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

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,

    bocca config -u

Now things should be set to build and test the project,

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

Create a River Component

Create a port for a river component,

    bocca create port riverPort
    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 ( );
    }

Create a river component,

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

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,

    bocca edit component river --header
    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) */
    };

Don't forget to include `sed.h`,

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

Write the implementation of the port methods.

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