diff --git a/.ci/gitlab/setup-gitlab-runner.sh b/.ci/gitlab/setup-gitlab-runner.sh
new file mode 100644
index 0000000000000000000000000000000000000000..36b3bb7d3487e4a24e07b019b52573736cbc6f23
--- /dev/null
+++ b/.ci/gitlab/setup-gitlab-runner.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+############################################
+# Description:
+#   Manage the gitlab runners
+# Usage:
+#   $ ./setup-gitlab-runner new <TOKEN>
+#   $ ./setup-gitlab-runner start
+#
+# Register in crontab:
+#
+#   */10 * * * * $HOME/setup-github-runner.sh start >& /tmp/$USER/github-runner/start.log
+#
+# Author: Tao Lin <lintao AT ihep.ac.cn>
+############################################
+
+#############################################
+# Configuration
+#############################################
+export RUNNER_TOP_DIR=/tmp/$USER/gitlab-runner
+export SINGULARITY_BINDPATH=/cvmfs
+export RUNNER_URL=https://code.ihep.ac.cn
+
+[ -d "$RUNNER_TOP_DIR" ] || mkdir $RUNNER_TOP_DIR
+
+#############################################
+# Create a new gitlab runner (glr)
+#############################################
+
+# ./gitlab-runner register  --url https://code.ihep.ac.cn  --token XXXXXX
+
+function glr-preq() {
+    # if $HOME/gitlab-runner exists
+    if [ -f "$HOME/gitlab-runner" ]; then
+        cp $HOME/gitlab-runner .
+    else
+        curl -L --output gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
+    fi
+
+    chmod +x gitlab-runner
+
+}
+
+function glr-new() {
+    local runner_url=$1; shift
+    local token=$1; shift
+    local executor=${1:-shell}; shift
+    local shell=${1:-bash}; shift
+
+    pushd $RUNNER_TOP_DIR
+
+    # check if gitlab-runner exists
+    if [ ! -f gitlab-runner ]; then
+        glr-preq
+    fi
+
+    ./gitlab-runner register --url $runner_url --token $token --executor $executor --shell $shell
+
+    popd
+}
+
+function new() {
+    local token=$1; shift
+    if [ -z "$token" ]; then
+        echo "Please pass the token to this script" 1>&2
+        exit -1
+    fi
+    glr-new $RUNNER_URL $token
+
+}
+
+#############################################
+# Create a new gitlab runner (glr)
+#############################################
+
+function glr-start() {
+    local glr=gitlab-runner
+
+    pushd $RUNNER_TOP_DIR
+
+    apptainer instance start ~/github-runner.sif ${glr}
+    apptainer run instance://${glr} bash -c "./gitlab-runner run -c ./config.toml &"
+
+    popd
+}
+
+function start() {
+    glr-start
+}
+
+#############################################
+# Command line options
+#############################################
+
+cmd=$1; shift
+if [ -z "$cmd" ]; then
+    echo "Please specify the command to be invoked" 1>&2
+    exit -1
+fi
+
+case $cmd in
+    new)
+        new $*
+        ;;
+    start)
+        start $*
+        ;;
+    *)
+        echo "Unknown command '$cmd'" 1>&2
+        ;;
+esac
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..efd284358d4d0f3af8210fe838e90093a0103476
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,62 @@
+##############################################################################
+# CI for CEPCSW at IHEP GitLab
+##############################################################################
+
+workflow:
+  rules:
+    # These 3 rules from https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Workflows/MergeRequest-Pipelines.gitlab-ci.yml
+    # Run on merge requests
+    - if: $CI_MERGE_REQUEST_IID
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+    # Run on tags
+    - if: $CI_COMMIT_TAG
+    # Run when called from an upstream pipeline https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html?tab=Multi-project+pipeline#use-rules-to-control-downstream-pipeline-jobs
+    - if: $CI_PIPELINE_SOURCE == 'pipeline'
+    - if: $CI_PIPELINE_SOURCE == 'parent-child'
+    # Run on commits to the default branch
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    # The last rule above blocks manual and scheduled pipelines on non-default branch. The rule below allows them:
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+    # Run if triggered from Web using 'Run Pipelines'
+    - if: $CI_PIPELINE_SOURCE == "web"
+    # Run if triggered from WebIDE
+    - if: $CI_PIPELINE_SOURCE == "webide"
+
+
+
+stages:
+  - build
+  # - test
+
+##############################################################################
+# Build Template
+##############################################################################
+.build_template:
+  stage: build
+  variables:
+    LCG_RELEASE:
+    CEPCSW_BLDTOOL: ninja
+  script:
+    - bash ./.build.ci.sh
+
+
+##############################################################################
+# Build CentOS 7 (LCG)
+##############################################################################
+build:lcg:el7:
+  extends: .build_template
+  variables:
+    LCG_RELEASE: LCG
+  tags:
+    - centos7
+
+##############################################################################
+# Build CentOS 7 (KEY4HEP)
+##############################################################################
+# build:k4:el7:
+#   extends: .build_template
+#   variables:
+#     LCG_RELEASE: KEY4HEP_STACK
+#   tags:
+#     - centos7
+
diff --git a/Examples/options/detsim_tracker.py b/Examples/options/detsim_tracker.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc0ac912ccd2e7d958ced146aa3072598aee9503
--- /dev/null
+++ b/Examples/options/detsim_tracker.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+#Author: Zhan Li <lizhan@ihep.ac.cn>
+#Created [2024-03-07 Thu 14:53]
+
+import os
+import sys
+
+import Gaudi.Configuration
+from Configurables import RndmGenSvc, HepRndm__Engine_CLHEP__RanluxEngine_, k4DataSvc, GeomSvc
+from Configurables import TimeProjectionChamberSensDetTool
+from Configurables import GenAlgo
+from Configurables import GtGunTool
+from Configurables import StdHepRdr
+from Configurables import SLCIORdr
+from Configurables import HepMCRdr
+from Configurables import GenPrinter
+from Configurables import GtBeamBackgroundTool
+from Configurables import DetSimSvc
+from Configurables import DetSimAlg
+from Configurables import AnExampleDetElemTool
+from Configurables import PodioOutput
+from Configurables import ApplicationMgr
+
+seed = [42]
+
+rndmengine = Gaudi.Configuration.HepRndm__Engine_CLHEP__HepJamesRandom_("RndmGenSvc.Engine")
+rndmengine.SetSingleton = True
+rndmengine.Seeds = seed
+
+rndmgensvc = RndmGenSvc("RndmGenSvc")
+rndmgensvc.Engine = rndmengine.name()
+
+
+dsvc = k4DataSvc("EventDataSvc")
+#geometry_option = "CepC_v4-onlyVXD.xml"
+geometry_option = "CepC_v4_onlyTracker.xml"
+#geometry_option = "CepC_v4.xml"
+
+geometry_path = os.path.join(os.getenv("DETCEPCV4ROOT"), "compact", geometry_option)
+geosvc = GeomSvc("GeomSvc")
+geosvc.compact = geometry_path
+
+#Previously I do not have these 2 lines
+tpc_sensdettool = TimeProjectionChamberSensDetTool("TimeProjectionChamberSensDetTool")
+tpc_sensdettool.TypeOption = 1
+
+
+# Physics Generator
+bg = GtBeamBackgroundTool("GtBeamBackgroundTool")
+bg.InputFileMap = {"default":"/scratchfs/atlas/lizhan/cepc/CEPCSW/ToCEPCSWsingle.out"}
+#bg.InputFileMap = {"default":"/cefs/higgs/shihy/tools/CEPCSW/CEPCSW/Test/0/ToCEPCSW-1.out"}
+bg.InputBeamEnergyMap = {"default":120}
+bg.RotationAlongYMap = {"default":16.5e-3}
+
+
+genprinter = GenPrinter("GenPrinter")
+
+genalg = GenAlgo("GenAlgo")
+genalg.GenTools = ["GtBeamBackgroundTool"]
+
+detsimsvc = DetSimSvc("DetSimSvc")
+
+detsimalg = DetSimAlg("DetSimAlg")
+detsimalg.RandomSeeds = seed
+
+
+detsimalg.RunCmds = []
+detsimalg.AnaElems = [
+    "Edm4hepWriterAnaElemTool"
+]
+detsimalg.RootDetElem = "WorldDetElemTool"
+
+example_dettool = AnExampleDetElemTool("AnExampleDetElemTool")
+
+
+# POD I/O
+out = PodioOutput("outputalg")
+out.filename = "test-SIT.root"
+out.outputCommands = ["keep *"]
+
+ApplicationMgr( TopAlg = [genalg, detsimalg, out],
+                EvtSel = 'NONE',
+                EvtMax = 1,
+                ExtSvc = [rndmengine, rndmgensvc, dsvc, geosvc],
+)
\ No newline at end of file
diff --git a/Examples/options/tracker_analysis.py b/Examples/options/tracker_analysis.py
new file mode 100644
index 0000000000000000000000000000000000000000..896a31ca5a058ba80f6bb5688eab6047a9676451
--- /dev/null
+++ b/Examples/options/tracker_analysis.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+#Author: Zhan Li <lizhan@ihep.ac.cn>
+#Created [2024-03-07 Thu 14:53]
+
+import ROOT
+import math
+
+#read
+#path1="/scratchfs/atlas/lizhan/cepc/CEPCSW/test-onlyVXD-single.root"
+#path1="/scratchfs/atlas/lizhan/cepc/CEPCSW/test-detsim10.root"
+
+def GetVXDHitMapFromFile(infile_name,out_file_name):
+    file = ROOT.TFile(infile_name)
+    tree = file.Get("events")
+    posx_VXD=[]
+    posy_VXD=[]
+    posz_VXD=[]
+    posr_VXD=[]
+    for entry in tree:
+        for elem in entry.VXDCollection:
+            x=elem.position.x
+            y=elem.position.y
+            z=elem.position.z
+            r = math.sqrt(x*x + y*y)
+            posx_VXD.append(x)
+            posy_VXD.append(y)
+            posz_VXD.append(z)
+            posr_VXD.append(r)
+    file.Close()
+    VXDHitMap = ROOT.TH2F("VXDHitMap","Hit Map of VXD",40,-125,125,20,0,80)
+    for i in range(0,len(posz_VXD)):
+        VXDHitMap.Fill(posz_VXD[i],posr_VXD[i])
+    ofile=ROOT.TFile(out_file_name,"Recreate")
+    VXDHitMap.Write()
+    ofile.Close()
+    
+    
+def GetSITHitMapFromFile(infile_path,out_file_name):    
+    file = ROOT.TFile(infile_path)
+    tree = file.Get("events")
+    posx_SIT=[]
+    posy_SIT=[]
+    posz_SIT=[]
+    posr_SIT=[]
+    for entry in tree:
+        for elem in entry.SITCollection:
+            x=elem.position.x
+            y=elem.position.y
+            z=elem.position.z
+            r = math.sqrt(x*x + y*y)
+            posx_SIT.append(x)
+            posy_SIT.append(y)
+            posz_SIT.append(z)
+            posr_SIT.append(r)
+    file.Close()
+    SITHitMap = ROOT.TH2F("SITHitMap","Hit Map of SIT",200,-2400,2400,20,120,320)
+    for i in range(0,len(posz_SIT)):
+        SITHitMap.Fill(posz_SIT[i],posr_SIT[i])
+    ofile=ROOT.TFile(out_file_name,"Recreate")
+    SITHitMap.Write()
+    ofile.Close()
+
+print("test")
+
+def GetSETHitMapFromFile(infile_path,out_file_name):    
+    file = ROOT.TFile(infile_path)
+    tree = file.Get("events")
+    posx_SET=[]
+    posy_SET=[]
+    posz_SET=[]
+    posr_SET=[]
+    for entry in tree:
+        for elem in entry.SETCollection:
+            x=elem.position.x
+            y=elem.position.y
+            z=elem.position.z
+            r = math.sqrt(x*x + y*y)
+            posx_SET.append(x)
+            posy_SET.append(y)
+            posz_SET.append(z)
+            posr_SET.append(r)
+    file.Close()
+    SETHitMap = ROOT.TH2F("SETHitMap","Hit Map of SET",200,-2400,2400,20,1700,2000)
+    for i in range(0,len(posz_SET)):
+        SETHitMap.Fill(posz_SET[i],posr_SET[i])
+    ofile=ROOT.TFile(out_file_name,"Recreate")
+    SETHitMap.Write()
+    ofile.Close()
+
+
+
+#draw
+def DrawHitMap(in_file_name,TH2name,out_file_name):
+    file = ROOT.TFile(in_file_name)
+    TH2_HitMap=file.Get(TH2name)
+    c = ROOT.TCanvas("c", "c", 800, 800)
+    ROOT.gStyle.SetOptStat(0)
+    TH2_HitMap.GetXaxis().SetTitle("Z [mm]")
+    TH2_HitMap.GetXaxis().SetTitleOffset(0.77)
+    TH2_HitMap.GetXaxis().SetTitleSize(0.05)
+    TH2_HitMap.GetYaxis().SetTitle("R [mm]")
+    TH2_HitMap.GetYaxis().SetTitleOffset(0.8)
+    TH2_HitMap.GetYaxis().SetTitleSize(0.045)
+    TH2_HitMap.Draw("COL Z CJUST SAME")
+
+    c.SaveAs(out_file_name+".root")
+    c.SaveAs(out_file_name+".pdf")
+
+path1="/scratchfs/atlas/lizhan/cepc/CEPCSW/test-SIT.root"
+
+VXDOutFile="/scratchfs/atlas/lizhan/cepc/CEPCSW/VXDHM.root"
+SITOutFile="/scratchfs/atlas/lizhan/cepc/CEPCSW/SITHM.root"
+SETOutFile="/scratchfs/atlas/lizhan/cepc/CEPCSW/SETHM.root"
+GetVXDHitMapFromFile(path1,VXDOutFile)
+GetSITHitMapFromFile(path1,SITOutFile)
+GetSETHitMapFromFile(path1,SETOutFile)
+
+DrawHitMap(VXDOutFile,"VXDHitMap","Full_VXDHM_1evt")
+DrawHitMap(SITOutFile,"SITHitMap","Full_SITHM_1evt")
+DrawHitMap(SETOutFile,"SETHitMap","Full_SETHM_1evt")
\ No newline at end of file