diff --git a/cfg/encoder_intra_vtm.cfg b/cfg/encoder_intra_vtm.cfg
index e3cc0a9ad25e16106211814f797f69a40551b340..041ade27fedf5422edd41dd3a888a607f05f737b 100644
--- a/cfg/encoder_intra_vtm.cfg
+++ b/cfg/encoder_intra_vtm.cfg
@@ -120,6 +120,9 @@ PBIntraFast                  : 1
 FastMrg                      : 1
 AMaxBT                       : 1
 
+# Encoder optimization tools
+AffineAmvrEncOpt             : 0
+
 ### DO NOT ADD ANYTHING BELOW THIS LINE ###
 ### DO NOT DELETE THE EMPTY LINE BELOW ###
 
diff --git a/cfg/encoder_lowdelay_P_vtm.cfg b/cfg/encoder_lowdelay_P_vtm.cfg
index 9c2f8ece3e201df90b0200240d366f634222ae48..e9aa837f2171481534ac8592c269c6dc83b6a682 100644
--- a/cfg/encoder_lowdelay_P_vtm.cfg
+++ b/cfg/encoder_lowdelay_P_vtm.cfg
@@ -137,6 +137,9 @@ PBIntraFast                  : 1
 FastMrg                      : 1
 AMaxBT                       : 1
 
+# Encoder optimization tools
+AffineAmvrEncOpt             : 0
+
 ### DO NOT ADD ANYTHING BELOW THIS LINE ###
 ### DO NOT DELETE THE EMPTY LINE BELOW ###
 
diff --git a/cfg/encoder_lowdelay_vtm.cfg b/cfg/encoder_lowdelay_vtm.cfg
index 324320970afffe7c5a042636337590cdb55b0c94..0922ffc13b62dab779cd616704db7945e19d3feb 100644
--- a/cfg/encoder_lowdelay_vtm.cfg
+++ b/cfg/encoder_lowdelay_vtm.cfg
@@ -140,6 +140,9 @@ PBIntraFast                  : 1
 FastMrg                      : 1
 AMaxBT                       : 1
 
+# Encoder optimization tools
+AffineAmvrEncOpt             : 0
+
 ### DO NOT ADD ANYTHING BELOW THIS LINE ###
 ### DO NOT DELETE THE EMPTY LINE BELOW ###
 
diff --git a/cfg/encoder_randomaccess_vtm.cfg b/cfg/encoder_randomaccess_vtm.cfg
index ab7c99c0cb22fc888ec9da541acde853d45ecc60..7ca088122f6b1439b67870bb4c2adbcc64c56038 100644
--- a/cfg/encoder_randomaccess_vtm.cfg
+++ b/cfg/encoder_randomaccess_vtm.cfg
@@ -155,6 +155,9 @@ PBIntraFast                  : 1
 FastMrg                      : 1
 AMaxBT                       : 1
 
+# Encoder optimization tools
+AffineAmvrEncOpt             : 1
+
 ### DO NOT ADD ANYTHING BELOW THIS LINE ###
 ### DO NOT DELETE THE EMPTY LINE BELOW ###
 
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 611f6f82f6b5ca37a2b62ac3108be0f98d9380bc..54cfe178e2cfab8cf953d86c3f2a5ab06c59bc3b 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -262,6 +262,9 @@ void EncApp::xInitLibCfg()
 #endif
 #if JVET_M0246_AFFINE_AMVR
   m_cEncLib.setUseAffineAmvr                                     ( m_AffineAmvr );
+#endif
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  m_cEncLib.setUseAffineAmvrEncOpt                               ( m_AffineAmvrEncOpt );
 #endif
   m_cEncLib.setIBCMode                                           ( m_IBCMode );
   m_cEncLib.setIBCLocalSearchRangeX                              ( m_IBCLocalSearchRangeX );
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index ea91679e6aa8ba27564bce3f45a20baadae298ba..f31fad8636275253c227ef20c31d5251b929fa5e 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -869,6 +869,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 #endif
 #if JVET_M0246_AFFINE_AMVR
   ("AffineAmvr",                                      m_AffineAmvr,                                     false, "Eanble AMVR for affine inter mode")
+#endif
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  ("AffineAmvrEncOpt",                                m_AffineAmvrEncOpt,                               false, "Enable encoder optimization of affine AMVR")
 #endif
   ( "IBC",                                            m_IBCMode,                                           0u, "IBCMode (0x1:enabled, 0x0:disabled)  [default: disabled]")
   ( "IBCLocalSearchRangeX",                           m_IBCLocalSearchRangeX,                            128u, "Search range of IBC local search in x direction")
@@ -3155,6 +3158,10 @@ void EncAppCfg::xPrintParameter()
 #endif
 #if JVET_M0246_AFFINE_AMVR
     msg( VERBOSE, "AffineAmvr:%d ", m_AffineAmvr );
+#endif
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    m_AffineAmvrEncOpt = m_AffineAmvr ? m_AffineAmvrEncOpt : false;
+    msg( VERBOSE, "AffineAmvrEncOpt:%d ", m_AffineAmvrEncOpt );
 #endif
   }
     msg(VERBOSE, "IBC:%d ", m_IBCMode);
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 9d7b9a62ca4abd2a412fdf2c4546995ad7303a04..c91c7ec580402ba1b0a7941bb7222fb0e4404b7f 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -243,6 +243,9 @@ protected:
 #if JVET_M0246_AFFINE_AMVR
   bool      m_AffineAmvr;
 #endif
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  bool      m_AffineAmvrEncOpt;
+#endif
 
   unsigned  m_IBCMode;
   unsigned  m_IBCLocalSearchRangeX;
diff --git a/source/Lib/CommonLib/MotionInfo.h b/source/Lib/CommonLib/MotionInfo.h
index 43acd88f1ca37ff7be0488afd9133ee92bf765fe..c193228a79b0c9523180c969f7199c7e18a2fb8e 100644
--- a/source/Lib/CommonLib/MotionInfo.h
+++ b/source/Lib/CommonLib/MotionInfo.h
@@ -161,6 +161,9 @@ class GBiMotionParam
   bool       m_readOnlyAffine[2][2][33];
   Mv         m_mvAffine[2][2][33][3];
   Distortion m_distAffine[2][2][33];
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  int        m_mvpIdx[2][2][33];
+#endif
 
 public:
 
@@ -182,6 +185,9 @@ public:
     memset(m_dist, -1, 2 * 33 * sizeof(Distortion));
     memset(m_readOnlyAffine, false, 2 * 2 * 33 * sizeof(bool));
     memset(m_distAffine, -1, 2 * 2 * 33 * sizeof(Distortion));
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    memset( m_mvpIdx, 0, 2 * 2 * 33 * sizeof( int ) );
+#endif
   }
 
   void setReadMode(bool b, uint32_t uiRefList, uint32_t uiRefIdx) { m_readOnly[uiRefList][uiRefIdx] = b; }
@@ -206,16 +212,30 @@ public:
 
   Mv& getAffineMv(uint32_t uiRefList, uint32_t uiRefIdx, uint32_t uiAffineMvIdx, int bP4) { return m_mvAffine[bP4][uiRefList][uiRefIdx][uiAffineMvIdx]; }
 
-  void copyAffineMvFrom(Mv(&racAffineMvs)[3], Distortion uiDist, uint32_t uiRefList, uint32_t uiRefIdx, int bP4)
+  void copyAffineMvFrom(Mv(&racAffineMvs)[3], Distortion uiDist, uint32_t uiRefList, uint32_t uiRefIdx, int bP4
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                        , const int mvpIdx
+#endif
+  )
   {
     memcpy(m_mvAffine[bP4][uiRefList][uiRefIdx], racAffineMvs, 3 * sizeof(Mv));
     m_distAffine[bP4][uiRefList][uiRefIdx] = uiDist;
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    m_mvpIdx[bP4][uiRefList][uiRefIdx]     = mvpIdx;
+#endif
   }
 
-  void copyAffineMvTo(Mv acAffineMvs[3], Distortion& ruiDist, uint32_t uiRefList, uint32_t uiRefIdx, int bP4)
+  void copyAffineMvTo(Mv acAffineMvs[3], Distortion& ruiDist, uint32_t uiRefList, uint32_t uiRefIdx, int bP4
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                      , int& mvpIdx
+#endif
+  )
   {
     memcpy(acAffineMvs, m_mvAffine[bP4][uiRefList][uiRefIdx], 3 * sizeof(Mv));
     ruiDist = m_distAffine[bP4][uiRefList][uiRefIdx];
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    mvpIdx  = m_mvpIdx[bP4][uiRefList][uiRefIdx];
+#endif
   }
 };
 struct LutMotionCand
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index e0dfe4779d6d787859714e1b12467df7b4b07818..cb1dc56721259efdd479c56284673317f95b2bfc 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -115,6 +115,9 @@ typedef std::pair<int, int>  TrCost;
 #endif
 
 #define JVET_M0246_AFFINE_AMVR                            1
+#if JVET_M0246_AFFINE_AMVR
+#define JVET_M0247_AFFINE_AMVR_ENCOPT                     1
+#endif
 #define JVET_M0421_SPLIT_SIG                              1
 
 #define JVET_M0173_MOVE_GT2_TO_FIRST_PASS                 1 // Moving the gtr2 flag to the first coding pass
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index a34fdac1aa7579b157ad0bf5ce8ff5338ff848f0..bbfd74821a63dea93ca14b8dec8a7450abc75930 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -243,6 +243,9 @@ protected:
 #endif
 #if JVET_M0246_AFFINE_AMVR
   bool      m_AffineAmvr;
+#endif
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  bool      m_AffineAmvrEncOpt;
 #endif
   unsigned  m_IBCMode;
   unsigned  m_IBCLocalSearchRangeX;
@@ -767,6 +770,10 @@ public:
   void      setUseAffineAmvr                ( bool b )       { m_AffineAmvr = b;    }
   bool      getUseAffineAmvr                ()         const { return m_AffineAmvr; }
 #endif
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  void      setUseAffineAmvrEncOpt          ( bool b )       { m_AffineAmvrEncOpt = b;    }
+  bool      getUseAffineAmvrEncOpt          ()         const { return m_AffineAmvrEncOpt; }
+#endif
 
   void      setIBCMode                      (unsigned n)     { m_IBCMode = n; }
   unsigned  getIBCMode                      ()         const { return m_IBCMode; }
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index 1b5559dff1c59e6f5298d1f710ecffc744f132ad..65d35faf49c6580fbe3c340ae2ce1b958e9ae45e 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -3950,20 +3950,35 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
         }
         else
         {
-          xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
+          xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                                   , aaiMvpIdx[iRefList][iRefIdxTemp], affiAMVPInfoTemp[eRefPicList]
+#endif
+          );
         }
       }
       else
       {
-        xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
+        xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                                 , aaiMvpIdx[iRefList][iRefIdxTemp], affiAMVPInfoTemp[eRefPicList]
+#endif
+        );
       }
       if(pu.cu->cs->sps->getSpsNext().getUseGBi() && pu.cu->GBiIdx == GBI_DEFAULT && pu.cu->slice->isInterB())
       {
         m_uniMotions.setReadModeAffine(true, (uint8_t)iRefList, (uint8_t)iRefIdxTemp, pu.cu->affineType);
-        m_uniMotions.copyAffineMvFrom(cMvTemp[iRefList][iRefIdxTemp], uiCostTemp - m_pcRdCost->getCost(uiBitsTemp), (uint8_t)iRefList, (uint8_t)iRefIdxTemp, pu.cu->affineType);
+        m_uniMotions.copyAffineMvFrom(cMvTemp[iRefList][iRefIdxTemp], uiCostTemp - m_pcRdCost->getCost(uiBitsTemp), (uint8_t)iRefList, (uint8_t)iRefIdxTemp, pu.cu->affineType
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                                      , aaiMvpIdx[iRefList][iRefIdxTemp]
+#endif
+        );
       }
       // Set best AMVP Index
       xCopyAffineAMVPInfo( affiAMVPInfoTemp[eRefPicList], aacAffineAMVPInfo[iRefList][iRefIdxTemp] );
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+      if ( pu.cu->imv != 2 || !m_pcEncCfg->getUseAffineAmvrEncOpt() )
+#endif
       xCheckBestAffineMVP( pu, affiAMVPInfoTemp[eRefPicList], eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
 
       if ( iRefList == 0 )
@@ -4185,8 +4200,15 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
         uiBitsTemp += m_auiMVPIdxCost[aaiMvpIdxBi[iRefList][iRefIdxTemp]][AMVP_MAX_NUM_CANDS];
 
         // call Affine ME
-        xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPredBi[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, true );
+        xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPredBi[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, 
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                                 aaiMvpIdxBi[iRefList][iRefIdxTemp], aacAffineAMVPInfo[iRefList][iRefIdxTemp],
+#endif
+          true );
         xCopyAffineAMVPInfo( aacAffineAMVPInfo[iRefList][iRefIdxTemp], affiAMVPInfoTemp[eRefPicList] );
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+        if ( pu.cu->imv != 2 || !m_pcEncCfg->getUseAffineAmvrEncOpt() )
+#endif
         xCheckBestAffineMVP( pu, affiAMVPInfoTemp[eRefPicList], eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPredBi[iRefList][iRefIdxTemp], aaiMvpIdxBi[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
 
         if ( uiCostTemp < uiCostBi )
@@ -4532,13 +4554,25 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
                                            Mv              acMv[3],
                                            uint32_t&           ruiBits,
                                            Distortion&     ruiCost,
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                                           int&            mvpIdx,
+                                           const AffineAMVPInfo& aamvpi,
+#endif
                                            bool            bBi)
 {
-  if( pu.cu->cs->sps->getSpsNext().getUseGBi() && pu.cu->GBiIdx != GBI_DEFAULT && !bBi && xReadBufferedAffineUniMv(pu, eRefPicList, iRefIdxPred, acMvPred, acMv, ruiBits, ruiCost) )
+  if( pu.cu->cs->sps->getSpsNext().getUseGBi() && pu.cu->GBiIdx != GBI_DEFAULT && !bBi && xReadBufferedAffineUniMv(pu, eRefPicList, iRefIdxPred, acMvPred, acMv, ruiBits, ruiCost
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+      , mvpIdx, aamvpi
+#endif
+  ) )
   {
     return;
   }
 
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  uint32_t dirBits = ruiBits - m_auiMVPIdxCost[mvpIdx][aamvpi.numCand];
+  int bestMvpIdx   = mvpIdx;
+#endif
   const int width  = pu.Y().width;
   const int height = pu.Y().height;
 
@@ -4634,10 +4668,24 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
   // get cost with mv
   m_pcRdCost->setCostScale(0);
   uiBitsBest = ruiBits;
-  DTRACE( g_trace_ctx, D_COMMON, " (%d) xx uiBitsBest=%d\n", DTRACE_GET_COUNTER(g_trace_ctx,D_COMMON), uiBitsBest );
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  if ( pu.cu->imv == 2 && m_pcEncCfg->getUseAffineAmvrEncOpt() )
+  {
+    uiBitsBest  = dirBits + xDetermineBestMvp( pu, acMvTemp, mvpIdx, aamvpi );
+    acMvPred[0] = aamvpi.mvCandLT[mvpIdx];
+    acMvPred[1] = aamvpi.mvCandRT[mvpIdx];
+    acMvPred[2] = aamvpi.mvCandLB[mvpIdx];
+  }
+  else
+  {
+#endif
+    DTRACE( g_trace_ctx, D_COMMON, " (%d) xx uiBitsBest=%d\n", DTRACE_GET_COUNTER(g_trace_ctx,D_COMMON), uiBitsBest );
 #if JVET_M0246_AFFINE_AMVR
-  uiBitsBest += xCalcAffineMVBits( pu, acMvTemp, acMvPred, pu.cu->imv != 1 );
-  DTRACE( g_trace_ctx, D_COMMON, " (%d) yy uiBitsBest=%d\n", DTRACE_GET_COUNTER(g_trace_ctx,D_COMMON), uiBitsBest );
+    uiBitsBest += xCalcAffineMVBits( pu, acMvTemp, acMvPred, pu.cu->imv != 1 );
+    DTRACE( g_trace_ctx, D_COMMON, " (%d) yy uiBitsBest=%d\n", DTRACE_GET_COUNTER(g_trace_ctx,D_COMMON), uiBitsBest );
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  }
+#endif
 #else
   for ( int i = 0; i < mvNum; i++ )
   {
@@ -4666,7 +4714,9 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
 
   const int bufStride = pBuf->Y().stride;
   const int predBufStride = predBuf.Y().stride;
-
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  Mv prevIterMv[7][3];
+#endif
   int iIterTime;
   if ( pu.cu->affineType == AFFINEMODEL_6PARAM )
   {
@@ -4683,6 +4733,9 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
   }
   for ( int iter=0; iter<iIterTime; iter++ )    // iterate loop
   {
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    memcpy( prevIterMv[iter], acMvTemp, sizeof( Mv ) * 3 );
+#endif 
     /*********************************************************************************
      *                         use gradient to update mv
      *********************************************************************************/
@@ -4774,29 +4827,35 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
       );
     }
 #endif
-    bool bAllZero = false;
-    for ( int i = 0; i < mvNum; i++ )
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    if ( !m_pcEncCfg->getUseAffineAmvrEncOpt() )
     {
-#if JVET_M0246_AFFINE_AMVR
-      Mv deltaMv = acDeltaMv[i];
-      if ( pu.cu->imv == 2 )
+#endif
+      bool bAllZero = false;
+      for ( int i = 0; i < mvNum; i++ )
       {
-        deltaMv.roundToPrecision( MV_PRECISION_INTERNAL, MV_PRECISION_HALF );
-      }
-      if ( deltaMv.getHor() != 0 || deltaMv.getVer() != 0 )
+#if JVET_M0246_AFFINE_AMVR
+        Mv deltaMv = acDeltaMv[i];
+        if ( pu.cu->imv == 2 )
+        {
+          deltaMv.roundToPrecision( MV_PRECISION_INTERNAL, MV_PRECISION_HALF );
+        }
+        if ( deltaMv.getHor() != 0 || deltaMv.getVer() != 0 )
 #else
-      if ( acDeltaMv[i].getHor() != 0 || acDeltaMv[i].getVer() != 0 )
+        if ( acDeltaMv[i].getHor() != 0 || acDeltaMv[i].getVer() != 0 )
 #endif
-      {
-        bAllZero = false;
-        break;
+        {
+          bAllZero = false;
+          break;
+        }
+        bAllZero = true;
       }
-      bAllZero = true;
-    }
-
-    if ( bAllZero )
-      break;
 
+      if ( bAllZero )
+        break;
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    }
+#endif
     // do motion compensation with updated mv
     for ( int i = 0; i < mvNum; i++ )
     {
@@ -4824,6 +4883,29 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
              pu.cu->lumaSize(),
              *pu.cs->sps);
     }
+
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    if ( m_pcEncCfg->getUseAffineAmvrEncOpt() )
+    {
+      bool identical = false;
+      for ( int k = iter; k >= 0; k-- )
+      {
+        if ( acMvTemp[0] == prevIterMv[k][0] && acMvTemp[1] == prevIterMv[k][1] )
+        {
+          identical = pu.cu->affineType ? acMvTemp[2] == prevIterMv[k][2] : true;
+          if ( identical )
+          {
+            break;
+          }
+        }
+      }
+      if ( identical )
+      {
+        break;
+      }
+    }
+#endif
+
     xPredAffineBlk( COMPONENT_Y, pu, refPic, acMvTemp, predBuf, false, pu.cu->slice->clpRng( COMPONENT_Y ) );
 
     // get error
@@ -4834,7 +4916,21 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
     m_pcRdCost->setCostScale(0);
     uint32_t uiBitsTemp = ruiBits;
 #if JVET_M0246_AFFINE_AMVR
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    if ( pu.cu->imv == 2 && m_pcEncCfg->getUseAffineAmvrEncOpt() )
+    {
+      uiBitsTemp  = dirBits + xDetermineBestMvp( pu, acMvTemp, bestMvpIdx, aamvpi );
+      acMvPred[0] = aamvpi.mvCandLT[bestMvpIdx];
+      acMvPred[1] = aamvpi.mvCandRT[bestMvpIdx];
+      acMvPred[2] = aamvpi.mvCandLB[bestMvpIdx];
+    }
+    else
+    {
+      uiBitsTemp += xCalcAffineMVBits( pu, acMvTemp, acMvPred, pu.cu->imv != 1 );
+    }
+#else
     uiBitsTemp += xCalcAffineMVBits( pu, acMvTemp, acMvPred, pu.cu->imv != 1 );
+#endif
 #else
     for ( int i = 0; i < mvNum; i++ )
     {
@@ -4860,6 +4956,9 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
       uiCostBest = uiCostTemp;
       uiBitsBest = uiBitsTemp;
       memcpy( acMv, acMvTemp, sizeof(Mv) * 3 );
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+      mvpIdx = bestMvpIdx;
+#endif
     }
   }
 
@@ -4902,6 +5001,56 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
 
   if (uiCostBest <= AFFINE_ME_LIST_MVP_TH*m_hevcCost)
   {
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    //search 8 nearest neighbors; integer distance
+    int testPos[8][2] = { { -1, 0 },{ 0, -1 },{ 0, 1 },{ 1, 0 },{ -1, -1 },{ -1, 1 },{ 1, 1 },{ 1, -1 } };
+    const uint32_t mvShift = pu.cu->imv == 1 ? 0 : ( pu.cu->imv == 2 ? ( MV_FRACTIONAL_BITS_DIFF << 1 ) : MV_FRACTIONAL_BITS_DIFF );
+    const int maxSearchRound = 3;
+
+    if ( m_pcEncCfg->getUseAffineAmvrEncOpt() && m_pcEncCfg->getIntraPeriod() != ( uint32_t ) -1 && pu.cu->imv )
+    {
+      for ( int rnd = 0; rnd < ( pu.cu->slice->getTLayer() <= 2 ? maxSearchRound : maxSearchRound - 1 ); rnd++ )
+      {
+        bool modelChange = false;
+        //search the model parameters with finear granularity;
+        for ( int j = 0; j < mvNum; j++ )
+        {
+          for ( int iter = 0; iter < 2; iter++ )
+          {
+            Mv centerMv[3];
+            memcpy( centerMv, acMv, sizeof( Mv ) * 3 );
+            memcpy( acMvTemp, acMv, sizeof( Mv ) * 3 );
+            for ( int i = ( iter ? 0: 4 ); i < ( iter ? 4 : 8 ); i++ )
+            {
+              acMvTemp[j].set( centerMv[j].getHor() + ( testPos[i][0] << mvShift ), centerMv[j].getVer() + ( testPos[i][1] << mvShift ) );
+
+              clipMv( acMvTemp[j], pu.cu->lumaPos(), pu.cu->lumaSize(), *pu.cs->sps );
+              xPredAffineBlk( COMPONENT_Y, pu, refPic, acMvTemp, predBuf, false, pu.cu->slice->clpRng( COMPONENT_Y ) );
+
+              Distortion costTemp = m_pcRdCost->getDistPart( predBuf.Y(), pBuf->Y(), pu.cs->sps->getBitDepth( CHANNEL_TYPE_LUMA ), COMPONENT_Y, DF_HAD );
+              uint32_t bitsTemp   = ruiBits;
+              bitsTemp += xCalcAffineMVBits( pu, acMvTemp, acMvPred, pu.cu->imv != 1 );
+              costTemp = ( Distortion ) ( floor( fWeight * ( double ) costTemp ) + ( double ) m_pcRdCost->getCost( bitsTemp ) );
+
+              if ( costTemp < uiCostBest )
+              {
+                uiCostBest = costTemp;
+                uiBitsBest = bitsTemp;
+                ::memcpy( acMv, acMvTemp, sizeof( Mv ) * 3 );
+                modelChange = true;
+              }
+            }
+          }
+        }
+
+        if ( !modelChange )
+        {
+          break;
+        }
+      }
+    }
+#endif
+
     Mv mvPredTmp[3] = { acMvPred[0], acMvPred[1], acMvPred[2] };
 #if JVET_M0246_AFFINE_AMVR
     if ( pu.cu->imv != 1 )
@@ -4988,6 +5137,11 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
 #if JVET_M0246_AFFINE_AMVR
   }
 #endif
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  acMvPred[0] = aamvpi.mvCandLT[mvpIdx];
+  acMvPred[1] = aamvpi.mvCandRT[mvpIdx];
+  acMvPred[2] = aamvpi.mvCandLB[mvpIdx];
+#endif
 
   // free buffer
   for (int i = 0; i<iParaNum; i++)
@@ -6207,12 +6361,25 @@ bool InterSearch::xReadBufferedUniMv(PredictionUnit& pu, RefPicList eRefPicList,
   return false;
 }
 
-bool InterSearch::xReadBufferedAffineUniMv(PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], uint32_t& ruiBits, Distortion& ruiCost)
+bool InterSearch::xReadBufferedAffineUniMv(PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], uint32_t& ruiBits, Distortion& ruiCost
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  , int& mvpIdx, const AffineAMVPInfo& aamvpi
+#endif
+)
 {
   if (m_uniMotions.isReadModeAffine((uint32_t)eRefPicList, (uint32_t)iRefIdx, pu.cu->affineType))
   {
-    m_uniMotions.copyAffineMvTo(acMv, ruiCost, (uint32_t)eRefPicList, (uint32_t)iRefIdx, pu.cu->affineType);
+    m_uniMotions.copyAffineMvTo(acMv, ruiCost, (uint32_t)eRefPicList, (uint32_t)iRefIdx, pu.cu->affineType
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                                , mvpIdx
+#endif
+    );
     m_pcRdCost->setCostScale(0);
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+    acMvPred[0] = aamvpi.mvCandLT[mvpIdx];
+    acMvPred[1] = aamvpi.mvCandRT[mvpIdx];
+    acMvPred[2] = aamvpi.mvCandLB[mvpIdx];
+#endif
 
     uint32_t uiMvBits = 0;
     for (int iVerIdx = 0; iVerIdx<(pu.cu->affineType ? 3 : 2); iVerIdx++)
@@ -6264,6 +6431,29 @@ void InterSearch::xClipMv( Mv& rcMv, const Position& pos, const struct Size& siz
   rcMv.setVer( std::min( verMax, std::max( verMin, rcMv.getVer() ) ) );
 }
 
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+uint32_t InterSearch::xDetermineBestMvp( PredictionUnit& pu, Mv acMvTemp[3], int& mvpIdx, const AffineAMVPInfo& aamvpi )
+{
+  bool mvpUpdated  = false;
+  uint32_t minBits = std::numeric_limits<uint32_t>::max();
+  for ( int i = 0; i < aamvpi.numCand; i++ )
+  {
+    Mv mvPred[3] = { aamvpi.mvCandLT[i], aamvpi.mvCandRT[i], aamvpi.mvCandLB[i] };
+    uint32_t candBits = m_auiMVPIdxCost[i][aamvpi.numCand];
+    candBits += xCalcAffineMVBits( pu, acMvTemp, mvPred, pu.cu->imv != 1 );
+
+    if ( candBits < minBits )
+    {
+      minBits    = candBits;
+      mvpIdx     = i;
+      mvpUpdated = true;
+    }
+  }
+  CHECK( !mvpUpdated, "xDetermineBestMvp() error" );
+  return minBits;
+}
+#endif
+
 #if JVET_M0444_SMVD
 void InterSearch::symmvdCheckBestMvp(
   PredictionUnit& pu, 
diff --git a/source/Lib/EncoderLib/InterSearch.h b/source/Lib/EncoderLib/InterSearch.h
index 27030ab31ec24eddb4085f35b4a0f7ba54efb931..da9abdf1bea40095271e8b8f1c0eede5da81fddc 100644
--- a/source/Lib/EncoderLib/InterSearch.h
+++ b/source/Lib/EncoderLib/InterSearch.h
@@ -404,6 +404,10 @@ protected:
                                     Mv              acMv[3],
                                     uint32_t&           ruiBits,
                                     Distortion&     ruiCost,
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                                    int&            mvpIdx,
+                                    const AffineAMVPInfo& aamvpi,
+#endif
                                     bool            bBi = false
                                   );
 
@@ -430,7 +434,11 @@ protected:
   void xSymmetricMotionEstimation( PredictionUnit& pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred, RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion& ruiCost, int gbiIdx );
 #endif
 
-  bool xReadBufferedAffineUniMv   ( PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], uint32_t& ruiBits, Distortion& ruiCost);
+  bool xReadBufferedAffineUniMv   ( PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], uint32_t& ruiBits, Distortion& ruiCost
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+                                    , int& mvpIdx, const AffineAMVPInfo& aamvpi
+#endif
+  );
   double xGetMEDistortionWeight   ( uint8_t gbiIdx, RefPicList eRefPicList);
   bool xReadBufferedUniMv         ( PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv& pcMvPred, Mv& rcMv, uint32_t& ruiBits, Distortion& ruiCost);
 
@@ -458,7 +466,9 @@ protected:
 
   void xExtDIFUpSamplingH         ( CPelBuf* pcPattern );
   void xExtDIFUpSamplingQ         ( CPelBuf* pcPatternKey, Mv halfPelRef );
-
+#if JVET_M0247_AFFINE_AMVR_ENCOPT
+  uint32_t xDetermineBestMvp      ( PredictionUnit& pu, Mv acMvTemp[3], int& mvpIdx, const AffineAMVPInfo& aamvpi );
+#endif
   // -------------------------------------------------------------------------------------------------------------------
   // compute symbol bits
   // -------------------------------------------------------------------------------------------------------------------