;$Id: model_data__define.pro,v 1.9 2006/07/28 23:26:04 azuah Exp $
;###############################################################################
;
; NAME:
;  MODEL_DATA__DEFINE
;
; PURPOSE:
;  Data object class definition for use in RAINS.
;
; CATEGORY:
;  DAVE, Data Analysis, RAINS, surface fitting
;
; AUTHOR:
;   Robert M. Dimeo, Ph.D.
;   NIST Center for Neutron Research
;   100 Bureau Drive
;   Gaithersburg, MD 20899
;   Phone: (301) 975-8135
;   E-mail: robert.dimeo@nist.gov
;   http://www.ncnr.nist.gov/staff/dimeo
;
; LICENSE:
;  The software in this file is written by an employee of
;  National Institute of Standards and Technology
;  as part of the DAVE software project.
;
;  The DAVE software package is not subject to copyright protection
;  and is in the public domain. It should be considered as an
;  experimental neutron scattering data reduction, visualization, and
;  analysis system. As such, the authors assume no responsibility
;  whatsoever for its use, and make no guarantees, expressed or
;  implied, about its quality, reliability, or any other
;  characteristic. The use of certain trade names or commercial
;  products does not imply any endorsement of a particular product,
;  nor does it imply that the named product is necessarily the best
;  product for the stated purpose. We would appreciate acknowledgment
;  if the DAVE software is used of if the code in this file is
;  included in another product.
;
;###############################################################################
;+
; NAME:
;       MODEL_DATA__DEFINE
;
; PURPOSE:
;
;   This function creates a DAVE data object from a DAVE pointer specified
;   either with the pointer itself or by the filename.  The function returns
;   a 1 or 0 based on successful extraction of the pointer information.
;
; AUTHOR:
;
;       Robert M. Dimeo, Ph.D.
;   NIST Center for Neutron Research
;       100 Bureau Drive
;   Gaithersburg, MD 20899
;       Phone: (301) 975-8135
;       E-mail: robert.dimeo@nist.gov
;       http://www.ncnr.nist.gov/staff/dimeo
;
; CATEGORY:
;
;       Utility, DAVE_REFINE application, DAVE project
;
; CALLING SEQUENCE:
;
;
;   odata = OBJ_NEW('model_data', FILENAME = filename, $
;                    DAVEPTR = daveptr,    $
;                    ERRMSG = errmsg)
;
; INPUT PARAMETERS (REQUIRED)
;
;  User must specify either FILENAME or DAVEPTR.
;
;  DAVEPTR:  This must be a properly defined DAVE pointer as defined
;        in DAVE.PRO for instance.
;  FILENAME: String with the fully-qualified path specifying the file that,
;        when restored, returns the DAVEPTR.
;
;
; OPTIONAL OUTPUT KEYWORDS
;
;  ERRMSG:     string variable specifying the cause of an error in which the
;        function returns 0.
;
; COMMON BLOCKS:
;
;   None
;
; DEPENDENCIES:
;
;  Requires the function GET_DAVE_PTR_CONTENTS, TVIMAGE
;
; REQUIREMENTS:
;
;  Written using IDL 5.6.  I cannot ensure that it will work with previous
;  releases.
;
; METHODS:
;
;  INIT:      Typical lifecycle method (function) that initializes data
;  CLEANUP:   Typical lifecycle method (procedure) that frees heap variables
;  GET:      Function that returns any of the data in the class
;  SELECT_GROUPS:  Function that reduces the data to a specific set of groups
;         selected from the x-axis or y-axis as specified by the keywords
;         XGROUPS and YGROUPS, both arrays of zero-based indices.
;  RESTORE_GROUPS: Function that restores the data to their original complete
;         set of groups.
;  SELECT_RANGE:   Function that reduces the data to a specific set of groups
;         selected from the limits specified through the keywords
;         XLO, XHI, YLO, YHI.  The default is to include the entire
;         data range.
;
; MODIFICATION HISTORY:
;
;       Written by Rob Dimeo, April 8, 2003
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::set,  wid = wid     $
                           ,setOrig=setOrig $
                           ,setCopy=setCopy $
                           ,qty=qty $
                           ,err=err $
                           ,xvals=xvals $
                           ,yvals=yvals $
                           ,xrange = xrange $
                           ,yrange = yrange $
                           ,fitGrps = fitGrps

if (n_elements(qty) gt 0) then (*self.pqty) = qty
if (n_elements(err) gt 0) then (*self.perr) = err
if (n_elements(xvals) gt 0) then (*self.px) = xvals
if (n_elements(yvals) gt 0) then (*self.py) = yvals
if (n_elements(fitGrps) gt 0) then (*self.fitGrpsPtr) = fix(fitGrps)
if (n_elements(setOrig) gt 0) then begin
   if (n_elements(qty) gt 0) then (*self.pqty_orig) = qty
   if (n_elements(err) gt 0) then (*self.perr_orig) = err
   if (n_elements(xvals) gt 0) then (*self.px_orig) = xvals
   if (n_elements(yvals) gt 0) then (*self.py_orig) = yvals
endif
if (n_elements(setCopy) gt 0) then begin
   if (n_elements(qty) gt 0) then (*self.pqty_copy) = qty
   if (n_elements(err) gt 0) then (*self.perr_copy) = err
   if (n_elements(xvals) gt 0) then (*self.px_copy) = xvals
   if (n_elements(yvals) gt 0) then (*self.py_copy) = yvals
   (*self.fitGrpsPtr) = indgen(n_elements(yvals)) + 1 ; all groups
endif

if n_elements(xrange) ne 0 then self.xrange = xrange
if n_elements(yrange) ne 0 then self.yrange = yrange

if (n_elements(wid) ne 0) then self.wid = wid

return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::get,  instrument = instrument, $
                           qty = qty,          $
                           nx = nx,           $
                           ny = ny,           $
                           fitGrps=fitGrps, $
                           qtunits = qtunits,      $
                           qtlabel = qtlabel,      $
                           err = err,          $
                           xvals = xvals,         $
                           xtype = xtype,         $
                           xunits = xunits,      $
                           xlabel = xlabel,      $
                           yvals = yvals,         $
                           ytype = ytype,         $
                           yunits = yunits,      $
                           ylabel = ylabel,      $
                           dname = dname,         $
                           dunits = dunits,      $
                           dlegend = dlegend,      $
                           dqty = dqty,        $
                           derr = derr,        $
                           treatment = treatment,    $
                           wid = wid,          $
                           display_name = display_name, $
                           filename = filename,      $
                           position = position,      $
                           xrange = xrange,      $
                           yrange = yrange,      $
                           getOrig = getOrig, $
                           getCopy = getCopy, $
                           ermsg = ermsg

ermsg = 'No errors generated'
xrange = self.xrange
yrange = self.yrange
position = self.position
filename = self.filename
wid = self.wid
display_name = self.display_name
instrument = self.instrument
if (ptr_valid(self.fitGrpsPtr)) then fitGrps = (*self.fitGrpsPtr)
if (n_elements(getCopy) eq 1) then begin
   qty = *self.pqty_copy
   err = *self.perr_copy
   xvals = *self.px_copy
   yvals = *self.py_copy
endif else if (n_elements(getOrig) eq 1) then begin
   qty = *self.pqty_orig
   err = *self.perr_orig
   xvals = *self.px_orig
   yvals = *self.py_orig
endif else begin
   qty = *self.pqty
   err = *self.perr
   xvals = *self.px
   yvals = *self.py
endelse

qty_size = size(qty)
nx = qty_size[1] & ny = qty_size[2]
qtunits = self.qtunits & qtlabel = self.qtlabel
xtype = self.xtype & ytype = self.ytype
yunits = self.yunits & xunits = self.xunits
xlabel = self.xlabel & ylabel = self.ylabel
treatment = *self.ptreatment
any_args_called = arg_present(dname) or arg_present(dunits) or $
           arg_present(dlegend) or arg_present(dqty) or $
           arg_present(derr)

if any_args_called and not self.descrip_def then begin
   ermsg = 'Descriptor information not present'
   return,0
endif
if any_args_called and self.descrip_def then begin
   dname = self.dname
   dunits = self.dunits
   dlegend = self.dlegend
   dqty = self.dqty
   derr = self.derr
endif
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::plot_group,group,          $
                 _Extra = extra,      $
                 errmsg = errmsg
if n_params() eq 0 then begin
   errmsg = 'You must specify which group to plot'
   return,0
endif
ret = self->restore_groups() ;??? comment out - RTA
x = *self.px
y = (*self.pqty)[*,group]
dy = (*self.perr)[*,group]
if extra.autoscale eq 1 then begin
   xrange = [min(x),max(x)]
   yrange = [min(y-dy),max(y+dy)]
endif else begin
   xrange = extra.xrange
   yrange = extra.yrange
endelse
self.xrange = xrange
self.yrange = yrange
yvalue = (*self.py)[group]
this_format = '(f10.2)'
ylabel = self.ylabel+' = '+strtrim(string(yvalue,format = this_format),2)
plot,*self.px,(*self.pqty)[*,group],psym = 4,xrange = xrange, yrange = yrange, $
   /xstyle,/ystyle,xtitle = self.xlabel,ytitle = self.qtlabel,title = ylabel
errplot,*self.px,(*self.pqty)[*,group]-(*self.perr)[*,group], $
   (*self.pqty)[*,group]+(*self.perr)[*,group],width = 0.0
ret = self->restore_groups()    ;??? comment out - RTA
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::restore_groups
*self.pqty = *self.pqty_orig
*self.perr = *self.perr_orig
*self.px = *self.px_orig
*self.py = *self.py_orig
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::restore_data
*self.pqty_orig = *self.pqty_copy
*self.perr_orig = *self.perr_copy
*self.px_orig = *self.px_copy
*self.py_orig = *self.py_copy
*self.pqty = *self.pqty_orig
*self.perr = *self.perr_orig
*self.px = *self.px_orig
*self.py = *self.py_orig
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::select_groups, xgroups = xgroups,     $
                  ygroups = ygroups,    $
                  errmsg = errmsg

; Groups specifies the groups to be included in the data
qty_size = size(*self.pqty)
nx = qty_size[1] & ny = qty_size[2]

errmsg = 'No errors generated in group selection'
if n_elements(xgroups) eq 0 and n_elements(ygroups) eq 0 then begin
   errmsg = 'You have not specified any groups to include'
   return,0
endif
if n_elements(xgroups) eq 0 then xgroups = indgen(nx)
if n_elements(ygroups) eq 0 then ygroups = indgen(ny)
if n_elements(xgroups) eq 1 or n_elements(ygroups) eq 1 then begin
   errmsg = 'You must specify at least 2 x-groups and 2 y-groups'
   return,0
endif

max_xgroups = max(xgroups)
max_ygroups = max(ygroups)

if ((max_xgroups+1) gt nx) or ((max_ygroups+1) gt ny) then begin
   errmsg = 'One or more groups specified exceed number of data groups'
   return,0
endif

*self.pqty = ((*self.pqty_orig)[xgroups,*])[*,ygroups]
*self.perr = ((*self.perr_orig)[xgroups,*])[*,ygroups]
*self.px = (*self.px_orig)[xgroups]
*self.py = (*self.py_orig)[ygroups]
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::select_range,  xlo = xlo,    $
                  xhi = xhi,   $
                  ylo = ylo,   $
                  yhi = yhi,   $
                  errmsg = errmsg
errmsg = 'No errors generated in SELECT_RANGE method'
ret = self->restore_groups()    ;??? comment out - RTA
if n_elements(xlo) eq 0 then xlo = min(*self.px)
if n_elements(xhi) eq 0 then xhi = max(*self.px)
if n_elements(ylo) eq 0 then ylo = min(*self.py)
if n_elements(yhi) eq 0 then yhi = max(*self.py)
xxlo = xlo < xhi
xxhi = xlo > xhi
xlo = xxlo & xhi = xxhi
yylo = ylo < yhi
yyhi = ylo > yhi
ylo = yylo & yhi = yyhi

xrange = where((*self.px le xhi) and (*self.px ge xlo),countx)
yrange = where((*self.py le yhi) and (*self.py ge ylo),county)
if countx le 1 then begin
   errmsg = 'Selected x-range is too small'
   return,0
endif
if county le 1 then begin
   errmsg = 'Selected y-range is too small'
   return,0
endif
ret = self->select_groups( xgroups = xrange,    $
               ygroups = yrange,     $
               errmsg = errmsg)
if ret eq 0 then begin
   return,0
endif
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::plot_image,_Extra = extra
position = self.position
ret = self->restore_groups()    ;??? comment out - RTA
if extra.autoscale eq 1 then begin
   x = *self.px & y = *self.py
   dely = max(y)-min(y) & delx = max(x)-min(x)
   xr = [min(x)-0.1*delx,max(x)+0.1*delx]
   yr = [min(y)-0.1*dely,max(y)+0.1*dely]
endif else begin
   xr = extra.xrange
   yr = extra.yrange
endelse
xrange = xr & yrange = yr
self.xrange = xrange & self.yrange = yrange
ret = self->select_range(  xlo = xrange[0],    $
               xhi = xrange[1],    $
               ylo = yrange[0],    $
               yhi = yrange[1],    $
               errmsg = errmsg)
if ret eq 0 then begin
   ret = self->restore_groups() ;??? comment out - RTA
   return,0
endif
nx = n_elements(*self.px) & ny = n_elements(*self.py)
if (nx lt 3) or (ny lt 3) then return,0
erase
;tvimage,bytscl(*self.pqty),position = position,/nointerpolation
;plot,*self.px,*self.py,/nodata,/noerase,xrange = [min(*self.px),max(*self.px)], $
;  yrange = [min(*self.py),max(*self.py)], $
;  /xstyle,/ystyle,position = position, $
;  xtitle = self.xlabel, ytitle = self.ylabel
x = *self.px & y = *self.py
; New here (05/28/03)
xm = [0.0,0.5*(x[1:nx-1]+x[0:nx-2])]
xp = [0.5*(x[1:nx-1]+x[0:nx-2]),0.0]
dx = fltarr(nx)
dx[0] = 2.0*(xp[0]-x[0])
dx[nx-1] = 2.0*(x[nx-1]-xm[nx-1])
dx[1:nx-2] = xp[1:nx-2]-xm[1:nx-2]
xm[0] = xm[1]-dx[0]
xp[nx-1] = xm[nx-1]+dx[nx-1]


ym = [0.0,0.5*(y[1:ny-1]+y[0:ny-2])]
yp = [0.5*(y[1:ny-1]+y[0:ny-2]),0.0]
dy = fltarr(ny)
dy[0] = 2.0*(yp[0]-y[0])
dy[ny-1] = 2.0*(y[ny-1]-ym[ny-1])
dy[1:ny-2] = yp[1:ny-2]-ym[1:ny-2]
ym[0] = ym[1]-dy[0]
yp[ny-1] = ym[ny-1]+dy[ny-1]

spectrogram, bytscl(*self.pqty), xm, ym, xp, yp,xrange = xr,   $
   yrange = yr, $
   /xstyle,/ystyle,position = position, $
   xtitle = self.xlabel, ytitle = self.ylabel

ret = self->restore_groups()    ;??? comment out - RTA
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro model_data::cleanup
ptr_free,self.pqty,self.perr,self.px,self.py,self.ptreatment
ptr_free,self.pqty_orig,self.perr_orig,self.px_orig,self.py_orig, $
         self.pqty_copy,self.perr_copy,self.px_copy,self.py_copy,self.fitGrpsPtr
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::extract_dave_info,daveptr,errmsg = errmsg
errmsg = 'No errors generated in extracting data from DAVE pointer'
if n_params() eq 0 then begin
   errmsg = 'You must pass in the DAVEPTR variable'
   return,0
endif

if not ptr_valid(daveptr) then begin
   errmsg = 'DAVEPTR is not a heap variable'
   return,0
endif
ret_val = get_dave_ptr_contents(          $
               daveptr,           $
               instrument = instrument,  $
               qty = qty,          $
               qtunits = qtunits,      $
               qtlabel = qtlabel,      $
               err = err,          $
               xvals = xvals,         $
               xtype = xtype,         $
               xunits = xunits,      $
               xlabel = xlabel,      $
               yvals = yvals,         $
               ytype = ytype,         $
               yunits = yunits,      $
               ylabel = ylabel,      $
               specificstr = specificstr,   $
               treatment = treatment,    $
               ermsg = errmsg         )
if ret_val ne 1 then begin
   errmsg = errmsg
   return,0
endif

self.instrument = instrument
self.pqty = ptr_new(qty)
self.perr = ptr_new(err)
self.pqty_copy = ptr_new(qty)
self.perr_copy = ptr_new(err)
self.pqty_orig = ptr_new(qty,/no_copy)
self.perr_orig = ptr_new(err,/no_copy)

self.qtunits = qtunits
self.qtlabel = qtlabel

if n_elements(xvals) gt (size(*self.pqty))[1] then begin
   nx = n_elements(xvals)
   x = 0.5*(xvals[1:nx-1]+xvals[0:nx-2])
   xvals = x
endif

self.px = ptr_new(xvals)
self.py = ptr_new(yvals)
self.px_copy = ptr_new(xvals)
self.py_copy = ptr_new(yvals)
self.fitGrpsPtr = ptr_new(indgen(n_elements(yvals)) + 1) ; all groups
self.px_orig = ptr_new(xvals,/no_copy)
self.py_orig = ptr_new(yvals,/no_copy)

self.xtype = xtype & self.xunits = xunits & self.xlabel = xlabel
self.ytype = ytype & self.yunits = yunits & self.ylabel = ylabel

xcol_pos = strpos(xunits,':')
xtitle = xlabel+'('+strmid(xunits,xcol_pos+1)+')'
self.xlabel = xtitle
ycol_pos = strpos(yunits,':')
invAngstromSym = '!3!sA!r!u!9 %!3!n!E-1!N'
if strupcase(yunits) eq 'WAVEVECTOR:A-1' then begin
   ytitle = 'Q ('+invAngstromSym+')'
endif else begin
   ytitle = ylabel+'('+strmid(yunits,ycol_pos+1)+')'
endelse
self.ylabel = ytitle

self.ptreatment = ptr_new(treatment,/no_copy)
; Now check if there is any descripter information
ret_val = get_dave_ptr_contents(            $
               daveptr,           $
               dname = dname,         $
               dunits = dunits,      $
               dlegend = dlegend,      $
               dqty = dqty,        $
               derr = derr,        $
               ermsg = errmsg)

self.descrip_def = 0
if ret_val ne 0 then begin
   self.dunits = dunits
   self.dname = dname
   self.dlegend = dlegend
   self.dqty = dqty
   self.derr = derr
   self.descrip_def = 1
endif
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function model_data::init,             $
               filename = filename,   $
               path = path,      $
               daveptr = daveptr,   $
               debug = debug,      $
               errmsg = errmsg
errmsg = 'No errors generated in model initialization module'
self.wid = 0L
if n_elements(debug) ne 0 then begin
;  filename = 'c:\dimeo\hfbs\data\ch3i\bulk\reduced\20010618_05dyn.dave'
   filename = 'c:\dimeo\hfbs\data\ch3i\bulk\reduced\20010618_04_sumdyn.dave'
endif
if n_elements(filename) ne 0 and $
   n_elements(daveptr) ne 0 then begin
   errmsg = 'You cannot specify both FILENAME and DAVEPTR'
   return,0
endif
if n_elements(filename) ne 0 then begin
   self.filename = filename
   restore,filename
   ret_val = self->extract_dave_info(daveptr,errmsg = errmsg)
   if ret_val eq 0 then begin
     return,0
   endif
   heap_free,daveptr
   ptr_free,daveptr
endif

if ((n_elements(daveptr)) ne 0) and ptr_valid(daveptr) then begin
   ret_val = self->extract_dave_info(daveptr,errmsg = errmsg)
   if ret_val eq 0 then begin
     return,0
   endif
endif
self.xrange = fltarr(2) & self.yrange = fltarr(2)
if n_elements(filename) eq 0 then filename = 'No file'
if n_elements(path) ne 0 then begin
   self.display_name = strmid(filename,strlen(path))
endif else begin
   self.display_name = 'Test data'
   path = 'No path'
endelse
self.path = path
self.position = [0.15,0.15,0.9,0.9]
return,1
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pro model_data__define
; Object class definition
define ={model_data,         $
         position:fltarr(4),   $
         filename:'',      $
         display_name:'',   $
         path:'',       $
         wid:0L,         $
         pqty:ptr_new(),      $
         perr:ptr_new(),      $
         px:ptr_new(),      $
         py:ptr_new(),      $
         pqty_orig:ptr_new(), $ ; unblemished
         perr_orig:ptr_new(), $ ; unblemished
         px_orig:ptr_new(),     $ ; unblemished
         py_orig:ptr_new(),     $ ; unblemished
         pqty_copy:ptr_new(), $ ; unblemished
         perr_copy:ptr_new(), $ ; unblemished
         px_copy:ptr_new(),     $ ; unblemished
         py_copy:ptr_new(),     $ ; unblemished
         fitGrpsPtr:ptr_new(), $
         qtunits:'',       $
         qtlabel:'',       $
         xtype:'',         $
         xlabel:'',        $
         ytype:'',         $
         ylabel:'',        $
         xunits:'',        $
         yunits:'',        $
         instrument:'',      $
         xrange:fltarr(2),    $
         yrange:fltarr(2),    $
         ptreatment:ptr_new(),   $
         descrip_def:0,      $
         dname:'',         $
         dunits:'',        $
         dlegend:'',       $
         dqty:0.0,         $
         derr:0.0       $
        }

end
