diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp
index 007ffd3cb2d64a0480042285f535bd886c75b280..a10bd3829b7e9344dbf5a8c9654cf10d59d7d720 100644
--- a/source/Lib/CommonLib/CodingStructure.cpp
+++ b/source/Lib/CommonLib/CodingStructure.cpp
@@ -274,6 +274,19 @@ void CodingStructure::clearCuPuTuIdxMap( const UnitArea &_area, uint32_t numCu,
 }
 #endif
 
+#if JVET_O0050_LOCAL_DUAL_TREE
+CodingUnit* CodingStructure::getLumaCU( const Position &pos )
+{
+  const ChannelType effChType = CHANNEL_TYPE_LUMA;
+  const CompArea &_blk = area.blocks[effChType];
+  CHECK( !_blk.contains( pos ), "must contain the pos" );
+
+  const unsigned idx = m_cuIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )];
+
+  if( idx != 0 ) return cus[idx - 1];
+  else           return nullptr;
+}
+#endif
 
 CodingUnit* CodingStructure::getCU( const Position &pos, const ChannelType effChType )
 {
@@ -285,20 +298,16 @@ CodingUnit* CodingStructure::getCU( const Position &pos, const ChannelType effCh
   if( !_blk.contains( pos ) )
 #endif
   {
-    if (parent)
-    {
 #if JVET_O0050_LOCAL_DUAL_TREE
-      if (treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA)
-      {
-        CHECK(parent->treeType != TREE_D, "wrong parent treeType ");
-      }
-#endif
-      return parent->getCU(pos, effChType);
-    }
-    else
+    //keep this check, which is helpful to identify bugs
+    if( treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA )
     {
-      return nullptr;
+      CHECK( parent == nullptr, "parent shall be valid; consider using function getLumaCU()" );
+      CHECK( parent->treeType != TREE_D, "wrong parent treeType " );
     }
+#endif
+    if( parent ) return parent->getCU( pos, effChType );
+    else         return nullptr;
   }
   else
   {
@@ -321,7 +330,10 @@ const CodingUnit* CodingStructure::getCU( const Position &pos, const ChannelType
   {
 #if JVET_O0050_LOCAL_DUAL_TREE
     if( treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA )
+    {
+      CHECK( parent == nullptr, "parent shall be valid; consider using function getLumaCU()" );
       CHECK( parent->treeType != TREE_D, "wrong parent treeType" );
+    }
 #endif
     if( parent ) return parent->getCU( pos, effChType );
     else         return nullptr;
diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h
index 17acbeb2a141c62672ebaf39bf749e6beedb5eab..8a11a3befa0e2b2fa667cf7d69c1df1cdec1c51a 100644
--- a/source/Lib/CommonLib/CodingStructure.h
+++ b/source/Lib/CommonLib/CodingStructure.h
@@ -132,6 +132,9 @@ public:
   const TransformUnit  *getTU(const Position &pos, const ChannelType _chType, const int subTuIdx = -1) const;
 
   CodingUnit     *getCU(const Position &pos, const ChannelType _chType);
+#if JVET_O0050_LOCAL_DUAL_TREE
+  CodingUnit     *getLumaCU( const Position &pos );
+#endif
   PredictionUnit *getPU(const Position &pos, const ChannelType _chType);
   TransformUnit  *getTU(const Position &pos, const ChannelType _chType, const int subTuIdx = -1);
 
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 18ae062abc939b5c2351b1f3cef785aaaa8ef1d9..254b5690a5f7b54f962404b12da958d4bf0a7c0c 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -442,7 +442,12 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
   bool           lastSegment  = false;
 
   // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
+#if JVET_O0050_LOCAL_DUAL_TREE
+  //Note: do not reset qg at chroma CU
+  if( pps.getUseDQP() && partitioner.currQgEnable() && !isChroma(partitioner.chType) )
+#else
   if( pps.getUseDQP() && partitioner.currQgEnable() )
+#endif
   {
     cuCtx.qgStart    = true;
     cuCtx.isDQPCoded = false;
@@ -453,11 +458,7 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
   }
 
   // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
-#if JVET_O0050_LOCAL_DUAL_TREE
-  if (partitioner.isSepTree(cs) && pPartitionerChroma != nullptr)
-#else
   if (CS::isDualITree(cs) && pPartitionerChroma != nullptr)
-#endif
   {
     if (pps.getUseDQP() && pPartitionerChroma->currQgEnable())
     {
@@ -635,6 +636,7 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
   cu.tileIdx = cs.picture->brickMap->getBrickIdxRsMap( currArea.lumaPos() );
 #if JVET_O0050_LOCAL_DUAL_TREE
   CHECK( cu.cs->treeType != partitioner.treeType, "treeType mismatch" );
+  int lumaQPinLocalDualTree = -1;
 #endif
 
   // Predict QP on start of quantization group
@@ -652,7 +654,15 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
   {
     const Position chromaCentral(cu.chromaPos().offset(cu.chromaSize().width >> 1, cu.chromaSize().height >> 1));
     const Position lumaRefPos(chromaCentral.x << getComponentScaleX(COMPONENT_Cb, cu.chromaFormat), chromaCentral.y << getComponentScaleY(COMPONENT_Cb, cu.chromaFormat));
+#if JVET_O0050_LOCAL_DUAL_TREE
+    //derive chroma qp, but the chroma qp is saved in cuCtx.qp which is used for luma qp
+    //therefore, after decoding the chroma CU, the cuCtx.qp shall be recovered to luma qp in order to decode next luma cu qp
+    const CodingUnit* colLumaCu = cs.getLumaCU( lumaRefPos );
+    CHECK( colLumaCu == nullptr, "colLumaCU shall exist" );
+    lumaQPinLocalDualTree = cuCtx.qp;
+#else
     const CodingUnit* colLumaCu = cs.getCU(lumaRefPos, CHANNEL_TYPE_LUMA);
+#endif
 
     if (colLumaCu) cuCtx.qp = colLumaCu->qp;
   }
@@ -665,6 +675,13 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
     cu.shareParentSize = (shareStateDec == SHARING) ? shareParentSize : partitioner.currArea().lumaSize();
 
   bool isLastCtu = coding_unit( cu, partitioner, cuCtx );
+#if JVET_O0050_LOCAL_DUAL_TREE
+  //recover cuCtx.qp to luma qp after decoding the chroma CU
+  if( pps.getUseDQP() && partitioner.isSepTree( cs ) && isChroma( cu.chType ) )
+  {
+    cuCtx.qp = lumaQPinLocalDualTree;
+  }
+#endif
 
 #if JVET_O0119_BASE_PALETTE_444
   uint32_t compBegin;
@@ -697,8 +714,19 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
   {
     cs.reorderPrevPLT(cs.prevPLT, cu.curPLTSize, cu.curPLT, cu.reuseflag, compBegin, numComp, jointPLT);
   }
+#endif
+#if JVET_O0050_LOCAL_DUAL_TREE
+  if( cu.chType == CHANNEL_TYPE_CHROMA )
+  {
+    DTRACE( g_trace_ctx, D_QP, "[chroma CU]x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Cb().x, cu.Cb().y, cu.Cb().width, cu.Cb().height, cu.qp );
+  }
+  else
+  {
 #endif
   DTRACE( g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width, cu.Y().height, cu.qp );
+#if JVET_O0050_LOCAL_DUAL_TREE
+  }
+#endif
   if (startShareThisLevel == 1)
     shareStateDec = NO_SHARE;
   return isLastCtu;
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 73b7b329e59bb17f2e873eeee931e4738fe9ac3b..f1701bb927fe18dab0d23bed430b5faf92c8ddf9 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -386,7 +386,12 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
   const CodingUnit &cu        = *cs.getCU( currArea.blocks[partitioner.chType], partitioner.chType );
 
   // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
+#if JVET_O0050_LOCAL_DUAL_TREE
+  //Note: do not reset qg at chroma CU
+  if( pps.getUseDQP() && partitioner.currQgEnable() && !isChroma( partitioner.chType ) )
+#else
   if( pps.getUseDQP() && partitioner.currQgEnable() )
+#endif
   {
     cuCtx.qgStart    = true;
     cuCtx.isDQPCoded          = false;
@@ -396,11 +401,7 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
     cuCtx.isChromaQpAdjCoded  = false;
   }
   // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
-#if JVET_O0050_LOCAL_DUAL_TREE
-  if (partitioner.isSepTree(cs) && pPartitionerChroma != nullptr)
-#else
   if (CS::isDualITree(cs) && pPartitionerChroma != nullptr)
-#endif
   {
     if (pps.getUseDQP() && pPartitionerChroma->currQgEnable())
     {
@@ -525,7 +526,18 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
   // coding unit
   coding_unit( cu, partitioner, cuCtx );
 
+#if JVET_O0050_LOCAL_DUAL_TREE
+  if( cu.chType == CHANNEL_TYPE_CHROMA )
+  {
+    DTRACE_COND( (isEncoding()), g_trace_ctx, D_QP, "[chroma CU]x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Cb().x, cu.Cb().y, cu.Cb().width, cu.Cb().height, cu.qp );
+  }
+  else
+  {
+#endif
   DTRACE_COND( ( isEncoding() ), g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width, cu.Y().height, cu.qp );
+#if JVET_O0050_LOCAL_DUAL_TREE
+  }
+#endif
   DTRACE_BLOCK_REC_COND( ( !isEncoding() ), cs.picture->getRecoBuf( cu ), cu, cu.predMode );
 }
 
@@ -1765,7 +1777,11 @@ void CABACWriter::cu_palette_info(const CodingUnit& cu, ComponentID compBegin, u
 
   if (cu.useEscape[compBegin] && cu.cs->pps->getUseDQP() && !cuCtx.isDQPCoded)
   {
+#if JVET_O0050_LOCAL_DUAL_TREE
+    if (!cu.isSepTree() || isLuma(tu.chType))
+#else
     if (!CS::isDualITree(*tu.cs) || isLuma(tu.chType))
+#endif
     {
       cu_qp_delta(cu, cuCtx.qp, cu.qp);
       cuCtx.qp = cu.qp;
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index b32a868382234ffd2bbf947dea7849274c6cf45d..cb8c2c4d2088ba85bbbe39ed60bd3db4e0144131 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -995,7 +995,21 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
   m_CABACEstimator->getCtx() = m_CurrCtx->best;
 
   // QP from last processed CU for further processing
+#if JVET_O0050_LOCAL_DUAL_TREE
+  //copy the qp of the last non-chroma CU
+  int numCUInThisNode = (int)bestCS->cus.size();
+  if( numCUInThisNode > 1 && bestCS->cus.back()->chType == CHANNEL_TYPE_CHROMA && !CS::isDualITree( *bestCS ) )
+  {
+    CHECK( bestCS->cus[numCUInThisNode-2]->chType != CHANNEL_TYPE_LUMA, "wrong chType" );
+    bestCS->prevQP[partitioner.chType] = bestCS->cus[numCUInThisNode-2]->qp;
+  }
+  else
+  {
+#endif
   bestCS->prevQP[partitioner.chType] = bestCS->cus.back()->qp;
+#if JVET_O0050_LOCAL_DUAL_TREE
+  }
+#endif
   if ((!slice.isIntra() || slice.getSPS()->getIBCFlag())
     && partitioner.chType == CHANNEL_TYPE_LUMA
     && bestCS->cus.size() == 1 && (bestCS->cus.back()->predMode == MODE_INTER || bestCS->cus.back()->predMode == MODE_IBC)
@@ -1566,14 +1580,14 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
       {
         for( int i = 0; i < bestSubCS->cus.size(); i++ )
         {
-          CHECK( bestSubCS->cus[i]->predMode != MODE_INTER, "all CUs must be inter mode in an Intra coding region (SCIPU)" );
+          CHECK( bestSubCS->cus[i]->predMode != MODE_INTER, "all CUs must be inter mode in an Inter coding region (SCIPU)" );
         }
       }
       else if( partitioner.isConsIntra() )
       {
         for( int i = 0; i < bestSubCS->cus.size(); i++ )
         {
-          CHECK( bestSubCS->cus[i]->predMode != MODE_INTRA && bestSubCS->cus[i]->predMode != MODE_IBC, "all CUs must be intra/ibc mode in an Intra coding region (SCIPU)" );
+          CHECK( bestSubCS->cus[i]->predMode == MODE_INTER, "all CUs must not be inter mode in an Intra coding region (SCIPU)" );
         }
       }
 #endif
@@ -1622,6 +1636,69 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
 #if JVET_O0050_LOCAL_DUAL_TREE
   if( chromaNotSplit )
   {
+#if JVET_O0050_LOCAL_DUAL_TREE
+    //Note: In local dual tree region, the chroma CU refers to the central luma CU's QP. 
+    //If the luma CU QP shall be predQP (no residual in it and before it in the QG), it must be revised to predQP before encoding the chroma CU
+    //Otherwise, the chroma CU uses predQP+deltaQP in encoding but is decoded as using predQP, thus causing encoder-decoded mismatch on chroma qp.
+    if( tempCS->pps->getUseDQP() )
+    {
+      //find parent CS that including all coded CUs in the QG before this node
+      CodingStructure* qgCS = tempCS;
+      bool deltaQpCodedBeforeThisNode = false;
+      if( partitioner.currArea().lumaPos() != partitioner.currQgPos )
+      {
+        int numParentNodeToQgCS = 0;
+        while( qgCS->area.lumaPos() != partitioner.currQgPos )
+        {
+          CHECK( qgCS->parent == nullptr, "parent of qgCS shall exsit" );
+          qgCS = qgCS->parent;
+          numParentNodeToQgCS++;
+        }
+
+        //check whether deltaQP has been coded (in luma CU or luma&chroma CU) before this node
+        CodingStructure* parentCS = tempCS->parent;
+        for( int i = 0; i < numParentNodeToQgCS; i++ )
+        {
+          //checking each parent
+          CHECK( parentCS == nullptr, "parentCS shall exsit" );
+          for( const auto &cu : parentCS->cus )
+          {
+            if( cu->rootCbf && !isChroma( cu->chType ) )
+            {
+              deltaQpCodedBeforeThisNode = true;
+              break;
+            }
+          }
+          parentCS = parentCS->parent;
+        }
+      }
+      
+      //revise luma CU qp before the first luma CU with residual in the SCIPU to predQP
+      if( !deltaQpCodedBeforeThisNode )
+      {
+        //get pred QP of the QG
+        const CodingUnit* cuFirst = qgCS->getCU( CHANNEL_TYPE_LUMA );
+        CHECK( cuFirst->lumaPos() != partitioner.currQgPos, "First cu of the Qg is wrong" );
+        int predQp = CU::predictQP( *cuFirst, qgCS->prevQP[CHANNEL_TYPE_LUMA] );
+        
+        //revise to predQP
+        int firstCuHasResidual = (int)tempCS->cus.size();
+        for( int i = 0; i < tempCS->cus.size(); i++ )
+        {
+          if( tempCS->cus[i]->rootCbf )
+          {
+            firstCuHasResidual = i;
+            break;
+          }
+        }
+
+        for( int i = 0; i < firstCuHasResidual; i++ )
+        {
+          tempCS->cus[i]->qp = predQp;
+        }
+      }
+    }
+#endif
     assert( tempCS->treeType == TREE_L );
     uint32_t numCuPuTu[6];
     tempCS->picture->cs->getNumCuPuTuOffset( numCuPuTu );
@@ -2333,7 +2410,12 @@ void EncCu::xCheckDQP( CodingStructure& cs, Partitioner& partitioner, bool bKeep
   bool hasResidual = false;
   for( const auto &cu : cs.cus )
   {
+#if JVET_O0050_LOCAL_DUAL_TREE
+    //not include the chroma CU because chroma CU is decided based on corresponding luma QP and deltaQP is not signaled at chroma CU
+    if( cu->rootCbf && !isChroma( cu->chType ))
+#else
     if( cu->rootCbf )
+#endif
     {
       hasResidual = true;
       break;
@@ -2359,7 +2441,12 @@ void EncCu::xCheckDQP( CodingStructure& cs, Partitioner& partitioner, bool bKeep
     // NOTE: reset QPs for CUs without residuals up to first coded CU
     for( const auto &cu : cs.cus )
     {
+#if JVET_O0050_LOCAL_DUAL_TREE
+      //not include the chroma CU because chroma CU is decided based on corresponding luma QP and deltaQP is not signaled at chroma CU
+      if( cu->rootCbf && !isChroma( cu->chType ))
+#else
       if( cu->rootCbf )
+#endif
       {
         break;
       }