This document describes the object-oriented concepts used to implement scientific data analysis tasks, mainly in the context of the HESSI data analysis software. It also describes the method to implement data analysis tasks using frameworks. This description assumes an IDL solarsoft environment.
font-family : Myriad Web, Geneva, Arial, Helvetica,sans-serif; The HESSI data analysis software uses object classes to stepwise reconstruct images, spectra and lightcurves from raw data. Each step of the construction is associated with an object class. In the context discussed here, an object class includes a primary data type, a process method, control and information parameters, accessor methods and display methods.
An instance of an object class must be first created before it can be used:
IDL> o = Obj_New( 'class_name' )
An IDL object reference variable o is returned from the instance creation. Data and methods associated with the object can be accessed with this variable. During the instance creation, default values are set (i.e. assigned) to control parameters. Often, a constructor function is used to create the object reference:
IDL>o=class_name()
Primary data requests are handled by the accessor method GetData:
IDL>data = o->GetData( )
where o is the object reference. The object first checks whether its state is consistent, that is, if the primary data contained in the object corresponds to the values of the control parameters. If the object state is consistent, GetData returns (i.e. passes to the client that issued the request) the data that is contained in the "memory" of the object. If the object state is not consistent, the GetData method calls first the Process method, which puts the object back into a consistent state by running the transformation procedure associated with the class. After the transformation procedure has completed, the data is returned to the client.
Although the Process method can be defined fully arbitrarily, it usually consists of four steps.
Object classes can be divided into abstract and concrete classes.
Abstract classes are never used alone. Their existence (as good parents!) makes sense only through concrete classes (their childs).
For HESSI, concrete classes are divided into several groups.
All HESSI classes have a common parent class. This common parent class is called Framework. This class contains the code of the generic methods Get, GetData, Set, SetData, Plot, Write, Print. Because all classes inherit Framework, they implement the same interface. In other words, the same accessor and display methods are available for each class. As a result, and because each processing step reads data from a source object which implements the same parent class (a sibling object), the software actually reuses at all levels the same code of a single accessor method.
The data analysis process consist of a chain of transformation processed by sibling objects. A given class gets data from a source class, and passes data to a client class. The last client, of course, is the user.
Classes can be organized in well-defined design patterns. For HESSI, the imaging algorithms implement the template method pattern. An abstract class hsi_image_alg is common to all imaging algorithms. A (small) concrete class implements a so-called hook that contains the actual image algorithm (clean, mem, pixon, forward fit). All other processing steps are common and therefore implemented in the abstract class. In this way, the client object does not need to care what it uses for imaging algorithm. It only knows that it has for source an object with parent class of type hsi_image_alg
This describes how you can use a framework for your own development. Let's assume you have the implementation of an algorithm and you want to manage it using a framework. First, why do you want that? There are several reasons.
Let's assume you have implemented an algorithm algo that you want to manage with a framework. algo has the following delaration:
PRO Algo, input, output, param1, param2, out_param1, out_param2, KEYWORD1=keyword1, KEYWORD2=keyword2
where input is a variable containing the data to be processed by the algorithm (source data), output is the data generated by the algorithm, param1 and param2 are input (control) parameters, and out_param1 and out_param2 are output (info) parameters. Last, keyword1 and keyword2 are keyword values that can be set by users at run time.
There are a number of IDL templates ready to help you in the implementation. You can find these templates in your SSW installation in the directory $SSW/hessi/idl/objects, or you can get them here: framework_template__define.pro framework_template_control__define.pro, framework_template_control.pro, framework_template_info__define.pro.
Here is how you deal with it:
PRO Algo_Control__Define
struct = {algo_control, param1: 0L, param2: 0.}
END
FUNCTION Algo_Control
var = {algo_control}
var.param1 = 1002L
var.param2 = !pi
RETURN, var
PRO Algo_Info__Define
struct = {algo_info, out_param1: 0B, out_param2: 0D}
END
FUNCTION Algo::INIT, SOURCE = source, _EXTRA=_extra
IF NOT Obj_Valid( source ) THEN BEGIN
source = obj_new( 'algo_source' )
ENDIF
ret=self->Framework::INIT( CONTROL = framework_template_control(), $
INFO={framework_template_info}, $
SOURCE=source, $
_EXTRA=_extra )
RETURN, ret
END
PRO Algo::Process, KEYWORD1=keyword1, KEYWORD2=keyword2
param1 = self->Get( /PARAM1 )
param2 = self->Get( /PARAM2 )
source = self->Get( /SOURCE )
data = source->GetData( _EXTRA = _extra )
Algo, input, output, param1, param2, $
out_param1, out_param2, $
KEYWORD1=keyword1, KEYWORD2=keyword2
self->SetData, output
self->Set, OUT_PARAM1 = out_param1
self->Set, OUT_PARAM2 = out_param2
END
FUNCTION Algo::GetData, $
THIS_X_SLICE=this_x_slice, $
THIS_Y_SLICE=this_y_slice, $
THIS_Z_SLICE=this_z_slice, $,br>
_EXTRA=_extra
data=self->Framework::GetData( _EXTRA = _extra )
; actually this could be done more nicely, but anyway...
IF Keyword_Set( THIS_X_SLICE ) THEN BEGIN
RETURN, data[this_x_slice,*,*]
ENDIF ELSE IF Keyword_Set( THIS_Y_SLICE ) THEN BEGIN
RETURN, data[*,this_y_slice,*]
ENDIF ELSE IF Keyword_Set( THIS_Z_SLICE ) THEN BEGIN
RETURN, data[*, *, this_z_slice ]
ENDIF
RETURN, data
PRO Algo::Set, $
PARAMETER=parameter, $
_EXTRA=_extra
IF Keyword_Set( PARAMETER ) THEN BEGIN
; first set the parameter using the original Set
self->Framework::Set, PARAMETER = parameter
; then take some action that depends on this parameter
Take_Some_Action, parameter
ENDIF
; for all other parameters (included in _extra), just pass them to the
; original Set procedure in Framework
IF Keyword_Set( _EXTRA ) THEN BEGIN
self->Framework::Set, _EXTRA = _extra
ENDIF
END
FUNCTION Algo::Get, $
NOT_FOUND=NOT_found, $
FOUND=found, $
PARAMETER=parameter, $
_EXTRA=_extra
IF Keyword_Set( PARAMETER ) THEN BEGIN
parameter_local = self->Framework::Get( /PARAMETER )
Do_Something_With_Parameter, parameter_local
ENDIF
RETURN, self->Framework::Get( PARAMETER=parameter, $
NOT_FOUND = not_found, FOUND=found, _EXTRA = _extra )
END
This document is maintained by André Csillaghy