diff --git a/.gitignore b/.gitignore
index ee77c3f..b8b8850 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
 *.py~
 *_Definition*.xml
 *.vtp
+.idea/
diff --git a/hb2b_geometry.py b/hb2b_geometry.py
new file mode 100644
index 0000000..c5c3fc9
--- /dev/null
+++ b/hb2b_geometry.py
@@ -0,0 +1,218 @@
+import sys
+import datetime
+import helper
+
+# Definition of constants
+HB3A_L1 = 2.
+
+
+HB2B_SETUP = {'L1': 2.678898,
+              'L2': 0.95,  # arm length
+              'PixelNumber': {'1K': (1024, 1024), '2K': (2048, 2048)},
+              'PixelSize': {'1K': 0.00029296875, '2K': 0.00029296875*0.5}
+              }
+
+
+HZB_SETUP = {'L1': 2.678898,
+             'L2': 1.13268,  # arm length
+             'PixelNumber': {'1K': (1024, 1024), '2K': (2048, 2048), '256': (256, 256)},
+             'PixelSize': {'1K': 0.00029296875, '2K': 0.00029296875*0.5, '256': 0.00029296875*4}
+             }
+
+XRAY_SETUP = {'L1': 2.678898,
+              'L2': 0.416,  # arm length
+              'PixelNumber': {'1K': (1024, 1024), '2K': (2048, 2048)},
+              'PixelSize': {'1K': 0.0004000, '2K': 0.0004000*0.5}
+              }
+
+
+class ResidualStressGeometry(helper.MantidGeom):
+    """
+    HB3A geometry extended from MantidGeom
+    """
+    def __init__(self, instname, comment=None, valid_from=None, valid_to=None):
+        """
+        initialization
+        :param instname: name of instrument
+        :param comment: comment
+        :param valid_from: beginning date
+        :param valid_to: end date
+        """
+        super(ResidualStressGeometry, self).__init__(instname, comment, valid_from, valid_to)
+
+        return
+
+
+# END-DEF-HB3A
+
+
+def generate_1bank_2d_idf(instrument_name, geom_setup_dict, pixel_setup, output_idf_name):
+    """ Generate the general HB2B (or similar X-Ray) IDF file from 2018.12.01
+    :param instrument_name:
+    :param geom_setup_dict:
+    :param pixel_setup:
+    :param output_idf_name:
+    :return:
+    """
+    # generate instrument geometry object
+    authors = ["Wenduo Zhou"]
+    begin_date = '2018-12-01 00:00:01'
+    end_date = '2100-10-20 23:59:59'
+
+    # boiler plate stuff
+    hb2b = ResidualStressGeometry(instrument_name,
+                                  comment="Created by " + ", ".join(authors),
+                                  valid_from=begin_date,
+                                  valid_to=end_date)
+
+    # TODO/FIXME - NO HFIR Default
+    # hb2b.addComment('DEFAULTS')
+    # hb2b.addHfirDefaults()
+
+    # source
+    hb2b.addComment("SOURCE")
+    hb2b.addModerator(geom_setup_dict['L1'])
+    # sample
+    hb2b.addComment("SAMPLE")
+    hb2b.addSamplePosition()
+
+    # TODO/FIXME - NO Default
+    # monitor
+    # hb2b.addComment("MONITORS")
+    # hb2b.add_monitor_type()
+
+    # Build 'arm'/panel/shiftpanel
+    hb2b.addComment("PANEL")
+
+    # TODO - the following part is refactored to method add_panel in hb3a_geometry
+    # define arm - component
+    pixel_row_count, pixel_column_count = geom_setup_dict['PixelNumber'][pixel_setup]
+    arm_loc_dict = dict()
+    arm_loc_dict['r-position'] = {'value': 0.0}
+    arm_loc_dict['t-position'] = {'value': 0.0}
+    arm_loc_dict['p-position'] = {'value': 0.0}
+    arm_loc_dict['roty'] = {'logfile': 'value+0.0', 'id': '2theta'}  # roty works as 2theta with experiment
+    arm_node = hb2b.add_component(type_name='arm', idfillbyfirst='x', idstart=1, idstepbyrow=pixel_column_count)
+    arm_loc_node = hb2b.add_location('bank1', arm_node, arm_loc_dict)
+
+    # define type: arm
+    arm_type_node = hb2b.add_component_type(type_name='arm')
+
+    # define component panel under type arm
+    arm_loc_dict = {'z': {'logfile': 'value+{}'.format(geom_setup_dict['L2']), 'id': 'cal::arm'},
+                    'rotx': {'logfile': 'value+0.0', 'id': 'cal::flip'},
+                    'roty': {'logfile': 'value+0.0', 'id': 'cal::roty'},
+                    'rotz': {'logfile': 'value+0.0', 'id': 'cal::spin'},
+                    }
+    arm_node = hb2b.add_component(type_name='panel', idfillbyfirst=None, idstart=None,
+                                  idstepbyrow=None, root=arm_type_node)
+    hb2b.add_location(None, arm_node, arm_loc_dict)
+
+    # add panel
+    panel_loc_dict = {'x': {'logfile': 'value', 'id': 'cal::deltax'},
+                      'y': {'logfile': 'value', 'id': 'cal::deltay'}}
+    panel_type_node = hb2b.add_component_type('panel')
+    panel_node = hb2b.add_component(type_name='shiftpanel', idfillbyfirst=None, idstart=None,
+                                    idstepbyrow=None, root=panel_type_node)
+    hb2b.add_location(None, panel_node, panel_loc_dict)
+
+    # generate rectangular detector based on 'shiftpanel'
+    pixel_size_x = pixel_size_y = geom_setup_dict['PixelSize'][pixel_setup]
+    x_start = (float(pixel_column_count)*0.5 - 0.5) * pixel_size_x
+    x_step = - pixel_size_x
+    y_start = -(float(pixel_row_count)*0.5 - 0.5) * pixel_size_y
+    y_step = pixel_size_y
+    hb2b.add_rectangular_detector(x_start=x_start, x_step=x_step, x_pixels=pixel_column_count,
+                                  y_start=y_start, y_step=y_step, y_pixels=pixel_row_count,
+                                  pixel_size_x=pixel_size_x, pixel_size_y=pixel_size_y,
+                                  pixel_size_z=0.0001)
+
+    hb2b.write_terminal()
+    hb2b.writeGeom(output_idf_name)
+
+    return
+
+
+def generate_2d_configure(instrument_name, geom_setup_dict, pixel_setup, hydra_idf_name):
+    """ Generate an ASCII file for the instrument configuration
+    Here is the example:
+    ```
+    # ASCII instrument configuration file for 2K detector (2048 x 2048)
+    arm = 0.416
+    rows = 2048
+    columns = 2048
+    pixel_size_x = 0.0002
+    pixel_size_y = 0.0002
+    ```
+    :param instrument_name:
+    :param geom_setup_dict:
+    :param pixel_setup:
+    :param hydra_idf_name:
+    :return:
+    """
+    pixel_row_count, pixel_column_count = geom_setup_dict['PixelNumber'][pixel_setup]
+
+    config_str = '# Instrument configuration file for {} ({})\n'.format(instrument_name, pixel_setup)
+    config_str += 'arm  = {}\n'.format(geom_setup_dict['L2'])
+    config_str += 'rows = {}\n'.format(pixel_row_count)
+    config_str += 'columns = {}\n'.format(pixel_column_count)
+    config_str += 'pixel_size_x = {}\n'.format(geom_setup_dict['PixelSize'][pixel_setup])
+    config_str += 'pixel_size_y = {}\n'.format(geom_setup_dict['PixelSize'][pixel_setup])
+
+    # write out
+    idf_file = open(hydra_idf_name, 'w')
+    idf_file.write(config_str)
+    idf_file.close()
+
+    return
+
+
+def main(argv):
+    """ Main
+    :param argv:
+    :return:
+    """
+    if len(argv) < 3:
+        print ('Generate HB2B IDF: {} [hb2b [xray, hzb]] [1k [2k]]'.format(argv[0]))
+        sys.exit(0)
+
+    instrument = argv[1]
+    if instrument == 'hb2b':
+        geom_setup_dict = HB2B_SETUP
+        instrument_name = 'HB2B'
+    elif instrument == 'xray':
+        geom_setup_dict = XRAY_SETUP
+        instrument_name = 'XRAY'
+    elif instrument == 'hzb':
+        geom_setup_dict = HZB_SETUP
+        instrument_name = 'HZB'
+    else:
+        print ('[ERROR] Instrument {} is not supported.'.format(instrument))
+        sys.exit(-1)
+
+    # about the number of pixels setup
+    pixel_setup = argv[2]
+    if pixel_setup == '1k':
+        pixel_setup = '1K'
+    elif pixel_setup == '2k':
+        pixel_setup = '2K'
+    else:
+        print ('[ERROR] Pixel setup {} is not supported.'.format(pixel_setup))
+
+    # create instrument
+    now = datetime.datetime.now()
+
+    output_idf_name = '{}_Definition_{:04}{:02}{:02}_{:02}{:02}.xml' \
+                      ''.format(instrument_name, now.year, now.month, now.day,
+                                now.hour, now.minute)
+    output_config_name = '{}_Definition_{:04}{:02}{:02}_{:02}{:02}.txt' \
+                         ''.format(instrument_name, now.year, now.month, now.day,
+                                   now.hour, now.minute)
+    generate_1bank_2d_idf(instrument_name, geom_setup_dict, pixel_setup, output_idf_name)
+    generate_2d_configure(instrument_name, geom_setup_dict, pixel_setup, output_config_name)
+
+    return
+
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/hb3a_geometry.py b/hb3a_geometry.py
new file mode 100644
index 0000000..ceb1dc9
--- /dev/null
+++ b/hb3a_geometry.py
@@ -0,0 +1,328 @@
+import sys
+import datetime
+import helper
+
+# Definition of constants
+HB3A_L1 = 2.
+
+# TODO - Need to separate FourCircle_256_SETUP and FourCircle_512_SETUP
+# TODO - Unify the dictioanry layout!
+FourCircle_256_SETUP = {'L1': 2.0,
+                        'L2': 0.3750,  # arm length
+                        'PixelNumber': {1: (256, 256)},  # bank: (row, col)
+                        'PixelSize': {1: 0.0001984375},  # bank: pixel size
+                        'Panel Center': {1: (0, 0)}
+                        }
+
+FourCircle_512_SETUP = {'L1': 2.0,
+                        'L2': 0.3750,  # arm length
+                        'PixelNumber': {1: (512, 512)},  # bank: (row, col)
+                        'PixelSize': {1: 0.0002265625},
+                        'Panel Center': {1: (0, 0)}
+                        }
+
+ZEBRA_SETUP = {'L1': 2.0,
+               'L2': 0.3750,  # arm length
+               'PixelNumber': {1: (256, 256)},
+               'PixelSize': {1: 0.01234567}}
+
+Y = 0.120  # distance between two panels = 512 * 0.0002265625 = 0.116
+DEMAND_SETUP = {'L1': 2.0,
+                'L2': 0.3750,
+                'Panel Center': {1: (0, -Y), 2: (0, 0), 3: (0, Y)},
+                'PixelNumber': {1: (512, 512), 2: (512, 512), 3: (512, 512)},
+                'PixelSize': {1: 0.0002265625, 2: 0.0002265625, 3: 0.0002265625}
+                }
+
+
+class DemandGeometry(helper.MantidGeom):
+    """
+    HB3A geometry extended from MantidGeom
+    """
+    def __init__(self, instname, comment=None, valid_from=None, valid_to=None):
+        """
+        initialization
+        :param instname: name of instrument
+        :param comment: comment
+        :param valid_from: beginning date
+        :param valid_to: end date
+        """
+        super(DemandGeometry, self).__init__(instname, comment, valid_from, valid_to)
+
+        return
+
+    def add_panel(self, bank_id, geom_setup_dict, cal_shift_x, cal_shift_y, cal_shift_z,
+                  cal_rotx, cal_roty, cal_rotz, component_use_bank_id, start_pixel_id=1):
+        """ Add 1 2D detector panel
+        Code is somehow shared with hb2b_geometry.generate_1bank_2d_idf
+        For Mantid IDF, the logic of applying shift to a detector is from bottom to top as
+        - shift X and Y
+        - shift Z, rotX, rotY, rotZ
+        - shift 2theta
+        In the same level, rotation shift is applied in a higher priority than linear shift
+        Args:
+            bank_id:
+            geom_setup_dict: dictionary for geometry parameters such as L2, panel center position (Y)
+            cal_shift_x: String, name of calibration shift along X-axis
+            cal_shift_y: String, name of calibration shift along Y-axis
+            cal_shift_z: String
+                log name of calibration shift along Z-axis
+            cal_rotx: Log name of shift of rotation along X axis
+            cal_roty: Log name of shift of rotation along Y axis
+            cal_rotz: Log name of shift of rotation along Z axis
+            component_use_bank_id: boolean
+                If true, then all the component name containing bank number
+            start_pixel_id: integer
+                starting pixel ID
+
+        Returns:
+        Integer
+            number of pixels in this panel
+        """
+        # Define arm component - component with roty as 2theta
+        pixel_row_count, pixel_column_count = geom_setup_dict['PixelNumber'][bank_id]
+        arm_loc_dict = dict()
+        arm_loc_dict['r-position'] = {'value': 0.0}
+        arm_loc_dict['t-position'] = {'value': 0.0}
+        arm_loc_dict['p-position'] = {'value': 0.0}
+        arm_loc_dict['roty'] = {'logfile': 'value+0.0', 'id': '2theta'}  # roty works as 2theta with experiment
+
+        # generate arm node name
+        arm_node_name = 'arm'
+        if component_use_bank_id:
+            arm_node_name += '{}'.format(bank_id)
+
+        arm_node = self.add_component(type_name=arm_node_name, idfillbyfirst=None, idstart=None,
+                                      idstepbyrow=None)
+        self.add_location('bank{}'.format(bank_id), arm_node, arm_loc_dict)
+
+        # Define arm node type: arm
+        arm_type_node = self.add_component_type(type_name=arm_node_name)
+
+        # Define component panel installed on the arm
+        # rotation along x-, y- and z- axis and shift along arm will be defined from log
+        arm_loc_dict = {'z': {'logfile': 'value+{}'.format(geom_setup_dict['L2']), 'id': cal_shift_z},
+                        'rotx': {'logfile': 'value+0.0', 'id': cal_rotx},
+                        'roty': {'logfile': 'value+0.0', 'id': cal_roty},
+                        'rotz': {'logfile': 'value+0.0', 'id': cal_rotz},
+                        }
+        # Add component-panel to arm-type-node
+        # generate name for panel
+        panel_node_name = 'panel'
+        if component_use_bank_id:
+            panel_node_name += '{}'.format(bank_id)
+        # add panel as a component
+        panel_node = self.add_component(type_name=panel_node_name, idfillbyfirst=None, idstart=None,
+                                        idstepbyrow=None, root=arm_type_node)
+        # Add location to panel node
+        self.add_location(None, panel_node, arm_loc_dict)
+
+        # Add another layer: shift-panel on to panel for shift in X- and Y- direction
+        center_y = geom_setup_dict['Panel Center'][bank_id][1]
+        panel_loc_dict = {'x': {'logfile': 'value', 'id': cal_shift_x},
+                          'y': {'logfile': '{}+value'.format(center_y), 'id': cal_shift_y}}
+        # add  panel as component type
+        panel_type_node = self.add_component_type(panel_node_name)
+
+        # Add component shift_panel
+        shift_panel_node = self.add_component(type_name='shiftpanel', idfillbyfirst='x', idstart=start_pixel_id,
+                                              idstepbyrow=pixel_column_count, root=panel_type_node)
+        self.add_location(None, shift_panel_node, panel_loc_dict)
+
+        return pixel_row_count * pixel_column_count
+
+# END-DEF-HB3A
+
+
+def generate_1_panel_idf(is_zebra, idf_name, config_name, linear_pixel_size):
+    """ Generate the 1 panel (256 X 256) IDF valid to October 2018
+    :param is_zebra:
+    :param idf_name:
+    :param config_name:
+    :param linear_pixel_size:
+    :return:
+    """
+    if is_zebra:
+        instrument_name = 'ZEBRA'
+        geom_setup_dict = ZEBRA_SETUP
+    else:
+        instrument_name = 'HB3A'
+        if linear_pixel_size == 256:
+            geom_setup_dict = FourCircle_256_SETUP
+        elif linear_pixel_size == 512:
+            geom_setup_dict = FourCircle_512_SETUP
+        else:
+            raise RuntimeError('HB3A-1-Panel with {0} x {0} does not exist'.format(linear_pixel_size))
+
+    # generate instrument geometry object
+    authors = ["Wenduo Zhou"]
+    begin_date = '2018-12-01 00:00:01'
+    end_date = '2100-10-20 23:59:59'
+
+    # boiler plate stuff
+    scx_instrument = DemandGeometry(instrument_name,
+                                    comment="Created by " + ", ".join(authors),
+                                    valid_from=begin_date,
+                                    valid_to=end_date)
+
+    # source
+    scx_instrument.addComment("SOURCE")
+    scx_instrument.addModerator(geom_setup_dict['L1'])
+    # sample
+    scx_instrument.addComment("SAMPLE")
+    scx_instrument.addSamplePosition()
+
+    # Build 'arm'/panel/shiftpanel
+    scx_instrument.addComment("PANEL")
+    scx_instrument.add_panel(bank_id=1, geom_setup_dict=geom_setup_dict,
+                             cal_shift_x='cal::diffx', cal_shift_y='cal::diffy',
+                             cal_shift_z='cal::diffz', cal_rotx='cal::rotx',
+                             cal_roty='cal::roty', cal_rotz='cal::rotz',
+                             component_use_bank_id=False, start_pixel_id=1)
+
+    # Generate rectangular detector based on 'shiftpanel'
+    # TODO - refactor this to a method (not ASAP)
+    pixel_size_x = pixel_size_y = geom_setup_dict['PixelSize'][1]
+    pixel_row_count, pixel_column_count = geom_setup_dict['PixelNumber'][1]
+    x_start = (float(pixel_column_count)*0.5 - 0.5) * pixel_size_x
+    x_step = - pixel_size_x
+    y_start = -(float(pixel_row_count)*0.5 - 0.5) * pixel_size_y
+    y_step = pixel_size_y
+    scx_instrument.add_rectangular_detector(x_start=x_start, x_step=x_step, x_pixels=pixel_column_count,
+                                            y_start=y_start, y_step=y_step, y_pixels=pixel_row_count,
+                                            pixel_size_x=pixel_size_x, pixel_size_y=pixel_size_y,
+                                            pixel_size_z=0.0001)  # pixel size Z is not concerned
+
+    scx_instrument.write_terminal()
+    scx_instrument.writeGeom(idf_name)
+
+    return
+
+
+def generate_3_panel_idf(out_file_name):
+    """
+    generate 3 panel (3 x 4 x 256 x 256) instrument definition for 2018 October
+    upgrade
+    :return:
+    """
+    # generate instrument geometry object
+    instrument_name = 'HB3A'
+    authors = ["Wenduo Zhou"]
+    begin_date = '2018-10-20 23:59:59'
+    end_date = '3018-10-20 23:59:59'
+
+    # boiler plate stuff
+    hb3a = DemandGeometry(instrument_name,
+                          comment="Created by " + ", ".join(authors),
+                          valid_from=begin_date,
+                          valid_to=end_date)
+
+    # source
+    hb3a.addComment("SOURCE")
+    hb3a.addModerator(HB3A_L1)
+    # sample
+    hb3a.addComment("SAMPLE")
+    hb3a.addSamplePosition()
+    # monitor: No monitor
+    # hb3a.addComment("MONITORS")
+    # hb3a.add_monitor_type()
+    # self._vulcan.addMonitors(distance=[-1.5077], names=["monitor1"])
+
+    # Build 'arm'/panel/shiftpanel
+    # TODO FIXME - shall be: 1 arm, 3 panel
+    hb3a.addComment("PANEL Lower")
+    bank1_num_pixels = hb3a.add_panel(bank_id=1, geom_setup_dict=DEMAND_SETUP,
+                                      cal_shift_x='cal::diffx1', cal_shift_y='cal::diffy1',
+                                      cal_shift_z='cal::diffz1', cal_rotx='cal::rotx1',
+                                      cal_roty='cal::roty1', cal_rotz='cal::rotz1',
+                                      component_use_bank_id=True, start_pixel_id=1)
+
+    hb3a.addComment("PANEL Middle")
+    bank2_num_pixels = hb3a.add_panel(bank_id=2, geom_setup_dict=DEMAND_SETUP,
+                                      cal_shift_x='cal::diffx2', cal_shift_y='cal::diffy2',
+                                      cal_shift_z='cal::diffz2', cal_rotx='cal::rotx2',
+                                      cal_roty='cal::roty2', cal_rotz='cal::rotz2',
+                                      component_use_bank_id=True, start_pixel_id=1+bank1_num_pixels)
+
+    hb3a.addComment("PANEL Upper")
+    hb3a.add_panel(bank_id=3, geom_setup_dict=DEMAND_SETUP,
+                   cal_shift_x='cal::diffx3', cal_shift_y='cal::diffy3',
+                   cal_shift_z='cal::diffz3', cal_rotx='cal::rotx3',
+                   cal_roty='cal::roty3', cal_rotz='cal::rotz3',
+                   component_use_bank_id=True, start_pixel_id=1 + bank1_num_pixels + bank2_num_pixels)
+
+    # Generate rectangular detector based on 'shiftpanel'
+    # TODO - refactor this to a method (not ASAP)
+    pixel_size_x = pixel_size_y = DEMAND_SETUP['PixelSize'][1]
+    pixel_row_count, pixel_column_count = DEMAND_SETUP['PixelNumber'][1]
+    x_start = (float(pixel_column_count)*0.5 - 0.5) * pixel_size_x
+    x_step = - pixel_size_x
+    y_start = -(float(pixel_row_count)*0.5 - 0.5) * pixel_size_y
+    y_step = pixel_size_y
+    hb3a.add_rectangular_detector(x_start=x_start, x_step=x_step, x_pixels=pixel_column_count,
+                                  y_start=y_start, y_step=y_step, y_pixels=pixel_row_count,
+                                  pixel_size_x=pixel_size_x, pixel_size_y=pixel_size_y,
+                                  pixel_size_z=0.0001)  # pixel size Z is not concerned
+
+    # Write out HB3A
+    hb3a.write_terminal()
+    hb3a.writeGeom(out_file_name)
+
+    return
+
+
+def main(argv):
+    """ Main
+    :param argv:
+    :return:
+    """
+    if len(argv) < 2:
+        print ('Generate HB3A IDF: {} [number of panel [1, 3, zebra] [pixel size (default)]'.format(argv[0]))
+        sys.exit(0)
+    else:
+        is_zebra = argv[1].lower() == 'zebra'
+        if is_zebra:
+            instrument_name = 'ZEBRA'
+        else:
+            instrument_name = 'HB3A'
+
+    # about output file
+    now = datetime.datetime.now()
+    output_idf_name = '{}_Definition_{:04}{:02}{:02}_{:02}{:02}.xml' \
+                      ''.format(instrument_name, now.year, now.month, now.day,
+                                now.hour, now.minute)
+    instrument_config_name = '{}_Definition_{:04}{:02}{:02}_{:02}{:02}.txt' \
+                             ''.format(instrument_name, now.year, now.month, now.day, now.hour, now.minute)
+
+    if is_zebra:
+        generate_1_panel_idf(is_zebra, output_idf_name, instrument_config_name, None)
+    else:
+        num_panel = int(argv[1])
+
+        if num_panel == 1:
+            # 1 panel case: need to find out if it is 256 x 256
+            if len(argv) == 3:
+                linear_pixel_number = int(argv[2])
+                if linear_pixel_number not in [256, 512]:
+                    print ('[ERROR] HB3A 1-panel only supports 256(x256) and 512(x512) but not {}(x{})'
+                           ''.format(linear_pixel_number, linear_pixel_number))
+
+            generate_1_panel_idf(is_zebra, output_idf_name, instrument_config_name, linear_pixel_number)
+        elif num_panel == 3:
+            # 3 panels: DEMAND
+            generate_3_panel_idf(output_idf_name)
+        else:
+            # Non-support
+            print ('Panel configuration {} is not supported'.format(argv[1]))
+            sys.exit(-1)
+    # END-IF-ELSE
+
+    print ('[REPORT] {} and {} are generated for {}'.format(output_idf_name, instrument_config_name, instrument_name))
+
+    return
+
+
+if __name__ == '__main__':
+    main(sys.argv)
+
+
diff --git a/helper.py b/helper.py
index 7ff426f..32383e6 100644
--- a/helper.py
+++ b/helper.py
@@ -11,7 +11,8 @@
 XSI = "http://www.w3.org/2001/XMLSchema-instance"
 SCHEMA_LOC = "http://www.mantidproject.org/IDF/1.0 http://schema.mantidproject.org/IDF/1.0/IDFSchema.xsd"
 
-class MantidGeom:
+
+class MantidGeom(object):
 
     def __init__(self, instname, comment=None, valid_from=None, valid_to=None):
         from datetime import datetime
@@ -36,6 +37,15 @@ def __init__(self, instname, comment=None, valid_from=None, valid_to=None):
             else:
                 self.__root.append(le.Comment(comment))
 
+    def write_terminal(self):
+        """
+        write the current content to terminal
+        :return:
+        """
+        print (le.tostring(self.__root, pretty_print=True, xml_declaration=True))
+
+        return
+
     def writeGeom(self, filename):
         """
         Write the XML geometry to the given filename
@@ -246,15 +256,138 @@ def addComponent(self, type_name, idlist=None, root=None, blank_location=True):
             root = self.__root
 
         if idlist is not None:
-            comp = le.SubElement(root, "component", type=type_name,
-                                 idlist=idlist)
+            comp = le.SubElement(root, "component", type=type_name, idlist=idlist)
         else:
             comp = le.SubElement(root, "component", type=type_name)
+
         l=comp
         if blank_location:
             l = le.SubElement(comp, "location")
         return l
 
+    def add_component(self, type_name, idfillbyfirst, idstart, idstepbyrow, root=None):
+        """ add an component
+        :param type_name:
+        :param idfillbyfirst:
+        :param idstart:
+        :param idstepbyrow:
+        :param root:
+        :return:
+        """
+        if root is None:
+            root = self.__root
+
+        if idfillbyfirst is None:
+            comp = le.SubElement(root, 'component', type=type_name)
+        else:
+            comp = le.SubElement(root, 'component', type=type_name,
+                                 idfillbyfirst='{}'.format(idfillbyfirst),
+                                 idstart='{}'.format(idstart),
+                                 idstepbyrow='{}'.format(idstepbyrow))
+
+        return comp
+
+    def add_location(self, location_name, node, location_param_dict):
+        """ add location to a component
+        :param location_name:
+        :param node: node where location is belonged to
+        :param location_param_dict: dictionary for position parameters including parameters
+        :return:
+        """
+        arg_dict = dict()
+        if location_name is None:
+            pass
+            # location_node = le.SubElement(node, 'location')
+        else:
+            arg_dict['name'] = location_name
+            # location_node = le.SubElement(node, 'location', name=location_name)
+
+        # Create location node with "location name" as an option
+        location_node = le.SubElement(node, 'location', **arg_dict)
+
+        for param_name in sorted(location_param_dict.keys()):
+            if 'value' in location_param_dict[param_name]:
+                # write location parameter value directly
+                param_value = location_param_dict[param_name]['value']
+                self.add_parameter(param_name, param_value, location_node)
+            elif 'logfile' in location_param_dict[param_name]:
+                # write location parameter with value associated with log in workspace's run object: logfile
+                log_equation = location_param_dict[param_name]['logfile']
+                log_name = location_param_dict[param_name]['id']
+                self.add_log_file(param_name, log_equation, log_name, location_node)
+            else:
+                raise RuntimeError('Either value or logfile must be in location parameter dict')
+            # END-IF-ELSE
+        # END-FOR
+
+        return location_node
+
+    def add_parameter(self, par_name, par_value, node):
+
+        param_node = le.SubElement(node, 'parameter', name=par_name)
+        le.SubElement(param_node, 'value', val='{}'.format(par_value))
+
+        return param_node
+
+    def add_log_file(self, par_name, log_equation, log_id, root_node):
+
+        log_file_node = le.SubElement(root_node, 'parameter', name=par_name)
+        le.SubElement(log_file_node, 'logfile', eq=log_equation, id=log_id)
+
+        return log_file_node
+
+    def add_component_type(self, type_name):
+        """ Add panel
+        """
+        # TODO - TONIGHT 0 - Merge with add_type
+        type_node = self.add_type({'name': type_name})
+
+        return type_node
+
+    def add_rectangular_detector(self, x_start, x_step, x_pixels,
+                                 y_start, y_step, y_pixels,
+                                 pixel_size_x, pixel_size_y, pixel_size_z,
+                                 panel_name='shiftpanel'):
+        """
+        Add an angular detector in rectangular shape
+        :param x_start:
+        :param x_step:
+        :param x_pixels:
+        :param y_start:
+        :param y_step:
+        :param y_pixels:
+        :param pixel_size_x:
+        :param pixel_size_y:
+        :param pixel_size_z:
+        :param panel_name: name in the XML for panel
+        :return:
+        """
+        # add detector panel
+        self.addRectangularDetector(name=panel_name, type='pixel',
+                                    xstart='{}'.format(x_start), xstep='{}'.format(x_step),
+                                    xpixels='{}'.format(x_pixels),
+                                    ystart='{}'.format(y_start), ystep='{}'.format(y_step),
+                                    ypixels='{}'.format(y_pixels))
+
+        # add pixels
+        self.addCuboidPixel(name='pixel', shape_id='pixel-shape',
+                            lfb_pt=(pixel_size_x*0.5, -pixel_size_y*0.5, 0),  # left-front-bottom
+                            lft_pt=(pixel_size_x*0.5, pixel_size_y*0.5, 0),  # left-front-top
+                            lbb_pt=(pixel_size_x*0.5, -pixel_size_y*0.5, -pixel_size_z),  # left-back-bottom
+                            rfb_pt=(-pixel_size_x*0.5, -pixel_size_y*0.5, 0)  # right-front-bottom
+                            )
+
+        return
+
+    def add_type(self, type_info_dict, root_node=None):
+
+        if root_node is None:
+            root_node = self.__root
+
+        type_node = le.SubElement(root_node, 'type', name=type_info_dict['name'])
+
+        return type_node
+
     def addComponentILL(self, type_name, x, y, z, isType=None, root=None):
         """
         Add a component with location to the XML definition.