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

This directory contains examples of writing a user model that mimics
the beta1d 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, bet1a1d.fits, created using

  r0 = 23.5, beta = 0.71, 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 beta1d_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 1 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("beta1d_usermodel.sl");

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

  sherpa> () = evalfile("beta1d_usermodel.sl");
  Registering beta1d_um as a Sherpa model.
  sherpa> data beta1d.fits
  sherpa> source = beta1d_um[mymod]
  mymod.r0 parameter value [1] 10
  mymod.beta parameter value [1e-05] 0.5
  mymod.xpos parameter value [0] 
  mymod.ampl parameter value [1] 
  sherpa> show mymod
  beta1d_um[mymod]  (integrate: off)
      Param   Type      Value        Min        Max                 Units
      -----   ----      -----        ---        ---                 -----
   1     r0 thawed         10     1e-120     1e+120                      
   2   beta thawed        0.5      1e-05         10                      
   3   xpos frozen          0          0     1e+120                      
   4   ampl thawed          1          0     1e+120                      
  sherpa> set_log;
  sherpa> lp fit
  Warning: negative and zero values ignored in log scale
  sherpa> fit
   LVMQT: V2.0
   LVMQT: initial statistic value = 15600.8
   LVMQT: final statistic value = 496.754 at iteration 16
           mymod.r0  24.7523     
           mymod.beta  0.755139     
           mymod.ampl  98.387     

  sherpa> lp fit
  Warning: negative and zero values ignored in log scale


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 beta1d_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 "beta1d_interface" and
is made available to S-Lang via the name "_beta1d_um_<id>". These
names can be changed.

The final part of the interface is the S-Lang file beta1d_um_<id>.sl.
This contains a S-Lang routine (beta1d_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
"beta1d_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 beta1d_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 ("beta1d_um_c");
  Registering beta1d_um_c as a Sherpa model.

(or beta1d_um_cc or beta1d_um_f depending on the language being used)

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

  sherpa> data beta1d.fits
  sherpa> source = beta1d_um_c[mymod]
  mymod.r0 parameter value [1] 10
  mymod.beta parameter value [1e-05] 0.5
  mymod.xpos parameter value [0] 
  mymod.ampl parameter value [1] 
  sherpa> show mymod
  beta1d_um_c[mymod]  (integrate: off)
      Param   Type      Value        Min        Max                 Units
      -----   ----      -----        ---        ---                 -----
   1     r0 thawed         10     1e-120     1e+120                      
   2   beta thawed        0.5      1e-05         10                      
   3   xpos frozen          0          0     1e+120                      
   4   ampl thawed          1          0     1e+120                      
  sherpa> set_log;
  sherpa> lp fit
  Warning: negative and zero values ignored in log scale
  sherpa> fit
   LVMQT: V2.0
   LVMQT: initial statistic value = 15600.8
   LVMQT: final statistic value = 496.754 at iteration 16
           mymod.r0  24.7523     
           mymod.beta  0.755139     
           mymod.ampl  98.387     

  sherpa> lp fit
  Warning: negative and zero values ignored in log scale

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 beta1d_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/beta1d_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
is 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 beta1d.fits
  sherpa> source = usermodel[mymod]
  mymod.paramcount parameter value [0] 4
  mymod.param1 parameter value [1] 
  mymod.param2 parameter value [2] 
  mymod.param3 parameter value [3] 
  mymod.param4 parameter value [4] 
  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 beta1d.fits
  sherpa> source = usermodel[mymod]
  mymod.paramcount parameter value [0] 4
  mymod.r0 parameter value [1] 10
  mymod.beta parameter value [1e-05] 0.5
  mymod.xpos parameter value [0] 
  mymod.ampl parameter value [1] 
  sherpa> show mymod
  userModel[mymod]  (integrate: off)
      Param   Type      Value        Min        Max                 Units
      -----   ----      -----        ---        ---                 -----
   1paramcount frozen          4    -1e+120     1e+120                      
   2     r0 thawed         10     1e-120     1e+120                      
   3   beta thawed        0.5      1e-05         10                      
   4   xpos frozen          0          0     1e+120                      
   5   ampl thawed          1          0     1e+120                      
  sherpa> set_log
  sherpa> lp fit
  Warning: negative and zero values ignored in log scale
  sherpa> fit
   LVMQT: V2.0
   LVMQT: initial statistic value = 15600.8
   LVMQT: final statistic value = 496.754 at iteration 16
           mymod.r0  24.7523    
           mymod.beta  0.755139    
           mymod.ampl  98.387    

  sherpa> lp fit
  Warning: negative and zero values ignored in log scale

