diff --git a/athena.xml b/athena.xml
index 128cf4c7f81467a126ff359c638fc47f7103a7c7..a896d823c484a59a49fa46a2b5ebdd8742273fdc 100644
--- a/athena.xml
+++ b/athena.xml
@@ -190,7 +190,7 @@
   <include ref="compact/ce_mrich.xml"/>
   <include ref="compact/tof_endcap.xml"/>
   <include ref="compact/forward_trd.xml"/>
-  <include ref="compact/forward_rich.xml"/>
+  <include ref="compact/gaseous_rich.xml"/>
 
   <include ref="ip6/B0_tracker.xml"/>
   <include ref="ip6/far_forward_offM_tracker.xml"/>
diff --git a/compact/forward_rich.xml b/compact/forward_rich.xml
deleted file mode 100644
index f29d6bf682ca37c36a8fee1edf644471ebc064fc..0000000000000000000000000000000000000000
--- a/compact/forward_rich.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<lccdd>
-  <define>
-    <constant name="ForwardRICH_zmin"   value="BarrelTracking_length/2.0 + ForwardTracking_length "/>
-    <constant name="ForwardRICH_rmin"   value="ForwardPID_rmin1"/>
-    <constant name="ForwardRICH_rmax0"  value="TrackerBarrel_rmax"/>
-    <constant name="ForwardRICH_rmax1"  value="EcalBarrel_rmin"/>
-    <constant name="ForwardRICH_rmax2"  value="Solenoid_rmin-6*cm"/>
-    <constant name="ForwardRICHDepth"   value="0.9*m"/>
-  </define>
-
-  <detectors>
-    <detector 
-      id="ForwardRICH_ID" 
-      name="ForwardRICH" 
-      type="refdet_ForwardRICH" 
-      readout="ForwardRICHHits" 
-      vis="RICHVis">
-      <dimensions 
-        z0="ForwardRICH_zmin" 
-        snout_length="ForwardRICH_length - ForwardRICHDepth"
-        length="ForwardRICH_length" 
-        rmin="ForwardRICH_rmin" 
-        rmax0="ForwardRICH_rmax0" 
-        rmax1="ForwardRICH_rmax1" 
-        rmax2="ForwardRICH_rmax2"/>
-      <radiator material="N2cherenkov" />
-      <tank 
-        length="ForwardRICHDepth" 
-        gas="N2cherenkov"
-        vis="GreenVis"
-        rmin="ForwardRICH_rmin"
-        rmax1="ForwardRICH_rmax1"
-        rmax2="ForwardRICH_rmax2" />
-      <comment> What are the following MCP-PMT parameters?</comment>
-      <mcppmt
-        z0="0.0*cm"
-        rmin="ForwardRICH_rmax1" 
-        rmax="ForwardRICH_rmax2" 
-        rtol="1.0*cm" 
-        vis="BlueVis"
-        module_size="10*cm" 
-        module_gap="0.2*cm" 
-        thickness="1.0*cm" 
-        material="Quartz" />
-      <mirror 
-        z0="ForwardRICH_length - 40*cm" 
-        thickness="1*mm" 
-        material="PyrexGlass" 
-        vis="GrayVis">
-        <slice focus="10*cm" curve="300*cm" rmin="ForwardRICH_rmin" rmax="ForwardRICH_rmax1" phiw="59*degree" rotz="0*degree" />
-        <slice focus="10*cm" curve="300*cm" rmin="ForwardRICH_rmin" rmax="ForwardRICH_rmax1" phiw="59*degree" rotz="60*degree" />
-        <slice focus="10*cm" curve="300*cm" rmin="ForwardRICH_rmin" rmax="ForwardRICH_rmax1" phiw="59*degree" rotz="120*degree" />
-        <slice focus="10*cm" curve="300*cm" rmin="ForwardRICH_rmin" rmax="ForwardRICH_rmax1" phiw="59*degree" rotz="180*degree" />
-        <slice focus="10*cm" curve="300*cm" rmin="ForwardRICH_rmin" rmax="ForwardRICH_rmax1" phiw="59*degree" rotz="240*degree" />
-        <slice focus="10*cm" curve="300*cm" rmin="ForwardRICH_rmin" rmax="ForwardRICH_rmax1" phiw="59*degree" rotz="300*degree" />
-      </mirror>
-    </detector>
-  </detectors>
-
-  <readouts>
-    <readout name="ForwardRICHHits">
-      <segmentation type="CartesianGridXY" grid_size_x="3*mm" grid_size_y="3*mm" />
-      <id>system:8,layer:4,piece:4,module:14,x:32:-16,y:-16</id>
-    </readout>
-  </readouts>
-
-</lccdd>
diff --git a/compact/gaseous_rich.xml b/compact/gaseous_rich.xml
new file mode 100644
index 0000000000000000000000000000000000000000..30e76efe985ceb27112097df28173a9b6e117da6
--- /dev/null
+++ b/compact/gaseous_rich.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd>
+  <define>
+    <constant name="ForwardRICH_zmin"   value="BarrelTracking_length/2.0 + ForwardTracking_length "/>
+    <constant name="ForwardRICH_rmin"   value="ForwardPID_rmin1"/>
+    <constant name="ForwardRICH_rmax0"  value="TrackerBarrel_rmax"/>
+    <constant name="ForwardRICH_rmax1"  value="EcalBarrel_rmin"/>
+    <constant name="ForwardRICH_rmax2"  value="Solenoid_rmin-6*cm"/>
+    <constant name="ForwardRICHDepth"   value="0.9*m"/>
+    <constant name="ForwardRICH_sensor_rmin"   value="ForwardRICH_rmax1 - 40.*cm"/>
+    <constant name="ForwardRICH_sensor_rmax"   value="ForwardRICH_rmax2"/>
+  </define>
+
+  <detectors>
+    <detector
+        id="ForwardRICH_ID"
+        name="GaseousRICH"
+        type="athena_GaseousRICH"
+        readout="ForwardRICHHits"
+        gas="N2cherenkov"
+        vis="RICHVis">
+      <dimensions
+        z0="ForwardRICH_zmin"
+        snout_length="ForwardRICH_length - ForwardRICHDepth"
+        length="ForwardRICH_length"
+        rmin="ForwardRICH_rmin"
+        rmax0="ForwardRICH_rmax0"
+        rmax1="ForwardRICH_rmax1"
+        rmax2="ForwardRICH_rmax2"/>
+      <mirrors thickness="1*mm" material="PyrexGlass" vis="GrayVis">
+        <position z="ForwardRICH_length - 13*cm"/>
+        <mirror curve="300*cm" rmin="ForwardRICH_rmin+5*mm" rmax="ForwardRICH_rmax1" phiw="58*degree"
+          rotz="0*degree" roty="-15*degree" rotx="0"/>
+        <mirror curve="300*cm" rmin="ForwardRICH_rmin+5*mm" rmax="ForwardRICH_rmax1" phiw="58*degree"
+          rotz="60*degree" roty="-15*degree" rotx="0"/>
+        <mirror curve="300*cm" rmin="ForwardRICH_rmin+5*mm" rmax="ForwardRICH_rmax1" phiw="58*degree"
+          rotz="120*degree" roty="-15*degree" rotx="0"/>
+        <mirror curve="300*cm" rmin="ForwardRICH_rmin+5*mm" rmax="ForwardRICH_rmax1" phiw="58*degree"
+          rotz="180*degree" roty="-15*degree" rotx="0"/>
+        <mirror curve="300*cm" rmin="ForwardRICH_rmin+5*mm" rmax="ForwardRICH_rmax1" phiw="58*degree"
+          rotz="240*degree" roty="-15*degree" rotx="0"/>
+        <mirror curve="300*cm" rmin="ForwardRICH_rmin+5*mm" rmax="ForwardRICH_rmax1" phiw="58*degree"
+          rotz="300*degree" roty="-15*degree" rotx="0"/>
+      </mirrors>
+      <sensors>
+        <module sx="10*cm" sy="10*cm" sz="1.0*cm" gap="0.2*cm" material="Quartz" vis="AnlGold"/>
+        <comment>A thin optical material to accept optical photon in simulation</comment>
+        <optical material="AirOptical" thickness="0.1*mm"/>
+        <position z="8.*cm"/>
+        <sector rmin="ForwardRICH_sensor_rmin" rmax="ForwardRICH_sensor_rmax" phiw="58*degree"
+          rotz="0*degree" roty="-15*degree" rotx="0"/>
+        <sector rmin="ForwardRICH_sensor_rmin" rmax="ForwardRICH_sensor_rmax" phiw="58*degree"
+          rotz="60*degree" roty="-15*degree" rotx="0"/>
+        <sector rmin="ForwardRICH_sensor_rmin" rmax="ForwardRICH_sensor_rmax" phiw="58*degree"
+          rotz="120*degree" roty="-15*degree" rotx="0"/>
+        <sector rmin="ForwardRICH_sensor_rmin" rmax="ForwardRICH_sensor_rmax" phiw="58*degree"
+          rotz="180*degree" roty="-15*degree" rotx="0"/>
+        <sector rmin="ForwardRICH_sensor_rmin" rmax="ForwardRICH_sensor_rmax" phiw="58*degree"
+          rotz="240*degree" roty="-15*degree" rotx="0"/>
+        <sector rmin="ForwardRICH_sensor_rmin" rmax="ForwardRICH_sensor_rmax" phiw="58*degree"
+          rotz="300*degree" roty="-15*degree" rotx="0"/>
+      </sensors>
+    </detector>
+  </detectors>
+
+  <readouts>
+    <readout name="ForwardRICHHits">
+      <segmentation type="CartesianGridXY" grid_size_x="3*mm" grid_size_y="3*mm" />
+      <id>system:8,sector:8,module:16,x:32:-16,y:-16</id>
+    </readout>
+  </readouts>
+
+</lccdd>
diff --git a/src/ForwardRICH_geo.cpp b/src/ForwardRICH_geo.cpp
deleted file mode 100644
index 0c8f73b06c8419307c5d17d3670871cdb152b7af..0000000000000000000000000000000000000000
--- a/src/ForwardRICH_geo.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-//==========================================================================
-//  Forward Ring Imaging Cherenkov Detector
-//--------------------------------------------------------------------------
-//
-// Author: C. Peng (ANL)
-// Date: 09/30/2020
-//
-//==========================================================================
-
-#include <XML/Helper.h>
-#include "TMath.h"
-#include "TString.h"
-#include "GeometryHelpers.h"
-#include "Math/Point2D.h"
-#include "DDRec/Surface.h"
-#include "DDRec/DetectorData.h"
-#include "DD4hep/OpticalSurfaces.h"
-#include "DD4hep/DetFactoryHelper.h"
-#include "DD4hep/Printout.h"
-
-using namespace std;
-using namespace dd4hep;
-using namespace dd4hep::rec;
-
-
-// create the detector
-static Ref_t createDetector(Detector& desc, xml::Handle_t handle, SensitiveDetector sens)
-{
-    xml::DetElement detElem = handle;
-
-    std::string detName = detElem.nameStr();
-    int detID = detElem.id();
-
-    DetElement det(detName, detID);
-    xml::Component dims = detElem.dimensions();
-    xml::Component rads = detElem.child(_Unicode(radiator));
-    xml::Component mir = detElem.child(_Unicode(mirror));
-    xml::Component mcp = detElem.child(_Unicode(mcppmt));
-    xml::Component tank = detElem.child(_Unicode(tank));
-
-    // dimensions
-    double z0 = dims.z0();
-    double length = dims.length();
-    double rmin = dims.rmin();
-    double rmax0 = dims.attr<double>(_Unicode(rmax0));
-    double rmax1 = dims.attr<double>(_Unicode(rmax1));
-    double rmax2 = dims.attr<double>(_Unicode(rmax2));
-    double snout_length = dims.attr<double>(_Unicode(snout_length));
-
-    // mirror setting
-    auto mThick = mir.thickness();
-    auto mirZ = mir.attr<double>(_Unicode(z0));
-
-    // mcppmt setting
-    auto pRmin = mcp.rmin();
-    auto pRmax = mcp.rmax();
-    auto pThick = mcp.thickness();
-    auto pSize = mcp.attr<double>(_Unicode(module_size));
-    auto pGap = mcp.attr<double>(_Unicode(module_gap));
-    auto pTol = mcp.attr<double>(_Unicode(rtol));
-    auto pZ = mcp.attr<double>(_Unicode(z0));
-
-    // tank parameters
-    double tank_length = length - snout_length;
-
-    // materials
-    auto mirMat = desc.material(mir.materialStr());
-    auto gasMat = desc.material(rads.materialStr());
-    auto mcpMat = desc.material(mcp.materialStr());
-
-    double front_offset = snout_length+tank_length/2.0;
-
-    // constants
-    auto richCenterAngle = std::atan((rmin + (rmax1 - rmin)/2.)/(front_offset+mirZ));
-    //std::cout << richCenterAngle*180./M_PI << std::endl;
-
-    // an envelope for the detector
-    // use a complicated shape to avoid conflict with the other parts
-    // cone for radiator and the first set of mirrors
-    double halfLength = length/2.;
-    Cone env1(snout_length/2.0, rmin, rmax0, rmin, rmax1);
-    // envelope for detection plane
-    // Cone env2(halfLength - pZ/2., rmin, pRmax, rmin, rmax2);
-    Tube env2(rmin, pRmax + pTol + pGap + 1.0*cm, tank_length/2., 0., 2*M_PI);
-
-    UnionSolid envShape(env2, env1, Position(0., 0., -tank_length/2.-snout_length/2));
-
-    Volume envVol(detName + "_envelope", envShape, gasMat);
-    envVol.setVisAttributes(desc.visAttributes(detElem.visStr()));
-
-    // ---------------
-    // spherical mirrors inside it
-    int ilayer = 1;
-
-    // optical surface
-    OpticalSurfaceManager surfMgr = desc.surfaceManager();
-    OpticalSurface mirSurf  = surfMgr.opticalSurface("MirrorOpticalSurface");
-    // mirror slices
-    int imod = 1;
-    for (xml::Collection_t sl(mir, _Unicode(slice)); sl; ++sl, ++imod) {
-        auto focus = sl.attr<double>(_Unicode(focus));
-        auto wphi = sl.attr<double>(_Unicode(phiw));
-        auto rotZ = sl.attr<double>(_Unicode(rotz));
-        auto mRmin = sl.attr<double>(_Unicode(rmin));
-        auto mRmax = sl.attr<double>(_Unicode(rmax));
-        double curve = 0.;
-        if (sl.hasAttr(_Unicode(curve))) {
-            curve = sl.attr<double>(_Unicode(curve));
-        }
-        // geometry of mirror slice
-        PlacedVolume mirPV;
-        Volume mirVol(Form("mirror_v_dummy%d", imod));
-        mirVol.setMaterial(mirMat);
-        mirVol.setVisAttributes(desc.visAttributes(mir.visStr()));
-        // spherical mirror
-        if (curve > 0.) {
-            // somehow geant4 does not support -wphi/2. to wphi/2., so additonal rotation in Z
-            double mTheta1 = std::asin(mRmin/curve);
-            double mTheta2 = std::asin(mRmax/curve);
-            double rotY = -std::asin(focus/curve);
-            mirVol.setSolid(Sphere(curve, curve + mThick, mTheta1*1.01, mTheta2*0.99, 0., wphi));
-            // action is in a reverse order
-            Transform3D tr = Translation3D(0., 0., mirZ - front_offset)   // move for z position
-                           * RotationZ(rotZ)                            // rotate phi angle
-                           * RotationY(rotY)                            // rotate for focus point
-                           * RotationX(180*degree)
-                           * Translation3D(0., 0., -curve)              // move spherical shell to origin
-                           * RotationZ(-wphi/2.);                       // center phi angle to 0. (-wphi/2., wphi/2.)
-            mirPV = envVol.placeVolume(mirVol, tr);
-        // plane mirror
-        } else {
-            mirVol.setSolid(Tube(mRmin, mRmax, mThick/2.0, 0., wphi));
-            Transform3D tr = Translation3D(0., 0., mirZ - front_offset)   // move for z position
-                           * RotationZ(rotZ)                            // rotate phi angle
-                           * RotationZ(-wphi/2.);                       // center phi angle to 0. (-wphi/2., wphi/2.)
-            mirPV = envVol.placeVolume(mirVol, tr);
-        }
-        mirPV.addPhysVolID("layer", ilayer).addPhysVolID("module", imod);
-        DetElement mirDE(det, Form("Mirror_DE%d", imod), imod);
-        mirDE.setPlacement(mirPV);
-        SkinSurface mirSurfBorder(desc, mirDE, Form("RICHmirror%d", imod), mirSurf, mirVol);
-        mirSurfBorder.isValid();
-    }
-    ilayer++;
-
-    // ---------------
-    // photo-detector unit
-    // Fill the photo-detection plane with square shape MCP-PMTs
-    Box mcpShape1(pSize/2.0, pSize/2.0, pThick/2.0);
-    Volume mcpVol1("mcppmt_v_material", mcpShape1, mcpMat);
-
-    // a thin layer of cherenkov gas for accepting optical photons
-    Box mcpShape(pSize/2.0, pSize/2.0, pThick/2.0 + 0.1*mm);
-    Volume mcpVol("mcppmt_v", mcpShape, gasMat);
-    mcpVol.placeVolume(mcpVol1, Position(0., 0., -0.1*mm));
-
-    mcpVol.setVisAttributes(desc.visAttributes(mcp.visStr()));
-    sens.setType("photoncounter");
-    mcpVol.setSensitiveDetector(sens);
-
-    // photo-detector plane envelope
-    for (size_t ipd = 0; ipd < 6; ++ipd) {
-        double phmin = -M_PI/6.5; // added 0.5 to make it smaller
-        double phmax = M_PI/6.5;
-        Tube pdEnvShape(pRmin - pTol - pGap, pRmax + pTol + pGap, pThick/2.0 + 0.1*cm, phmin, phmax);
-        Volume pdVol("pd_envelope", pdEnvShape, desc.material("AirOptical"));
-        auto points = ref::utils::fillSquares({0., 0.}, pSize + pGap, pRmin + pTol + pGap, pRmax + pTol + pGap, phmin, phmax);
-        for (size_t i = 0; i < points.size(); ++i) {
-            auto pt = points[i];
-            auto mcpPV = pdVol.placeVolume(mcpVol, Position(pt.x(), pt.y(), 0.));
-            mcpPV.addPhysVolID("layer", ilayer).addPhysVolID("module", i + 1);
-            DetElement mcpDE(det, Form("MCPPMT_DE%d_%d", ipd + 1, i + 1), i + 1);
-            mcpDE.setPlacement(mcpPV);
-        }
-        Transform3D tr = Translation3D(0., 0., -front_offset + pZ + pThick/2.0)   // move for z position
-                        * RotationZ(ipd*M_PI/3.)        // rotate phi angle
-                        * RotationY(-richCenterAngle);  // rotate to perpendicular position
-        auto pdPV = envVol.placeVolume(pdVol, tr);
-        pdPV.addPhysVolID("layer", ilayer).addPhysVolID("piece", ipd + 1);
-    }
-    Volume motherVol = desc.pickMotherVolume(det);
-    PlacedVolume envPV = motherVol.placeVolume(envVol, Position(0, 0, z0 + front_offset));
-    envPV.addPhysVolID("system", detID);
-    det.setPlacement(envPV);
-
-    return det;
-}
-//@}
-
-// clang-format off
-DECLARE_DETELEMENT(refdet_ForwardRICH, createDetector)
-
diff --git a/src/GaseousRICH_geo.cpp b/src/GaseousRICH_geo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..38a66b8de76ede7147e64147b088712fcdf96a0e
--- /dev/null
+++ b/src/GaseousRICH_geo.cpp
@@ -0,0 +1,215 @@
+//==========================================================================
+//  Gaseous Ring Imaging Cherenkov Detector
+//--------------------------------------------------------------------------
+//
+// Author: C. Peng (ANL)
+// Date: 09/30/2020
+//
+//==========================================================================
+
+#include <XML/Helper.h>
+#include "TMath.h"
+#include "TString.h"
+#include "GeometryHelpers.h"
+#include "Math/Point2D.h"
+#include "DDRec/Surface.h"
+#include "DDRec/DetectorData.h"
+#include "DD4hep/OpticalSurfaces.h"
+#include "DD4hep/DetFactoryHelper.h"
+#include "DD4hep/Printout.h"
+
+using namespace std;
+using namespace dd4hep;
+using namespace dd4hep::rec;
+
+// headers
+void build_radiator(Detector &desc, Volume &env, xml::Component plm, const Position &offset);
+void build_mirrors(Detector &desc, DetElement &sdet, Volume &env, xml::Component plm, const Position &offset);
+void build_sensors(Detector &desc, Volume &env, xml::Component plm, const Position &offset, SensitiveDetector &sens);
+
+// helper function to get x, y, z if defined in a xml component
+template<class XmlComp>
+Position get_xml_xyz(XmlComp &comp, dd4hep::xml::Strng_t name)
+{
+    Position pos(0., 0., 0.);
+    if (comp.hasChild(name)) {
+        auto child = comp.child(name);
+        pos.SetX(dd4hep::getAttrOrDefault<double>(child, _Unicode(x), 0.));
+        pos.SetY(dd4hep::getAttrOrDefault<double>(child, _Unicode(y), 0.));
+        pos.SetZ(dd4hep::getAttrOrDefault<double>(child, _Unicode(z), 0.));
+    }
+    return pos;
+}
+
+// create the detector
+static Ref_t createDetector(Detector& desc, xml::Handle_t handle, SensitiveDetector sens)
+{
+    xml::DetElement detElem = handle;
+    std::string detName = detElem.nameStr();
+    int detID = detElem.id();
+
+    DetElement det(detName, detID);
+    xml::Component dims = detElem.dimensions();
+
+    // build a big envelope for all the components, filled with optical material to ensure transport of optical photons
+    double z0 = dims.z0();
+    double length = dims.length();
+    double rmin = dims.rmin();
+    double rmax0 = dims.attr<double>(_Unicode(rmax0));
+    double rmax1 = dims.attr<double>(_Unicode(rmax1));
+    double rmax2 = dims.attr<double>(_Unicode(rmax2));
+    double snout_length = dims.attr<double>(_Unicode(snout_length));
+    // fill envelope with radiator materials (need optical property for optical photons)
+    auto gasMat = desc.material(dd4hep::getAttrOrDefault(detElem, _Unicode(gas), "AirOptical"));
+
+    Cone snout(snout_length/2.0, rmin, rmax0, rmin, rmax1);
+    Tube tank(rmin, rmax2, (length - snout_length)/2., 0., 2*M_PI);
+    // shift the snout to the left side of tank
+    UnionSolid envShape(tank, snout, Position(0., 0., -length/2.));
+    // some reference points
+    auto snout_front = Position(0., 0., -(length + snout_length)/2.);
+    // auto snout_end = Position(0., 0., -(length - snout_length)/2.);  // tank_front
+    // auto tank_end = Position(0., 0., (length - snout_length)/2.);
+
+    Volume envVol(detName + "_envelope", envShape, gasMat);
+    envVol.setVisAttributes(desc.visAttributes(detElem.visStr()));
+
+    // sensitive detector type
+    sens.setType("photoncounter");
+
+    // @TODO: place a radiator
+    // build_radiator(desc, envVol, detElement.child(_Unicode(radiator)), snout_front);
+
+    // place mirrors
+    build_mirrors(desc, det, envVol, detElem.child(_Unicode(mirrors)), snout_front);
+
+    // place photo-sensors
+    build_sensors(desc, envVol, detElem.child(_Unicode(sensors)), snout_front, sens);
+
+    Volume motherVol = desc.pickMotherVolume(det);
+    PlacedVolume envPV = motherVol.placeVolume(envVol, Position(0, 0, z0) - snout_front);
+    envPV.addPhysVolID("system", detID);
+    det.setPlacement(envPV);
+
+    return det;
+}
+
+// @TODO: implement a radiator, now the envelope serves as the radiator
+void build_radiator(Detector &desc, Volume &env, xml::Component plm, const Position &offset)
+{
+    // place holder
+}
+
+// place mirrors
+void build_mirrors(Detector &desc, DetElement &sdet, Volume &env, xml::Component plm, const Position &offset)
+{
+    double thickness = dd4hep::getAttrOrDefault<double>(plm, _Unicode(dz), 1.*dd4hep::mm);
+    auto mat = desc.material(plm.attr<std::string>(_Unicode(material)));
+    auto vis = desc.visAttributes(plm.attr<std::string>(_Unicode(vis)));
+
+    // optical surface
+    OpticalSurfaceManager surfMgr = desc.surfaceManager();
+    auto surf = surfMgr.opticalSurface(dd4hep::getAttrOrDefault(plm, _Unicode(surface), "MirrorOpticalSurface"));
+
+    // placements
+    auto gpos = get_xml_xyz(plm, _Unicode(position)) + offset;
+    auto grot = get_xml_xyz(plm, _Unicode(position));
+    int imod = 1;
+    for (xml::Collection_t mir(plm, _Unicode(mirror)); mir; ++mir, ++imod) {
+        double rmin = mir.attr<double>(_Unicode(rmin));
+        double rmax = mir.attr<double>(_Unicode(rmax));
+        double phiw = dd4hep::getAttrOrDefault<double>(mir, _Unicode(phiw), 2.*M_PI);
+        double rotz = dd4hep::getAttrOrDefault<double>(mir, _Unicode(rotz), 0.);
+        double roty = dd4hep::getAttrOrDefault<double>(mir, _Unicode(roty), 0.);
+        double rotx = dd4hep::getAttrOrDefault<double>(mir, _Unicode(rotx), 0.);
+
+        Volume vol(Form("mirror_v_dummy%d", imod));
+        vol.setMaterial(mat);
+        vol.setVisAttributes(vis);
+        // mirror curvature
+        double curve = dd4hep::getAttrOrDefault<double>(mir, _Unicode(curve), 0.);
+        // spherical mirror
+        if (curve > 0.) {
+            double th1 = std::asin(rmin/curve);
+            double th2 = std::asin(rmax/curve);
+            vol.setSolid(Sphere(curve, curve + thickness, th1, th2, 0., phiw));
+        // plane mirror
+        } else {
+            vol.setSolid(Tube(rmin, rmax, thickness/2., 0., phiw));
+        }
+        // transforms are in a reverse order
+        Transform3D tr = Translation3D(gpos.x(), gpos.y(), gpos.z())
+                       * RotationZYX(grot.z(), grot.y(), grot.x())
+                       * RotationZ(rotz)                // rotation of the piece
+                       * RotationY(roty)                // rotation of the piece
+                       * RotationX(rotx)                // rotation of the piece
+                       * Translation3D(0., 0., -curve)  // move spherical shell to origin (no move for planes)
+                       * RotationZ(-phiw/2.);           // center phi angle to 0. (-phiw/2., phiw/2.)
+        auto pv = env.placeVolume(vol, tr);
+        DetElement de(sdet, Form("mirror_de%d", imod), imod);
+        de.setPlacement(pv);
+        SkinSurface skin(desc, de, Form("mirror_optical_surface%d", imod), surf, vol);
+        skin.isValid();
+    }
+}
+
+// place photo-sensors
+void build_sensors(Detector &desc, Volume &env, xml::Component plm, const Position &offset, SensitiveDetector &sens)
+{
+    // build sensor unit geometry
+    auto mod = plm.child(_Unicode(module));
+    double sx = mod.attr<double>(_Unicode(sx));
+    double sy = mod.attr<double>(_Unicode(sy));
+    double sz = mod.attr<double>(_Unicode(sz));
+    double gap = mod.attr<double>(_Unicode(gap));
+    auto mat = desc.material(mod.attr<std::string>(_Unicode(material)));
+    auto vis = desc.visAttributes(mod.attr<std::string>(_Unicode(vis)));
+
+    Box sensor(sx/2., sy/2., sz/2.);
+    Volume svol("sensor_v", sensor, mat);
+    svol.setVisAttributes(vis);
+
+    // a thin layer of cherenkov gas for accepting optical photons
+    auto opt = plm.child(_Unicode(optical));
+    double opthick = opt.attr<double>(_Unicode(thickness));
+    auto opmat = desc.material(opt.attr<std::string>(_Unicode(material)));
+
+    Box opshape(sx/2., sy/2., sz/2. + opthick/2.);
+    Volume opvol("sensor_v_optical", opshape, opmat);
+    opvol.placeVolume(svol, Position(0., 0., 0.));
+    opvol.setSensitiveDetector(sens);
+
+    // photo-detector plane envelope
+    auto gpos = get_xml_xyz(plm, _Unicode(position)) + offset;
+    auto grot = get_xml_xyz(plm, _Unicode(position));
+    int isec = 1;
+    for (xml::Collection_t sec(plm, _Unicode(sector)); sec; ++sec, ++isec) {
+        double rmin = sec.attr<double>(_Unicode(rmin));
+        double rmax = sec.attr<double>(_Unicode(rmax));
+        double phiw = dd4hep::getAttrOrDefault<double>(sec, _Unicode(phiw), 2.*M_PI);
+        double rotz = dd4hep::getAttrOrDefault<double>(sec, _Unicode(rotz), 0.);
+        double roty = dd4hep::getAttrOrDefault<double>(sec, _Unicode(roty), 0.);
+        double rotx = dd4hep::getAttrOrDefault<double>(sec, _Unicode(rotx), 0.);
+
+        // fill sensors to the piece
+        auto points = ref::utils::fillRectangles({0., 0.}, sx + gap, sy + gap, rmin - gap, rmax + gap, -phiw/2., phiw/2.);
+        int imod = 1;
+        for (auto &p : points) {
+            // transofrms are in a reversed order
+            Transform3D tr = Translation3D(gpos.x(), gpos.y(), gpos.z())
+                           * RotationZYX(grot.z(), grot.y(), grot.x())
+                           * RotationZ(rotz)                    // rotation of the sector
+                           * RotationY(roty)                    // rotation of the sector
+                           * RotationX(rotx)                    // rotation of the sector
+                           * Translation3D(p.x(), p.y(), 0.);   // place modules in each sector
+            auto pv = env.placeVolume(opvol, tr);
+            pv.addPhysVolID("sector", isec).addPhysVolID("module", imod++);
+        }
+    }
+}
+
+//@}
+
+// clang-format off
+DECLARE_DETELEMENT(athena_GaseousRICH, createDetector)
+
diff --git a/src/GeometryHelpers.cpp b/src/GeometryHelpers.cpp
index 201b88d6d41da7d653eef8825b3af4f0cb92e83d..c37d30bd175bfa1e1334b841fb7eb2c0115b4084 100644
--- a/src/GeometryHelpers.cpp
+++ b/src/GeometryHelpers.cpp
@@ -6,7 +6,7 @@ namespace ref::utils {
 typedef ROOT::Math::XYPoint Point;
 
 // check if a square in a ring
-inline bool in_ring(const Point &pt, double side, double rmin, double rmax, double phmin, double phmax)
+inline bool in_ring(const Point &pt, double sx, double sy, double rmin, double rmax, double phmin, double phmax)
 {
     if (pt.r() > rmax || pt.r() < rmin) {
         return false;
@@ -14,10 +14,10 @@ inline bool in_ring(const Point &pt, double side, double rmin, double rmax, doub
 
     // check four corners
     std::vector<Point> pts {
-        Point(pt.x() - side/2., pt.y() - side/2.),
-        Point(pt.x() - side/2., pt.y() + side/2.),
-        Point(pt.x() + side/2., pt.y() - side/2.),
-        Point(pt.x() + side/2., pt.y() + side/2.),
+        Point(pt.x() - sx/2., pt.y() - sy/2.),
+        Point(pt.x() - sx/2., pt.y() + sy/2.),
+        Point(pt.x() + sx/2., pt.y() - sy/2.),
+        Point(pt.x() + sx/2., pt.y() + sy/2.),
     };
     for (auto &p : pts) {
         if (p.r() > rmax || p.r() < rmin || p.phi() > phmax || p.phi() < phmin) {
@@ -28,11 +28,11 @@ inline bool in_ring(const Point &pt, double side, double rmin, double rmax, doub
 }
 
 // check if a square is overlapped with the others
-inline bool overlap(const Point &pt, double side, const std::vector<Point> &pts)
+inline bool overlap(const Point &pt, double sx, double sy, const std::vector<Point> &pts)
 {
     for (auto &p : pts) {
-        auto pn = (p - pt)/side;
-        if ((std::abs(pn.x()) < 1. - 1e-6) && (std::abs(pn.y()) < 1. - 1e-6)) {
+        auto pn = p - pt;
+        if ((std::abs(pn.x()) < (1. - 1e-6)*sx) && (std::abs(pn.y()) < (1. - 1e-6)*sy)) {
             return true;
         }
     }
@@ -40,25 +40,24 @@ inline bool overlap(const Point &pt, double side, const std::vector<Point> &pts)
 }
 
 // a helper function to recursively fill square in a ring
-void add_square(Point p, std::vector<Point> &res, double lside, double rmin, double rmax,
-                double phmin, double phmax)
+void add_rectangle(Point p, std::vector<Point> &res, double sx, double sy, double rmin, double rmax, double phmin, double phmax)
 {
     // outside of the ring or overlapping
-    if (!in_ring(p, lside, rmin, rmax, phmin, phmax) || overlap(p, lside, res)) {
+    if (!in_ring(p, sx, sy, rmin, rmax, phmin, phmax) || overlap(p, sx, sy, res)) {
         return;
     }
 
     res.emplace_back(p);
 
     // check adjacent squares
-    add_square(Point(p.x() + lside, p.y()), res, lside, rmin, rmax, phmin, phmax);
-    add_square(Point(p.x() - lside, p.y()), res, lside, rmin, rmax, phmin, phmax);
-    add_square(Point(p.x(), p.y() + lside), res, lside, rmin, rmax, phmin, phmax);
-    add_square(Point(p.x(), p.y() - lside), res, lside, rmin, rmax, phmin, phmax);
+    add_rectangle(Point(p.x() + sx, p.y()), res, sx, sy, rmin, rmax, phmin, phmax);
+    add_rectangle(Point(p.x() - sx, p.y()), res, sx, sy, rmin, rmax, phmin, phmax);
+    add_rectangle(Point(p.x(), p.y() + sy), res, sx, sy, rmin, rmax, phmin, phmax);
+    add_rectangle(Point(p.x(), p.y() - sy), res, sx, sy, rmin, rmax, phmin, phmax);
 }
 
 // fill squares
-std::vector<Point> fillSquares(Point ref, double lside, double rmin, double rmax, double phmin, double phmax)
+std::vector<Point> fillRectangles(Point ref, double sx, double sy, double rmin, double rmax, double phmin, double phmax)
 {
     // convert (0, 2pi) to (-pi, pi)
     if (phmax > M_PI) {
@@ -67,13 +66,13 @@ std::vector<Point> fillSquares(Point ref, double lside, double rmin, double rmax
     }
     // start with a seed square and find one in the ring
     // move to center
-    ref = ref - Point(int(ref.x()/lside)*lside, int(ref.y()/lside)*lside);
+    ref = ref - Point(int(ref.x()/sx)*sx, int(ref.y()/sy)*sy);
 
-    auto find_seed = [] (const Point &ref, int n, double side, double rmin, double rmax, double phmin, double phmax) {
+    auto find_seed = [] (const Point &ref, int n, double sx, double sy, double rmin, double rmax, double phmin, double phmax) {
         for (int ix = -n; ix < n; ++ix) {
             for (int iy = -n; iy < n; ++iy) {
-                Point pt(ref.x() + ix*side, ref.y() + iy*side);
-                if (in_ring(pt, side, rmin, rmax, phmin, phmax)) {
+                Point pt(ref.x() + ix*sx, ref.y() + iy*sy);
+                if (in_ring(pt, sx, sy, rmin, rmax, phmin, phmax)) {
                     return pt;
                 }
             }
@@ -82,8 +81,8 @@ std::vector<Point> fillSquares(Point ref, double lside, double rmin, double rmax
     };
 
     std::vector<Point> res;
-    ref = find_seed(ref, int(rmax/lside) + 2, lside, rmin, rmax, phmin, phmax);
-    add_square(ref, res, lside, rmin, rmax, phmin, phmax);
+    ref = find_seed(ref, int(rmax/sx) + 2, sx, sy, rmin, rmax, phmin, phmax);
+    add_rectangle(ref, res, sx, sy, rmin, rmax, phmin, phmax);
     return res;
 }
 
diff --git a/src/GeometryHelpers.h b/src/GeometryHelpers.h
index 12cf9a814a64ed978f7c8371a1e3a4c8d36c7543..028c474c833809af6fb3c605b7d956fcfe0d9153 100644
--- a/src/GeometryHelpers.h
+++ b/src/GeometryHelpers.h
@@ -7,8 +7,14 @@ namespace ref::utils {
 
 typedef ROOT::Math::XYPoint Point;
 
+// fill rectangles in a ring
+std::vector<Point> fillRectangles(Point ref, double sx, double sy, double rmin, double rmax,
+                                  double phmin = -M_PI, double phmax = M_PI);
 // fill squares in a ring
-std::vector<Point> fillSquares(Point ref, double lside, double rmin, double rmax,
-                               double phmin = -M_PI, double phmax = M_PI);
+inline std::vector<Point> fillSquares(Point ref, double size, double rmin, double rmax,
+                                      double phmin = -M_PI, double phmax = M_PI)
+{
+    return fillRectangles(ref, size, size, rmin, rmax, phmin, phmax);
+}
 
 } // ref::utils
diff --git a/src/HomogeneousCalorimeter_geo.cpp b/src/HomogeneousCalorimeter_geo.cpp
index 65fb78dddbaab2fd75d41094699b481d78d0d75a..a365801dc469d800e55682a2a63db9d24e6ed5e3 100644
--- a/src/HomogeneousCalorimeter_geo.cpp
+++ b/src/HomogeneousCalorimeter_geo.cpp
@@ -275,7 +275,7 @@ static void add_disk(Detector& desc, Assembly &env, xml::Collection_t &plm, Sens
     double phimin = dd4hep::getAttrOrDefault<double>(plm, _Unicode(phimin), 0.);
     double phimax = dd4hep::getAttrOrDefault<double>(plm, _Unicode(phimax), 2.*M_PI);
 
-    auto points = ref::utils::fillSquares({0., 0.}, modSize.x(), rmin, rmax, phimin, phimax);
+    auto points = ref::utils::fillRectangles({0., 0.}, modSize.x(), modSize.y(), rmin, rmax, phimin, phimax);
     // placement to mother
     auto pos = get_xml_xyz(plm, _Unicode(position));
     auto rot = get_xml_xyz(plm, _Unicode(rotation));