
Writing a 2D user model for Sherpa
----------------------------------

This directory contains examples of writing a user model that mimics
the gauss2d model of Sherpa.

The directories are:

  pure_slang_version/        the pure S-Lang version

  c_slang_version/           compiled models using the S-Lang interface
  cc_slang_version/
  fortran_slang_version/

  c_version/                 compiled models using the ascitUserModel
  fortran_version/           interface

Each directory contains a simulated dataset, gauss2d.fits, created using

  fwhm = 28.26, xpos = 85, ypos = 44, ampl = 100

and a test script (test.shp) which shows how the model can be used, and
compares the results to the Sherpa version. The test.log file contains
a screen dump of the test script output.

1) A pure S-Lang model
----------------------

See the contents of the pure_slang_version/ directory.

The gauss2d_usermodel.sl file contains one S-lang routine, which
evaluates the model given the model parameters and the x-axis values
to use. The name of the routine must match the name you wish to use
in model expressions (e.g. 'mymodel' in 'source = mymodel[foo]'). The
register_model() routine is used to tell Sherpa about the model, what
dimensionality the model works on (so 2 here), the parameter names and,
optionally, the default/min/max/thawed values of these parameters. See
    ahelp slang usermodel
or
    http://cxc.harvard.edu/ciao/ahelp/slang-usermodel.html
for more information.

The S-Lang code can be loaded into Sherpa using

  sherpa> () = evalfile("<filename>.sl");

so in this example

  sherpa> () = evalfile("gauss2d_usermodel.sl");

Once loaded the model is available for use just as with any other Sherpa
model. For example:

  sherpa> () = evalfile("gauss2d_usermodel.sl");
  Registering gauss2d_um as a Sherpa model.
  sherpa> data gauss2d.fits
  Warning: Could not retrieve WCS coord descriptor
  sherpa> source = gauss2d_um[mymod]
  mymod.fwhm parameter value [10] 
  mymod.xpos parameter value [0] 50
  mymod.ypos parameter value [0] 50
  mymod.ellip parameter value [0] 
  mymod.theta parameter value [0] 
  mymod.ampl parameter value [1] 
  sherpa> fit   
   LVMQT: V2.0
   LVMQT: initial statistic value = 64481.1
   LVMQT: final statistic value = 2439.25 at iteration 15
           mymod.fwhm  27.7075     
           mymod.xpos  85.0284     
           mymod.ypos  43.9771     
           mymod.ampl  100.246     

  sherpa> image fit


2) Using a compiled model with the S-Lang interface
---------------------------------------------------

See the contents of the

    c_slang_version/             model written in C
    cc_slang_version/            model written in C++
    fortran_slang_version/       model written in FORTRAN

directories for your chosen language.

The model is written in the gauss2d_model_<id>.<id> file (with <id>
being c, cc, or f). The routine can have any name and calling
interface. The user_model.c (or .cc) file contains the code that
creates a S-Lang module and the routine that is used to get the
model parameter and axis values from S-Lang, call the user model,
and then return the calculated model values to Sherpa (as a S-Lang 
array). In the examples this routine is called "gauss2d_interface" and
is made available to S-Lang via the name "_gauss2d_um_<id>". These
names can be changed.

The final part of the interface is the S-Lang file gauss2d_um_<id>.sl.
This contains a S-Lang routine (gauss2d_um_<id>) which is registered
with Sherpa as a S-Lang user model. This routine checks the arguments
it has been sent and then calls the compiled code (i.e. the
"gauss2d_um_<id>" routine) to perform the model calculation.

To build and use the example:

a) Ensure that CIAO is initialized

The build requires that the ASCDS_INSTALL environment variable has
been set up.

b)  ./configure

This creates a link to the correct Makefile.

c)  make

(or 'make -f Makefile.<os>' if step a has not been run)

Compiles the code and creates the *-module.so file (in the example this
is called gauss2d_um_<id>-module.so for <id>=c, cc, or f).

d)  add the current directory to the start of the library path

On Solaris and Linux

  setenv LD_LIBRARY_PATH `pwd`:$LD_LIBRARY_PATH

On OS-X

  setenv DYLD_LIBRARY_PATH `pwd`:$DYLD_LIBRARY_PATH

This is so that S-Lang can locate the *-module.so file.

e)   load the module into Sherpa

  sherpa> require ("gauss2d_um_c");
  Registering gauss2d_um_c as a Sherpa model.

(or gauss2d_um_cc or gauss2d_um_f depending on the language being used)

The model can then be used as with any other Sherpa model:

  sherpa> data gauss2d.fits
  Warning: Could not retrieve WCS coord descriptor
  sherpa> source = gauss2d_um_c[mymod]
  mymod.fwhm parameter value [10] 
  mymod.xpos parameter value [0] 50
  mymod.ypos parameter value [0] 50
  mymod.ellip parameter value [0] 
  mymod.theta parameter value [0] 
  mymod.ampl parameter value [1]
  sherpa> show mymod
  gauss2d_um_c[mymod]  (integrate: off)
      Param   Type      Value        Min        Max                 Units
      -----   ----      -----        ---        ---                 -----
   1   fwhm thawed         10     1e-120     1e+120                      
   2   xpos thawed         50    -1e-120     1e+120                      
   3   ypos thawed         50    -1e-120     1e+120                      
   4  ellip frozen          0          0      0.999                      
   5  theta frozen          0          0     6.2832                      
   6   ampl thawed          1    -1e-120     1e+120                      
  sherpa> fit   
   LVMQT: V2.0
   LVMQT: initial statistic value = 64481.1
   LVMQT: final statistic value = 2439.25 at iteration 15
           mymod.fwhm  27.7075     
           mymod.xpos  85.0284     
           mymod.ypos  43.9771     
           mymod.ampl  100.246     

  sherpa> image fit

f) Moving the model to a more useful location

Once you have the model working correctly you can place the
<name>-module.so and <name>.sl files can be moved into a more
permanent/safer location. The directory containing the .so file should
be added to the SLANG_MODULE_PATH environment variable and the
directory containing the .sl file added to the SLANG_SCRIPT_PATH
environment variable. So, if the .so file were in
~/local/lib/slang/modules/ and the .sl file in
~/local/share/slsh/local-packages/ (these are the "standard" names for
S-Lang packages), then

 setenv SLANG_SCRIPT_PATH ~/local/share/slsh/local-packages:$SLANG_SCRIPT_PATH
 setenv SLANG_MODULE_PATH ~/local/lib/slang/modules:$SLANG_MODULE_PATH

will allow you to load the model into Sherpa from any directory.

Note that both these environment variables are set/modified by the CIAO
set up script and that the values added by the script are needed for CIAO
to work correctly.

Alternatively, you can add the following lines to your ~/.sherparc file:

  private variable home = getenv ("HOME");
  prepend_to_slang_load_path (home+"/local/share/slsh/local-packages");
  set_import_module_path (home+"/lib/slang/modules:"+
    get_import_module_path ());

(adjusting the paths as necessary). You can also follow these by the 

  require ("<module-name>");

line (with <module-name> changed to the correct name) to make the model
be loaded when Sherpa is started up.


3) Using a compiled model with the ascfitUserModel interface
------------------------------------------------------------

See the contents of the

    c_version/             model written in C
    fortran_version/       model written in FORTRAN

directories for your chosen language.

The model is written in the gauss2d_model_<id>.<id> file (with <id>
being c or f). Unlike the S-Lang version, this file must contain 4
routines:

  int ascfitusermodelinit_(void);
  int ascfitusermodelsetparamvalues_(int* i, 
                                     double* value,
                                     double* min, 
                                     double*  max, 
                                     char* name, 			    
                                     char* descr, 
                                     char* units, 
                                     int* state, 
                                     int* nameLen, 
                                     int* descLen, 
                                     int* unitsLen);
  int ascfitusermodelfunction_(double* parameterList,
                               int* numberOfDimensions,
                               int* numberOfData,
                               double* outValues,
                               double* x0,
                               double* x1,
                               double* x2,
                               double* x3,
                               double* x4,
                               double* x5,
                               double* x6);
  void ascfitusermodelcleanup_(void);

See the c_version/gauss2d_model_c.c file for comments on what these
routines are meant to do and what the arguments are.

The userMethod.c and userStatFunctions.c files provide the user method
and statistic routines, although in these examples they are intended
as placeholders (the routines must exist in the shared library that
will be created).

To build and use the example:

a)  ./configure

This creates a link to the correct Makefile.

b)  make

(or 'make -f Makefile.<os>' if step a has not been run)

This compiles the code and creates the libascfitUser.so shared library
(libascfitUser.dylib on OS-X).

d)  add the current directory to the start of the library path

On Solaris and Linux

  setenv LD_LIBRARY_PATH `pwd`:$LD_LIBRARY_PATH

On OS-X

  setenv DYLD_LIBRARY_PATH `pwd`:$DYLD_LIBRARY_PATH

The directory containing the libascfitUser.so/dylib file must be
placed at the start of this list, otherwise Sherpa will use the
default library from the CIAO distribution rather than your own
version. To check this try on Solaris or Linux

  ldd `which sherpa` | grep ascfitUser
        libascfitUser.so => <pwd>/libascfitUser.so

which should report the current working directory rather than the CIAO
installation firectory for the library. The 'otool -L' command can be
used on OS-X, but it does not seem to report the directory location in
the same manner as ldd.

Another way to tell that your model has not been picked up is if
the following happens:

  sherpa> data gauss2d.fits
  Warning: Could not retrieve WCS coord descriptor
  sherpa> source = usermodel[mymod]
  mymod.paramcount parameter value [0] 6
  mymod.param1 parameter value [1] 
  mymod.param2 parameter value [2] 
  mymod.param3 parameter value [3] 
  mymod.param4 parameter value [4] 
  mymod.param5 parameter value [5] 
  mymod.param6 parameter value [6] 
  sherpa> lp fit
  Error: You cannot use the usermodel until you have
  defined your userFunction.
  Please consult the user manual for instructions on
  how to implement, compile and dynamically link your
  userFunction to ascfit.
  Error: error in user model function call.

The two signs are:

  - the parameters are called param<n> and not the parameter names
    you gave them in the ascfitusermodelsetparamvalues routine

  - the model will not evaluate but prints out an error message
    instead

e)   Start Sherpa

The library will be automatically loaded into Sherpa when it is
started. It can be used in a similar manner to other Sherpa models
noting that:

  - the name of the model is usermodel
  - the first parameter is actually the number of parameters the
    model expects.

So, using the example code:

  sherpa> data gauss2d.fits
  Warning: Could not retrieve WCS coord descriptor
  sherpa> source = usermodel[mymod]
  mymod.paramcount parameter value [0] 6
  mymod.fwhm parameter value [10] 
  mymod.xpos parameter value [0] 50
  mymod.ypos parameter value [0] 50
  mymod.ellip parameter value [0] 
  mymod.theta parameter value [0] 
  mymod.ampl parameter value [1] 
  sherpa> show mymod
  userModel[mymod]  (integrate: off)
      Param   Type      Value        Min        Max                 Units
      -----   ----      -----        ---        ---                 -----
   1paramcount frozen          6    -1e+120     1e+120                      
   2   fwhm thawed         10     1e-120     1e+120                      
   3   xpos thawed         50    -1e+120     1e+120                      
   4   ypos thawed         50    -1e+120     1e+120                      
   5  ellip frozen          0          0      0.999                      
   6  theta frozen          0          0     6.2832                      
   7   ampl thawed          1    -1e+120     1e+120                      
  sherpa> fit
   LVMQT: V2.0
   LVMQT: initial statistic value = 64481.1
   LVMQT: final statistic value = 2439.25 at iteration 15
           mymod.fwhm  27.7075    
           mymod.xpos  85.0284    
           mymod.ypos  43.9771    
           mymod.ampl  100.246    

  sherpa> image fit

