Repository: deepmodeling/rid-kit (language: python)

Issue:
Dflow
1. Add more monitoring output during the workflow.
2. Update the PLUMED and GROMACS in docker images to make workflow more fast.
3. Fix bugs to locally run on Slurm machine.


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 8c6fa37b..3da5e824 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,13 @@
 # Table of contents
 - [About Rid-kit](#about-rid-kit)
+- [Quick Start](#quick-start)
+- [Run the workflow without k8s enviroment](#run-the-workflow-without-k8s-environment)
 - [Main procedure of RiD](#main-procedure-of-rid)
 - [Use Rid-kit](#use-rid-kit)
 - [Installation of enviroment](#installation-of-enviroment)
 - [Installation of DeepMD potential](#installation-of-dp)
 - [Configure simulations](#configure-simulations)
+- [Configure machine resources](#configure-machine-resources)
 - [Troubleshooting](#troubleshooting)
 
 # About Rid-kit
@@ -12,21 +15,76 @@ Rid-kit is a package written in Python, designed to do enhanced sampling using r
 
 For more information, check the [documentation](https://rid-kit-dflow.readthedocs.io/en/latest/).
 
+# Quick start
+Rid-kit is based on [dflow](https://github.com/deepmodeling/dflow), one can decide whether to use a k8s environment to run a workflow. It is recommended to use k8s enviroment to run the workflow unless it is not doable anyway. The dflow team provide a community version of k8s [deepmodeling k8s](https://workflows.deepmodeling.com/), making the use of Rid-kit very convenient. To use the community version of k8s, one first need to register a Bohrium account in [Bohrium](https://bohrium.dp.tech/login) and learn a few concepts (job, jobgroup, project id) in the Bohrium website documents. Then the use of rid-kit is very easy.
+
+## Set the enviroment variables
+Just set the enviroment variables based on your personal Bohrium account information by
+
+```
+export DFLOW_HOST=https://workflows.deepmodeling.com
+export DFLOW_K8S_API_SERVER=https://workflows.deepmodeling.com
+export DFLOW_S3_REPO_KEY=oss-bohrium
+export DFLOW_S3_STORAGE_CLIENT=dflow.plugins.bohrium.TiefblueClient
+export BOHRIUM_USERNAME="<bohrium-email>"
+export BOHRIUM_PASSWORD="<bohrium-password>"
+export BOHRIUM_PROJECT_ID="<bohrium-project-id>"
+```
+
+## Install Rid-kit
+Install the latest rid-kit
+```
+git clone git@github.com:PKUfjh/rid-kit.git
+cd rid-kit
+git checkout dflow
+pip install setuptools_scm
+pip install .
+```
+## Run an example
+Change to the rid-kit directory
+```
+cd rid-kit
+```
+Run a example of Ala-dipeptide using dihedral as CVs (change to your own Bohrium account information)
+```
+rid submit -i ./tests/data/000 -c ./rid/template/rid_gmx_dih.json -m ./rid/template/machine_bohrium_k8s.json
+```
+You can also run the example on a Slurm machine (But you need to configure a conda enviroment on the slurm, see below)
+```
+rid submit -i ./tests/data/000 -c ./rid/template/rid_gmx_dih.json -m ./rid/template/machine_slurm_k8s.json
+```
+You can also run the example using distance as CVs (This will use constrained md as the mean force calculator)
+```
+rid submit -i ./tests/data/000 -c ./rid/template/rid_gmx_dis.json -m ./rid/template/machine_bohrium_k8s.json
+```
+
+Note that if you want to use constrained MD as the mean force calculator, apart from setting `method` to be `constrained` in the `label_config`, you should add `[ constraints ]` line corresponding to the `[ moleculartype ]` in your input `topology` file yourself, since gromacs specifies constraints information for each `[ moleculartype ]`.
+
+Also note that, since gromacs only supports constrained MD for `distance CV`, the constrained MD simulation in rid-kit only supports `distance CV` at this moment.
+
+# Run the Workflow without k8s environment
+To run the workflow without k8s environment, one can use the `Debug` mode of `Dflow`. In this mode however, one can not monitor the workflow in the `Argo` UI.
+## Run an example
+If one wants to run the workflow on the `Slurm` machine locally, type
+```
+DFLOW_DEBUG=1 rid submit -i /tests/data/000 -c /rid/template/rid_gmx_dih.json -m /rid/template/machine_slurm_local.json
+```
+
 # Main procedure of RiD
 
 RiD will run in iterations. Every iteration contains tasks below:
 
 1. Biased MD;
-2. Restrained MD;
+2. Restrained/Constrained MD;
 3. Training neural network.
 
 ## Biased MD
 
 Just like Metadynamics, RiD will sample based on a bias potential given by NN models. An uncertainty indicator will direct the process of adding bias potential.
 
-## Restrained MD
+## Restrained/Constrained MD
 
-This procedure will calculate the mean force based on the sampling results, which can generate data set for training. 
+This procedure will calculate the mean force based on the sampling results of restrained MD or constrained MD, which can generate data set for training. 
 
 ## Neural network training
 
@@ -40,7 +98,7 @@ A more detailed description of RiD is published now, please see:
 
 # Use Rid-kit
 
-A quick-start on using Rid-kit can be found as follows:
+A tutorial on using Rid-kit can be found as follows:
 
 - [Tutorial](/docs/source/tutorial.ipynb)
 
@@ -56,6 +114,9 @@ Installation of the DeepMD potential support can be found in
 # Configure simulations
 - [configure simulations](docs/source/rid_configuration.md)
 
+# Configure machine resources
+- [configure machines](docs/source/rid_machine.md)
+
 # Workflow Synopsis
 
 - ![image](docs/pics/rid_workflow.jpg)
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 940d4497..4ba2409e 100755
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -20,10 +20,11 @@ Contents
 .. toctree::
    :maxdepth: 2
 
-   Quick start <tutorial>
+   Tutorial <tutorial>
    Environment settings  <install>
    Installation with DP potential <install_dp>
-   Configure Simulations <rid_configuration>
+   Configure simulations <rid_configuration>
+   Configure machine resources <rid_machine>
    Rid API <api/api>
    Trouble Shooting <troubleshooting/index> 
 
diff --git a/docs/source/install.md b/docs/source/install.md
index d19a1c9e..40c897ed 100644
--- a/docs/source/install.md
+++ b/docs/source/install.md
@@ -13,7 +13,7 @@ The `enviroment` of rid-kit software is a bit complex, it uses `dflow` to manage
 We recommend using conda to manage the python enviroment. 
 Use the command
 ```
-conda create -n rid-drop python=3.9 libtensorflow_cc=2.6.2=*cuda110* tensorflow=2.6.2=*cuda110* nccl mdtraj numpy scikit-learn cmake -c conda-forge
+conda create -n rid-drop python=3.9 libtensorflow_cc=2.6.2=*cuda110* tensorflow=2.6.2=*cuda110* nccl mdtraj matplotlib==3.6.1 scikit-learn cmake dpdata parmed cython -c conda-forge
 ```
 to get the compiled libtensorflow_cc and other necessary packages.
 After installation, activate the enviroment and set library path
@@ -23,23 +23,23 @@ export LIBRARY_PATH=${CONDA_PREFIX}/lib:$LIBRARY_PATH
 export LD_LIBRARY_PATH=${CONDA_PREFIX}/lib:$LD_LIBRARY_PATH
 ```
 
-### Install plumed2.8.0
-You need to copy compiled `DeePFE.cpp` to the plumed directory. This file locates at `rid-kit/install/DeePFE.cpp`.
-Note that here we use c++ 14 standard to compile, the gcc version need to be newer than 6.1.0. In slurm enviroment, if the software is managed by `Enviroment Module`, one can load gcc such as `module load gcc/7.2.0` or other available versions.
+### Install plumed2.8.1
+To compile plumed with our free energy model interface, you need to copy compiled `DeePFE.cpp` to the plumed directory. This file locates at `rid-kit/install/DeePFE.cpp`.
+Note that here we use c++ 14 standard to compile, the gcc version need to be newer than 6.1.0. In slurm enviroment, if the software is managed by `Enviroment Module`, one can load gcc such as `module load gcc/10.1.0` or other available versions.
 
 set CC and CXX enviroment variables
 ```
-export CC=/yourpath/to/gcc-7.2.0
-export CXX=/yourpath/to/g++-7.2.0
+export CC=/yourpath/to/gcc-10.1.0
+export CXX=/yourpath/to/g++-10.1.0
 ```
 
 ```bash
-tar -xvzf plumed-2.8.0.tgz
-cp DeePFE.cpp plumed-2.8.0/src/bias
-cd plumed-2.8.0
+tar -xvzf plumed-2.8.1.tgz
+cp DeePFE.cpp plumed-2.8.1/src/bias
+cd plumed-2.8.1
 ./configure --prefix=$CONDA_PREFIX \
-                   CXXFLAGS="-std=gnu++14 -I $CONDA_PREFIX/include/" \
-                   LDFLAGS=" -L$CONDA_PREFIX/lib -ltensorflow_cc -ltensorflow_framework -Wl,-rpath=$CONDA_PREFIX/lib/" \
+                   CXXFLAGS="-O3 -I$CONDA_PREFIX/include/" \
+                   LDFLAGS=" -L$CONDA_PREFIX/lib -ltensorflow_cc -ltensorflow_framework"
 make -j 6
 make install
 ```
@@ -48,19 +48,18 @@ Set the enviroment variables
 export PLUMED_KERNEL=${CONDA_PREFIX}/lib/libplumedKernel.@SOEXT@
 ```
 
-### Install gromacs 2021.4
-Note that in order to compile gromacs with GPU support, you need to have CUDA toolkit installed. In slurm enviroment, if the software is managed by `Enviroment Module`, one can load CUDA toolkits such as `module load cuda/11.1`. Also note that in most slurm environment, there is no network connection at computing node, but if you want to compile gromacs without fftw installed, you will have to compile it on login-in node where network connection is on.
+### Install gromacs 2022.4
+Note that in order to compile gromacs with GPU support, you need to have CUDA toolkit installed. In slurm enviroment, if the software is managed by `Enviroment Module`, one can load CUDA toolkits such as `module load cuda/11.4`. Also note that in most slurm environment, there is no network connection at computing node, but if you want to compile gromacs without fftw installed, you will have to compile it on login-in node where network connection is on.
 
 ```bash
-tar -xzvf gromacs-2021.4.tar.gz
-cd gromacs-2021.4
-plumed patch -p  # make sure you use the correct plumed version
+tar -xzvf gromacs-2022.4.tar.gz
+cd gromacs-2022.4
+plumed patch -p # make sure you use the correct gromacs version
 mkdir build
 cd build
 cmake .. -DGMX_BUILD_OWN_FFTW=ON \
                 -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \
-                -DGMX_GPU=CUDA  \
-                -DGMX_SIMD=avx_512
+                -DGMX_GPU=CUDA
 make -j 4
 make install
 ```
diff --git a/docs/source/install_dp.md b/docs/source/install_dp.md
index 295ca14e..ffd2b90e 100644
--- a/docs/source/install_dp.md
+++ b/docs/source/install_dp.md
@@ -13,7 +13,7 @@ The `enviroment` of rid-kit software is a bit complex, it uses `dflow` to manage
 We recommend using conda to manage the python enviroment. 
 Use the command
 ```
-conda create -n rid-dp python=3.9 libtensorflow_cc=2.6.2=*cuda110* tensorflow=2.6.2=*cuda110* nccl mdtraj numpy scikit-learn cmake -c conda-forge
+conda create -n rid-dp python=3.9 libtensorflow_cc=2.6.2=*cuda110* tensorflow=2.6.2=*cuda110* nccl mdtraj numpy scikit-learn cmake dpdata cython -c conda-forge
 ```
 to get the compiled libtensorflow_cc and other necessary packages.
 After installation, activate the enviroment and set library path
@@ -69,8 +69,8 @@ tar -xvzf plumed-2.6.1.tgz
 cp DeePFE.cpp plumed-2.6.1/src/bias
 cd plumed-2.6.1
 ./configure --prefix=$CONDA_PREFIX \
-                   CXXFLAGS="-std=gnu++14 -I $CONDA_PREFIX/include/" \
-                   LDFLAGS=" -L$CONDA_PREFIX/lib -ltensorflow_cc -ltensorflow_framework -Wl,-rpath=$CONDA_PREFIX/lib/" \
+                   CXXFLAGS="-O3 -I $CONDA_PREFIX/include/" \
+                   LDFLAGS=" -L$CONDA_PREFIX/lib -ltensorflow_cc -ltensorflow_framework" \
 make -j 6
 make install
 ```
@@ -90,7 +90,7 @@ nvcc fatal   : Unsupported gpu architecture 'compute_30'
 ```bash
 tar -xzvf gromacs-2020.2.tar.gz
 cd gromacs-2020.2
-plumed patch -p  # make sure you use the correct plumed version
+plumed patch -p  # make sure you use the correct gromacs version
 dp_gmx_patch -d $gromacs_root -v $version -p #version=2020.2 is supported
 mkdir build
 cd build
diff --git a/docs/source/rid_configuration.md b/docs/source/rid_configuration.md
index d9db68d1..fba57cd1 100644
--- a/docs/source/rid_configuration.md
+++ b/docs/source/rid_configuration.md
@@ -39,7 +39,7 @@
 
 ## CV setting
 
-This section configures collective variables (CVs). `RiD-kit` provides two modes to configure CV: `"torsion"` and `"custom"`. 
+This section configures collective variables (CVs). `RiD-kit` provides three modes to configure CV: `"torsion"`, `"distance"` and `"custom"`. 
 
 ### Torsion mode
 
@@ -51,12 +51,24 @@ In torsion mode, `RiD-kit` uses torsion (dihedral angles) of proteins as collect
 
 * **`weights`** `(List[int])` weights of CVs to scale their values. Used in clustering to calculate the Euclidean distances between CVs. This can prevent from CV discrimination if some CV's range is much larger than another one.
 
+### Distance mode
+In distance mode, `RiD-kit` uses distance between atoms of systems as collective variables. Set `"mode": "distance"`. 
+`selected_atomid`, `angular_mask` and `weights` must not be `none` or empty if you use distance mode.
+
+* **`selected_atomid`** `(List[List[int]])` ids (starting form 1) of selected pair of atoms. 
+
+* **`angular_mask`** `(List[int])` the mask of augular (periodic) CVs, 1 for periodic and 0 for non-periodic. In distance mode, all CVs are nonperiodic, so a list filled by 0 with length equal to number of CVs should be set.
+
+* **`weights`** `(List[int])` weights of CVs to scale their values. Used in clustering to calculate the Euclidean distances between CVs. This can prevent from CV discrimination if some CV's range is much larger than another one.
+
 ### Custom mode
 
 `RiD-kit` also supports user-defined collective variables.
-Set `"mode": "custom"`. `cv_file`, `angular_mask` and `weights` must not be `none` or empty if you use custom mode.
+Set `"mode": "custom"` to use customed CV of your own design. `cv_file`, `angular_mask` and `weights` must not be `none` or empty if you use custom mode. The CV file is in the PLUMED2 format, you should add your own CV to the `PRINT` line in the CV file.
 
-* **`"cv_file"`** `(str)` Path to a CV file. This file defines collective variables in PLUMED2 format. Technically, CV that PLUMED2 supports can be suporrted by `RiD-kit`. 
+Note that if you use only one CV file, the CV file name should not end with `".pdb"`. If you use multiple files to define your own CV, the file to define your CV should not end with `".pdb"`, and other files should end with `".pdb"`.
+
+* **`"cv_file"`** `(List[str])` List of Paths to CV files. The files define collective variables in PLUMED2 format. Technically, CV that PLUMED2 supports can be suporrted by `RiD-kit`. 
   
 * **`"angular_mask"`** `(List[int])` the mask of augular (periodic) CVs, 1 for periodic and 0 for non-periodic. In custom mode, figure out the periodic CVs and set 1 at the corresponding location in list.
   
@@ -69,22 +81,46 @@ Set `"mode": "custom"`. `cv_file`, `angular_mask` and `weights` must not be `non
     "selected_resid": [ 1, 2 ],
     "angular_mask": [ 1, 1 ],
     "weights": [ 1, 1 ],
-    "cv_file":""
-},
+    "cv_file":[""]
+}
+```
+```JSON
+"CV": {
+        "mode": "distance",
+        "selected_atomid": [[2,5],[5,7]],
+        "angular_mask": [0,0],
+        "weights": [1,1],
+        "cv_file":[""]
+}
+```
+```JSON
+"CV": {
+        "mode": "custom",
+        "selected_atomid":[[161,165],[124,156],[124,161],[156,165]],
+        "angular_mask": [0,0,0,0],
+        "weights": [1,1,1,1],
+        "cv_file": ["colvar", "plmpath.pdb"]
+}
 ```
 
 ## ExploreMDConfig
+This section configures the parameters in `Exploration` step. Currently, rid-kit supports two types of sampler: `"gmx"` and `"lmp"`, stand for Gromacs and Lammps respectively.
 
-This section configures the parameters in `Exploration` step. MD eigine is Gromacs patched by PLUMED2. Parameter convention follows Gromacs and PLUMED2.
+### gmx type
 
+Set `"type": "gmx"` to use Gromacs as sampler. `nstep`, `temperature`, `ref-t`,`output_freq`, `dt`, `output_mode` must not be `none` or empty if you use gmx type. You can also set other parameters in `mdp` files for your usage (Acutally it is recommended since the default settings in rid-kit may be be the best suit for your system).
 
 * **`nsteps`** `(int)` Number of steps of MD simulation in exploration step.
 
-* **`output_freq`** `(int)`  Frame output frequence of MD simulations. A recommended value is `nsteps/1000` to make sure at least 1000 frames generated during exploration.
+* **`type`** `(str)` Type of sampler in `Exploration` step. Currently rid-kit support `gmx` (Gromacs) and `lmp` (Lammps).
 
 * **`temperature`** `(int)` Temperature of MD simulations. Please make sure this value is the same as the temperature in `Label` step unless you are meant to keep them different.
 
-* **`dt`** `(int)` Time interval of MD simulations. 0.002 is recommended for normal simulations. One may use a larger interval, e.g. 0.004, when heavy hydrogen modes in Gromacs.
+* **`ref-t`** `(str)` Temperature for each group of the system, default is `300 300` for `water non-water` group. 
+
+* **`output_freq`** `(int)`  Frame output frequence of MD simulations. A recommended value is `nsteps/1000` to make sure at least 1000 frames generated during exploration.
+
+* **`dt`** `(int)` Time interval of MD simulations in `ps`. 0.002 is recommended for normal simulations. One may use a larger interval, e.g. 0.004, when heavy hydrogen modes in Gromacs.
 
 * **`output_mode`** `(str):` Optional modes: `"both", "single", "double", "none"`. 
   * `"both"`: Generate both full presicion format `.trr` and compressed presision format `.xtc` trajectories during MD simulations.
@@ -96,23 +132,49 @@ This section configures the parameters in `Exploration` step. MD eigine is Groma
 * **`nt`** `(int)` Total number of threads to start (0 is guess). See detail in [Gromacs manual](https://manual.gromacs.org/current/onlinehelp/gmx-mdrun.html).
 * **`max_warning`** Max warnings in `gmx grompp` steps. See detail in [Gromacs manual](https://manual.gromacs.org/current/onlinehelp/gmx-grompp.html).
 
+### lmp type
+Set `"type": "lmp"` to use Lammps as sampler. `output_freq`, `dt` and `inputfile` must not be `none` or empty if you use lmp type. 
+
+* **`inputfile`** `(str)` Name of lammps input file to define the simulation paramers.
+
 ### Example
 
 ```JSON
 "ExploreMDConfig": {
-    "nsteps": 25000,
-    "output_freq": 25,
-    "temperature": 300,
-    "dt": 0.002,
-    "output_mode": "both",
-    "ntmpi": 1,
-    "nt": 8,
-    "max_warning": 0
-},
+        "nsteps": 50000,
+        "type": "gmx",
+        "temperature": 300,
+        "output_freq": 50,
+        "ref-t": "300 300",
+        "verlet-buffer-tolerance":"-1",
+        "rlist": 1,
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
+        "dt": 0.002,
+        "fourierspacing": "0.12",
+        "output_mode": "single",
+        "ntmpi": 1,
+        "nt": 8,
+        "max_warning": 2
+}
+```
+```JSON
+"ExploreMDConfig": {
+        "type":"lmp",
+        "inputfile":"input_explore.lammps",
+        "dt": 0.002,
+        "output_freq": 50,
+        "ntmpi": 1,
+        "nt": 8,
+        "max_warning": 2
+}
 ```
 
 ## SelectorConfig
-
 This section configures the parameters in `Selection` step. In `Selection` step, all CV values are clustered. Then data owning high model deviation are selected, collected and sent to `Label` step. 
 
 * **`"cluster_threshold"`** `(int)` Initial guess of cluster threshold. Note: the real cluster threshold is generated from this guess.
@@ -140,48 +202,69 @@ This section configures the parameters in `Selection` step. In `Selection` step,
 ```
 
 ## LabelMDConfig
+This section configures the parameters in `Label` step. Currently, rid-kit supports two methods of labeling: `"restrained"` and `"constrained"`, stand for `restrained MD` and `constrained MD` respectively. Most settings are quite similar to those in `Exploration` Step. The simulation time is usually different between `Exploration` and `Label` steps, while the simulation time for `Exploration` has more freedom, the simulation time for `Label` step has to be chosen with care (longer enough to ensure convergence and shorter enough to avoid wasting). Our experience is that `100ps` is enough for `torsion mode` in `restrained MD method`, `1ns` is enough for `distance mode` in `constrained MD method`.  
 
-This section configures the parameters in `Label` step. Most settings are quite similar to those in `Exploration` Step. In `Label`, `RiD-kit` performs restrained MD simulations, where hamonic restraints are exerted on CVs. These procedues need much shorter steps than `Exploration`.
+### restrained method
+Set `"method": "restrained"` to use restrained MD as mean force calculator. The only different parameters with `Exploration` step is `kappas`.
 
 * **`kappas`** `(List[int])` A list of force constants ($\kappa$) of harmonic restraints. The length of the list is equal to the number of CVs.
 
-* **`nsteps`** `(int)` Number of steps of MD simulation of restrained simulations.
-
-* **`output_freq`** `(int)`  Frame output frequence of MD simulations. A recommended value is `nsteps/1000` to make sure at least 1000 frames generated during exploration.
-
-* **`temperature`** `(int)` Temperature of MD simulations. Please make sure this value is the same as the temperature in `Exploration` step unless you are meant to keep them different.
-
-* **`dt`** `(int)` Time interval of MD simulations. 0.002 is recommended for normal simulations. Tune it down if you use a very large harmonic force, otherwise numerical explosion may occur.
-
-* **`output_mode`** `(str):` Optional modes: `"both", "single", "double", "none"`. 
-  * `"both"`: Generate both full presicion format `.trr` and compressed presision format `.xtc` trajectories during MD simulations.
-  * `"single"` Only generate compressed presision format `.xtc` trajectories during MD simulations.
-  * `"double"` Only generate full presicion format `.trr` trajectories during MD simulations.
-  *  `"none"` Don't generate trajectory files. (Used for tasks that only need PLUMED2 ouput.)
-  
-* **`ntmpi`** `(int)` Number of thread-MPI ranks to start (0 is guess). See detail in [Gromacs manual](https://manual.gromacs.org/current/onlinehelp/gmx-mdrun.html).
-* **`nt`** `(int)` Total number of threads to start (0 is guess). See detail in [Gromacs manual](https://manual.gromacs.org/current/onlinehelp/gmx-mdrun.html).
-* **`max_warning`** Max warnings in `gmx grompp` steps. See detail in [Gromacs manual](https://manual.gromacs.org/current/onlinehelp/gmx-grompp.html).
-
+### constrained method
+Set `"method": "constrained"` to use constrained MD as mean force calculator. Currently rid-kit only supports distance CV to use this method. The other parameters is the same with `Exploration` step.
 
 ### Example
 
+```JSON
+    "LabelMDConfig": {
+        "nsteps": 50000,
+        "temperature":300,
+        "method": "restrained",
+        "type": "gmx",
+        "output_freq": 100,
+        "ref-t": "300 300",
+        "rlist": 1,
+        "verlet-buffer-tolerance":"-1",
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
+        "dt": 0.002,
+        "fourierspacing": "0.12",
+        "output_mode": "single",
+        "ntmpi": 1,
+        "nt": 8,
+        "max_warning": 2,
+        "kappas": [ 500, 500 ]
+    }
+```
 ```JSON
 "LabelMDConfig": {
-    "kappas": [ 500, 500 ],
-    "nsteps": 25000,
-    "output_freq": 50,
-    "temperature": 300,
-    "dt": 0.002,
-    "output_mode": "both",
-    "ntmpi": 1,
-    "nt": 8,
-    "max_warning": 0
-},
+        "nsteps": 50000,
+        "temperature":300,
+        "method": "constrained",
+        "type": "gmx",
+        "output_freq": 100,
+        "ref-t": "300 300",
+        "rlist": 1,
+        "verlet-buffer-tolerance":"-1",
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
+        "dt": 0.001,
+        "fourierspacing": "0.12",
+        "output_mode": "both",
+        "ntmpi": 1,
+        "nt": 8,
+        "max_warning": 2
+}
 ```
 
 ## Train
-
 This section configures the parameters in `Train` step. `RiD-kit` is based on `Tensorflow`.
 
 * **`numb_models`** `(int)` Number of models that are trained in `Train` step. `RiD-kit` uses model deviations (or standrad deviation of output of these models) to evaluate the quality of free energy surface, so `numb_models` mush be **greater** than 1.
@@ -229,7 +312,7 @@ You can find a full example of `rid.json` within `"rid-kit/rid/template"`. Or yo
 {
     "name": "test",
     "numb_walkers": 2,
-    "numb_iters": 20,
+    "numb_iters": 2,
     "trust_lvl_1": 2,
     "trust_lvl_2": 3,
     "init_models": [],
@@ -239,54 +322,77 @@ You can find a full example of `rid.json` within `"rid-kit/rid/template"`. Or yo
         "selected_resid": [ 1, 2 ],
         "angular_mask": [ 1, 1 ],
         "weights": [ 1, 1 ],
-        "cv_file":""
+        "cv_file":[""]
     },
 
     "ExploreMDConfig": {
-        "nsteps": 25000,
-        "output_freq": 25,
+        "nsteps": 50000,
+        "type": "gmx",
         "temperature": 300,
+        "output_freq": 500,
+        "ref-t": "300 300",
+        "verlet-buffer-tolerance":"-1",
+        "rlist": 1,
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
         "dt": 0.002,
-        "output_mode": "both",
+        "fourierspacing": "0.12",
+        "output_mode": "single",
         "ntmpi": 1,
         "nt": 8,
-        "max_warning": 0
+        "max_warning": 2
     },
 
     "SelectorConfig": {
-        "numb_cluster_lower": 16,
-        "numb_cluster_upper": 26,
-        "cluster_threshold": 1,
+        "numb_cluster_lower": 12,
+        "numb_cluster_upper": 22,
+        "cluster_threshold": 1.5,
         "max_selection": 30,
-        "numb_cluster_threshold": 8,
+        "numb_cluster_threshold": 6,
         "slice_mode": "gmx"
     },
 
     "LabelMDConfig": {
-        "nsteps": 25000,
-        "output_freq": 50,
-        "temperature": 300,
+        "nsteps": 50000,
+        "temperature":300,
+        "method": "restrained",
+        "type": "gmx",
+        "output_freq": 100,
+        "ref-t": "300 300",
+        "rlist": 1,
+        "verlet-buffer-tolerance":"-1",
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
         "dt": 0.002,
-        "output_mode": "both",
+        "fourierspacing": "0.12",
+        "output_mode": "single",
         "ntmpi": 1,
         "nt": 8,
-        "max_warning": 0,
+        "max_warning": 2,
         "kappas": [ 500, 500 ]
     },
 
     "Train": {
         "numb_models": 4,
-        "neurons": [ 50, 50, 50, 50 ],
+        "neurons": [ 200, 200, 200, 200 ],
         "resnet": true,
-        "batch_size": 32,
-        "epoches": 2000,
+        "batch_size": 128,
+        "epoches": 8000,
         "init_lr": 0.0008,
         "decay_steps": 120,
         "decay_rate": 0.96,
-        "drop_out_rate": 0.1,
+        "drop_out_rate": 0,
         "numb_threads": 8,
         "use_mix": false,
-        "restart": false,
+        "restart": false
     }
 }
 ```
\ No newline at end of file
diff --git a/docs/source/rid_machine.md b/docs/source/rid_machine.md
new file mode 100644
index 00000000..3cc29c7d
--- /dev/null
+++ b/docs/source/rid_machine.md
@@ -0,0 +1,319 @@
+# Setting Machine configuration of `RiD-kit`
+
+`RiD-kit` uses a JSON-format file (typically `machine.json`) to configure machine resources to run the workflow. Here we explain different resources one by one.
+
+`Rid-kit` currently supports three types of machine platform to do computation: `local machine`, `Bohrium cloud` and `Slurm machine`. `local machine` and `Bohrium cloud` use the ready-made `Docker image` to manage computation enviroment, while `Slurm machine` use `Conda` to manage computation enviroment.
+
+## Local machine and Bohrium cloud
+To run rid-kit workflow on a local machine or server, you need to build your own `k8s` enviroment by following the tutorials. But this is not recommended since `Rid-kit` workflow requires a lot of computation resources. 
+
+To run rid-kit workflow on `Bohrium cloud`, you can either build your own `k8s` enviroment or simply use the community version of `k8s` [deepmodeling k8s](https://workflows.deepmodeling.com/).
+
+Then the configuration is very easy, just configure different images to different rid-kit workflow steps. `Rid-kit` currently has six different `Docker images` to support different steps, add `registry.dp.tech/public/` if submitting to the `Bohrium cloud`.
+
+`(registry.dp.tech/public/)pkufjhdocker/rid-gmx-exploration:stable` to do `run-exploration`. This image has gromacs compiled with plumed-patch, also supports tensorflow c++ interface, used to do neural network potential biased MD.
+
+`(registry.dp.tech/public/)pkufjhdocker/rid-gmx-plumed:stable` to do `run-label`. This image has gromacs compiled with plumed-patch, used to do restrained or constrained MD.
+
+`(registry.dp.tech/public/)pkufjhdocker/rid-gmx-tf:stable` to do `run-select` and `post-label`. This image has gromacs compiled with tensorflow python interface, used to select configuration in the `Exploration` step and calculating mean force in the constrained MD simulation. So if you are using restrained MD to calculate the mean force, `post-label` step can also use simple `pkufjhdocker/rid-tf-cpu:stable` images.
+
+`(registry.dp.tech/public/)pkufjhdocker/rid-tf-gpu:stable` to do `train`. This image is the standard tensorflow image with GPU support, used to train the free-energy model.
+
+`(registry.dp.tech/public/)pkufjhdocker/rid-tf-cpu:stable` to do other steps.
+
+`(registry.dp.tech/public/)pkufjhdocker/gmx-exploration-dp:stable` to do `run-exploration` and `run-label`, if you want to run a DP potential molecular dynamics. This image has gromacs compiled with plumed-patch and DP potential support, also support tensorflow c++ interface, used to do neural network potential biased MD with DP potential.
+
+
+### Local Machine Example
+```JSON
+{
+    "resources": {
+        "local_k8s_1": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-gmx-exploration:latest", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "local_k8s_2": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-gmx-plumed:latest", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "local_k8s_3": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-tf-cpu:latest", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "local_k8s_4": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-gmx-tf:latest", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "local_k8s_5": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-tf-gpu:latest", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        }
+    },
+
+    "tasks": {
+        "prep_exploration_config": "local_k8s_3",
+        "run_exploration_config": "local_k8s_1",
+        "prep_label_config": "local_k8s_3",
+        "run_label_config": "local_k8s_2",
+        "post_label_config": "local_k8s_4",
+        "prep_select_config": "local_k8s_3",
+        "run_select_config": "local_k8s_4",
+        "prep_data_config": "local_k8s_3",
+        "run_train_config": "local_k8s_5",
+        "workflow_steps_config": "local_k8s_3"
+    }
+}
+```
+
+### Bohrium example
+```JSON
+{
+    "resources": {
+        "bohrium1": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-exploration:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium2": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "test_rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-plumed:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium3": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-tf-cpu:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium4": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-tf:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium5": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-tf-gpu:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        }
+    },
+
+    "tasks": {
+        "prep_exploration_config": "bohrium3",
+        "run_exploration_config": "bohrium1",
+        "prep_label_config": "bohrium3",
+        "run_label_config": "bohrium2",
+        "post_label_config": "bohrium3",
+        "prep_select_config": "bohrium3",
+        "run_select_config": "bohrium4",
+        "prep_data_config": "bohrium3",
+        "run_train_config": "bohrium5",
+        "workflow_steps_config": "bohrium3"
+    }
+}
+```
+
+## Slurm machine
+To run rid-kit workflow on `Slurm machine`, you can either build your own `k8s` enviroment or simply use the community version of `k8s` [deepmodeling k8s](https://workflows.deepmodeling.com/).
+
+If you want to submit `Rid-kit` workflow to `Slurm machine`, you have to compile the rid-kit enviroment yourself on the `Slurm machine`, we recommend to manage the enviroment in by `Conda`, you can check [Installation](install.md) for the compilation detail.
+
+After compiling your `Conda` enviroment on the `Slurm machine`, the submission process is very easy now. Suppose the `Conda` enviroment can be activated by `rid.env` script, one submission example is the following:
+### Slurm example
+```JSON
+{
+    "resources": {
+        "local_k8s": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-tf-cpu:latest", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "remote_slurm": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "Slurm",
+                "context_type": "SSHContext",
+                "local_root" : "./",
+                "remote_root": "",
+                "remote_profile":{
+                    "hostname": "",
+                    "username": "",
+                    "password": "",
+                    "port": "",
+                    "timeout": 20
+                }
+            },
+            "resources_dict":{
+                "number_node": 1,
+                "cpu_per_node": 8,
+                "gpu_per_node": 1,
+                "queue_name": "",
+                "group_size": 1,
+                "custom_flags": [
+                    "#SBATCH --time=120:00:00",
+                    "#SBATCH --exclude=3090-[000,007]"
+                   ],
+                "source_list": ["/path/to/rid.env"]
+            }
+        }
+        }
+    },
+
+    "tasks": {
+        "prep_exploration_config": "local_k8s",
+        "run_exploration_config": "remote_slurm",
+        "prep_label_config": "local_k8s",
+        "run_label_config": "remote_slurm",
+        "post_label_config": "remote_slurm",
+        "prep_select_config": "local_k8s",
+        "run_select_config": "remote_slurm",
+        "prep_data_config": "local_k8s",
+        "run_train_config": "remote_slurm",
+        "workflow_steps_config": "local_k8s"
+    }
+}
+```
\ No newline at end of file
diff --git a/docs/source/tutorial.ipynb b/docs/source/tutorial.ipynb
index 0f45ce1b..8e164da9 100644
--- a/docs/source/tutorial.ipynb
+++ b/docs/source/tutorial.ipynb
@@ -25,45 +25,32 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Here, we try to use the easy-install scripts provided by `dflow` to install these dependencies. Download scripts at [dflow/scripts](https://github.com/deepmodeling/dflow/tree/master/scripts) and run  **with the privileges of the User**:\n",
-    "\n",
-    "\n",
-    "> **Note:**\n",
-    "> \n",
-    "> Don't try to run `minikube` with root privileges, otherwise an error may occur:\n",
-    "> \n",
-    "> `Exiting due to DRV_AS_ROOT: The \"docker\" driver should not be used with root privileges.`"
+    "Instead of using the easy-install scripts, it is recommended to configure your own minikube enviroment"
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": 1,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[INFO] Found docker executable at /usr/bin/docker\n",
-      "[INFO] Found minikube binary at /usr/local/bin/minikube\n",
-      "[INFO] Minikube has been started\n",
-      "--2022-08-05 21:05:16--  https://raw.githubusercontent.com/deepmodeling/dflow/master/manifests/quick-start-postgres-stable-cn.yaml\n",
-      "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.110.133, 185.199.108.133, ...\n",
-      "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... ^C\n"
-     ]
-    }
-   ],
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Installation of `minikube` is very easy, just follow the page [Minikube](https://minikube.sigs.k8s.io/docs/start/) based on your machine archetecture."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
    "source": [
-    "# for users in China, please use `-cn.sh` version to accelerate the installation process.\n",
-    "! chmod 755 install-linux-cn.sh\n",
-    "! ./install-linux-cn.sh"
+    "Then use minikube to start a k8s environment. This can be done by just type `minikube start`, but since running rid-kit requires relatively huge amount of resources, it is recommended to allocate more resources when executing `minikube start`, just as follows."
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Instead of using the easy-install scripts, it is recommended to configure your own minikube enviroment as follows"
+    "> **Note:**\n",
+    "> \n",
+    "> Don't try to run `minikube` with root privileges, otherwise an error may occur:\n",
+    "> \n",
+    "> `Exiting due to DRV_AS_ROOT: The \"docker\" driver should not be used with root privileges.`"
    ]
   },
   {
@@ -78,10 +65,10 @@
    "source": [
     "#choose the location of the minikube enviroment\n",
     "export MINIKUBE_HOME=~/.minikube  \n",
-    "#allocate enough memory in case of high parallelism\n",
+    "#allocate enough memory based on your machine in case of high parallelism\n",
     "minikube start --cpus 8 --memory 8192mb --kubernetes-version=1.23.9 --image-mirror-country='cn'\n",
     "# mount the storage path if you are using machine with shared memory storage system, change the minio host path accordingly\n",
-    "minikube start --cpus 8 --memory 8192mb --kubernetes-version=1.23.9 --mount --mount-string=\"/mnt/vepfs/jiahao/data:/data2\" --image-mirror-country='cn'\n"
+    "minikube start --cpus 8 --memory 8192mb --kubernetes-version=1.23.9 --mount --mount-string=\"/path_on_your_machine:/data2\" --image-mirror-country='cn'"
    ]
   },
   {
@@ -101,6 +88,24 @@
     "! kubectl apply -n argo -f https://raw.githubusercontent.com/deepmodeling/dflow/master/manifests/quick-start-postgres.yaml"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "If you want to mount your local path into the minio path, change the minio host path in the yaml file"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "```yaml\n",
+    "      - hostPath:\n",
+    "          path: /data2/minio\n",
+    "          type: DirectoryOrCreate\n",
+    "```"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -110,25 +115,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "minikube\n",
-      "type: Control Plane\n",
-      "host: Running\n",
-      "kubelet: Running\n",
-      "apiserver: Running\n",
-      "kubeconfig: Configured\n",
-      "\n"
-     ]
-    }
-   ],
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
-    "! minikube status"
+    "# check minikube status\n",
+    "! minikube status\n",
+    "# check k8s status in argo namespace\n",
+    "! kubectl get pods -n argo"
    ]
   },
   {
@@ -149,91 +143,12 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
-      "Processing /mnt/vepfs/yanze/dflow_project/rid-kit\n",
-      "  Preparing metadata (setup.py) ... \u001b[?25ldone\n",
-      "\u001b[?25hRequirement already satisfied: numpy in /home/yanze/miniconda3/lib/python3.9/site-packages (from rid==1.1.dev195+gb743cd0) (1.19.5)\n",
-      "Requirement already satisfied: scikit-learn in /home/yanze/miniconda3/lib/python3.9/site-packages (from rid==1.1.dev195+gb743cd0) (1.1.1)\n",
-      "Requirement already satisfied: pydflow in /home/yanze/miniconda3/lib/python3.9/site-packages (from rid==1.1.dev195+gb743cd0) (1.2.1)\n",
-      "Requirement already satisfied: tensorflow in /home/yanze/miniconda3/lib/python3.9/site-packages (from rid==1.1.dev195+gb743cd0) (2.4.1)\n",
-      "Requirement already satisfied: google in /home/yanze/miniconda3/lib/python3.9/site-packages/google-3.0.0-py3.9.egg (from rid==1.1.dev195+gb743cd0) (3.0.0)\n",
-      "Requirement already satisfied: mdtraj in /home/yanze/miniconda3/lib/python3.9/site-packages (from rid==1.1.dev195+gb743cd0) (1.9.7)\n",
-      "Requirement already satisfied: beautifulsoup4 in /home/yanze/miniconda3/lib/python3.9/site-packages (from google->rid==1.1.dev195+gb743cd0) (4.11.1)\n",
-      "Requirement already satisfied: astunparse in /home/yanze/miniconda3/lib/python3.9/site-packages (from mdtraj->rid==1.1.dev195+gb743cd0) (1.6.3)\n",
-      "Requirement already satisfied: scipy in /home/yanze/miniconda3/lib/python3.9/site-packages (from mdtraj->rid==1.1.dev195+gb743cd0) (1.8.1)\n",
-      "Requirement already satisfied: pyparsing in /home/yanze/miniconda3/lib/python3.9/site-packages (from mdtraj->rid==1.1.dev195+gb743cd0) (3.0.9)\n",
-      "Requirement already satisfied: pyyaml in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (6.0)\n",
-      "Requirement already satisfied: python-dateutil in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (2.8.2)\n",
-      "Requirement already satisfied: certifi in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (2022.6.15)\n",
-      "Requirement already satisfied: argo-workflows==5.0.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (5.0.0)\n",
-      "Requirement already satisfied: six in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (1.15.0)\n",
-      "Requirement already satisfied: typeguard in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (2.13.3)\n",
-      "Requirement already satisfied: urllib3 in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (1.26.7)\n",
-      "Requirement already satisfied: kubernetes in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (24.2.0)\n",
-      "Requirement already satisfied: cloudpickle in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (2.1.0)\n",
-      "Requirement already satisfied: jsonpickle in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (2.2.0)\n",
-      "Requirement already satisfied: minio in /home/yanze/miniconda3/lib/python3.9/site-packages (from pydflow->rid==1.1.dev195+gb743cd0) (7.1.9)\n",
-      "Requirement already satisfied: threadpoolctl>=2.0.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from scikit-learn->rid==1.1.dev195+gb743cd0) (3.1.0)\n",
-      "Requirement already satisfied: joblib>=1.0.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from scikit-learn->rid==1.1.dev195+gb743cd0) (1.1.0)\n",
-      "Requirement already satisfied: termcolor~=1.1.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (1.1.0)\n",
-      "Requirement already satisfied: h5py~=2.10.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (2.10.0)\n",
-      "Requirement already satisfied: grpcio~=1.32.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (1.32.0)\n",
-      "Requirement already satisfied: protobuf>=3.9.2 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (3.19.4)\n",
-      "Requirement already satisfied: tensorflow-estimator<2.5.0,>=2.4.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (2.4.0)\n",
-      "Requirement already satisfied: opt-einsum~=3.3.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (3.3.0)\n",
-      "Requirement already satisfied: wrapt~=1.12.1 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (1.12.1)\n",
-      "Requirement already satisfied: typing-extensions~=3.7.4 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (3.7.4.3)\n",
-      "Requirement already satisfied: wheel~=0.35 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (0.37.1)\n",
-      "Requirement already satisfied: keras-preprocessing~=1.1.2 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (1.1.2)\n",
-      "Requirement already satisfied: absl-py~=0.10 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (0.15.0)\n",
-      "Requirement already satisfied: gast==0.3.3 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (0.3.3)\n",
-      "Requirement already satisfied: tensorboard~=2.4 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (2.6.0)\n",
-      "Requirement already satisfied: google-pasta~=0.2 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (0.2.0)\n",
-      "Requirement already satisfied: flatbuffers~=1.12.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorflow->rid==1.1.dev195+gb743cd0) (1.12)\n",
-      "Requirement already satisfied: markdown>=2.6.8 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (3.3.7)\n",
-      "Requirement already satisfied: werkzeug>=0.11.15 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (2.1.2)\n",
-      "Requirement already satisfied: tensorboard-plugin-wit>=1.6.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (1.8.1)\n",
-      "Requirement already satisfied: requests<3,>=2.21.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (2.27.1)\n",
-      "Requirement already satisfied: tensorboard-data-server<0.7.0,>=0.6.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (0.6.0)\n",
-      "Requirement already satisfied: google-auth<2,>=1.6.3 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (1.35.0)\n",
-      "Requirement already satisfied: google-auth-oauthlib<0.5,>=0.4.1 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (0.4.6)\n",
-      "Requirement already satisfied: setuptools>=41.0.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (61.2.0)\n",
-      "Requirement already satisfied: soupsieve>1.2 in /home/yanze/miniconda3/lib/python3.9/site-packages (from beautifulsoup4->google->rid==1.1.dev195+gb743cd0) (2.3.1)\n",
-      "Requirement already satisfied: requests-oauthlib in /home/yanze/miniconda3/lib/python3.9/site-packages (from kubernetes->pydflow->rid==1.1.dev195+gb743cd0) (1.3.1)\n",
-      "Requirement already satisfied: websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from kubernetes->pydflow->rid==1.1.dev195+gb743cd0) (1.3.3)\n",
-      "Requirement already satisfied: cachetools<5.0,>=2.0.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from google-auth<2,>=1.6.3->tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (4.2.4)\n",
-      "Requirement already satisfied: rsa<5,>=3.1.4 in /home/yanze/miniconda3/lib/python3.9/site-packages (from google-auth<2,>=1.6.3->tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (4.8)\n",
-      "Requirement already satisfied: pyasn1-modules>=0.2.1 in /home/yanze/miniconda3/lib/python3.9/site-packages (from google-auth<2,>=1.6.3->tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (0.2.8)\n",
-      "Requirement already satisfied: importlib-metadata>=4.4 in /home/yanze/miniconda3/lib/python3.9/site-packages (from markdown>=2.6.8->tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (4.11.4)\n",
-      "Requirement already satisfied: idna<4,>=2.5 in /home/yanze/miniconda3/lib/python3.9/site-packages (from requests<3,>=2.21.0->tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (3.3)\n",
-      "Requirement already satisfied: charset-normalizer~=2.0.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from requests<3,>=2.21.0->tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (2.0.4)\n",
-      "Requirement already satisfied: oauthlib>=3.0.0 in /home/yanze/miniconda3/lib/python3.9/site-packages (from requests-oauthlib->kubernetes->pydflow->rid==1.1.dev195+gb743cd0) (3.2.0)\n",
-      "Requirement already satisfied: zipp>=0.5 in /home/yanze/miniconda3/lib/python3.9/site-packages (from importlib-metadata>=4.4->markdown>=2.6.8->tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (3.8.0)\n",
-      "Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in /home/yanze/miniconda3/lib/python3.9/site-packages (from pyasn1-modules>=0.2.1->google-auth<2,>=1.6.3->tensorboard~=2.4->tensorflow->rid==1.1.dev195+gb743cd0) (0.4.8)\n",
-      "Building wheels for collected packages: rid\n",
-      "  Building wheel for rid (setup.py) ... \u001b[?25ldone\n",
-      "\u001b[?25h  Created wheel for rid: filename=rid-1.1.dev195+gb743cd0-py3-none-any.whl size=77609 sha256=9f325b928e7c8f74cab0bda043a026949c5a427e0a7db1832043b42667f31707\n",
-      "  Stored in directory: /home/yanze/.cache/pip/wheels/ec/59/93/934a1323bb160c606bf1000cac32eae183a198305cb06cb1b0\n",
-      "Successfully built rid\n",
-      "Installing collected packages: rid\n",
-      "  Attempting uninstall: rid\n",
-      "    Found existing installation: rid 1.1.dev195+gb743cd0\n",
-      "    Uninstalling rid-1.1.dev195+gb743cd0:\n",
-      "      Successfully uninstalled rid-1.1.dev195+gb743cd0\n",
-      "Successfully installed rid-1.1.dev195+gb743cd0\n"
-     ]
-    }
-   ],
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "# the rid-kit repo path\n",
-    "! cd .. && pip install ."
+    "! pip install ."
    ]
   },
   {
@@ -256,200 +171,61 @@
    ]
   },
   {
+   "attachments": {},
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "* For `Slurm`, configure computational environments on your `Slurm` following the instructions in `Environment settings` at front page of the Rid document. With `dflow`, `rid-kit` send tasks to `Slurm` nodes from the host machines remotely without manually logging in the cluster.\n",
+    "* For `Slurm`, configure computational environments on your `Slurm` following the instructions in [Environment settings](https://github.com/PKUfjh/rid-kit/blob/dflow/docs/source/install.md). With `dflow`, `rid-kit` send tasks to `Slurm` nodes from the host machines remotely without manually logging in the cluster.\n",
     "* For local resources, just use the docker images we have built. No further manual configuration needed. We also provide `Dockerfile` of our images to enable flexible modification.\n",
-    "* For `Cloud Server`, like `Lebesgue`, use public images and no further manual configuration needed."
+    "* For `Cloud Server`, like `Bohrium`, use public images and no further manual configuration needed."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We highly recommend using `Bohrium` to do computation since no enviroment installation is required, you only need to use the public image.Specifically, the public images for rid-kit involves the following: `registry.dp.tech/public/pkufjhdocker/rid-gmx-exploration:stable` for doing `Exploration`, `registry.dp.tech/public/pkufjhdocker/rid-gmx-plumed:stable` for doing `Labeling`, `registry.dp.tech/public/pkufjhdocker/rid-gmx-tf:stable` for doing `Selection`, `registry.dp.tech/public/pkufjhdocker/rid-tf-gpu:stable` for `Training` and `registry.dp.tech/public/pkufjhdocker/rid-tf-cpu:stable` for doing other steps."
    ]
   },
   {
+   "attachments": {},
    "cell_type": "markdown",
    "metadata": {},
    "source": [
     "## Prepare machine configuration JSON.\n",
     "\n",
     "`rid-kit` uses `JSON` file to manage resources. In `machine.json`, define your own `resources` and dispatch `tasks` to them. \n",
+    "* In key `resources`, you define your own resources types. Resource names and their numbers are custom.\n",
+    "* In key `tasks`, you distribute resources you have defined to tasks of RiD. Do not change task names in `tasks` as they are fixed in codes.\n",
     "\n",
     "Generally, we would like to run low-cost tasks on cpu nodes or locally and submit high-cost tasks to `Slurm` or `Clouds`. \n",
-    "If submitting to `Slurm` environment, a `machine.json` may look like:\n",
     "\n",
-    "```JSON\n",
-    "{\n",
-    "    \"resources\": {\n",
-    "        \"local_k8s\": {\n",
-    "            \"template_config\" : {\n",
-    "                \"image\": \"pkufjhdocker/rid-tf-cpu:latest\", \n",
-    "                \"image_pull_policy\" : \"IfNotPresent\"\n",
-    "            }\n",
-    "        },\n",
-    "        \"remote_slurm\": {\n",
-    "            \"executor\": {\n",
-    "                \"type\": \"slurm\",\n",
-    "                \"host\": \"\",\n",
-    "                \"port\": 22,\n",
-    "                \"username\": \"\",\n",
-    "                \"password\": \"\",\n",
-    "                \"header\": [\n",
-    "                    \"#!/bin/bash\",\n",
-    "                    \"#SBATCH --partition GPU_2080Ti\",\n",
-    "                    \"#SBATCH -N 1\",\n",
-    "                    \"#SBATCH --ntasks-per-node 8\",\n",
-    "                    \"#SBATCH -t 120:0:0\",\n",
-    "                    \"#SBATCH --gres=gpu:1\",\n",
-    "                    \"conda init bash\",\n",
-    "                    \"source ~/.bashrc\",\n",
-    "                    \"source /path/to/rid-kit.env\"\n",
-    "                ]\n",
-    "            }\n",
-    "        }\n",
-    "    },\n",
+    "If submitting to mixed `Slurm` and `local` environment, a `machine.json` example is in [rid/template/machine_slurm_k8s.json](https://github.com/PKUfjh/rid-kit/blob/dflow/rid/template/machine_slurm_k8s.json):\n",
     "\n",
-    "    \"tasks\": {\n",
-    "        \"prep_exploration_config\": \"local_k8s\",\n",
-    "        \"run_exploration_config\": \"remote_slurm\",\n",
-    "        \"prep_label_config\": \"local_k8s\",\n",
-    "        \"run_label_config\": \"remote_slurm\",\n",
-    "        \"prep_select_config\": \"local_k8s\",\n",
-    "        \"run_select_config\": \"local_k8s\",\n",
-    "        \"prep_data_config\": \"local_k8s\",\n",
-    "        \"run_train_config\": \"remote_slurm\",\n",
-    "        \"workflow_steps_config\": \"local_k8s\"\n",
-    "    }\n",
-    "}\n",
-    "```\n",
+    "If submitting to `Bohrium` cloud platform, a `machine.json` example is in [rid/template/machine_bohrium.json](https://github.com/PKUfjh/rid-kit/blob/dflow/rid/template/machine_bohrium.json).\n",
     "\n",
-    "* In key `resources`, you define your own resources types. Resource names and their numbers are custom.\n",
-    "* In key `tasks`, you distribute resources you have defined to tasks of RiD. Do not change task names in `tasks` as they are fixed in codes.\n",
-    "If you submit jobs to `Slurm` enviroment, you will have to compile the computation enviroment on `Slurm` server for yourself. So we recommend to submit jobs to `Cloud` enviroment like `Bohrium`, in this case the `machine.json` may look like:\n",
+    "If submitting to mixed `Bohrium` and `local` platform, a `machine.json` example is in [rid/template/machine_bohrium_k8s.json](https://github.com/PKUfjh/rid-kit/blob/dflow/rid/template/machine_bohrium_k8s.json).\n",
     "\n",
-    "```JSON\n",
-    "{\n",
-    "    \"resources\": {\n",
-    "        \"local_k8s\": {\n",
-    "            \"template_config\" : {\n",
-    "                \"image\": \"pkufjhdocker/rid-tf-cpu:latest\", \n",
-    "                \"image_pull_policy\" : \"IfNotPresent\"\n",
-    "            }\n",
-    "        },\n",
-    "        \"bohrium1\": {\n",
-    "            \"executor\":{\n",
-    "            \"machine_dict\":{\n",
-    "                \"batch_type\": \"DpCloudServer\",\n",
-    "                \"context_type\": \"DpCloudServerContext\",\n",
-    "                \"local_root\" : \"./\",\n",
-    "                \"remote_profile\":{\n",
-    "                    \"email\": \"\",\n",
-    "                    \"password\": \"\",\n",
-    "                    \"program_id\": \"\",\n",
-    "                    \"input_data\":{\n",
-    "                        \"api_version\":2,\n",
-    "                        \"job_type\": \"container\",\n",
-    "                        \"log_file\": \"tmp_log\",\n",
-    "                        \"grouped\":true,\n",
-    "                        \"job_name\": \"test_rid\",\n",
-    "                        \"disk_size\": 100,\n",
-    "                        \"_instance_group_id\": 4,\n",
-    "                        \"scass_type\":\"c8_m32_1 * NVIDIA V100\",\n",
-    "                        \"platform\": \"ali\",\n",
-    "                        \"image_name\":\"pkufjhdocker/rid-gmx-exploration:latest\",\n",
-    "                        \"on_demand\":0\n",
-    "                        }\n",
-    "                }\n",
-    "            },\n",
-    "            \"resources_dict\":{\n",
-    "                \"number_node\": 1,\n",
-    "                \"cpu_per_node\": 8,\n",
-    "                \"gpu_per_node\": 1,\n",
-    "                \"queue_name\": \"GPU\",\n",
-    "                \"group_size\": 1\n",
-    "            }\n",
-    "        }\n",
-    "        },\n",
-    "        \"bohrium2\": {\n",
-    "            \"executor\":{\n",
-    "            \"machine_dict\":{\n",
-    "                \"batch_type\": \"DpCloudServer\",\n",
-    "                \"context_type\": \"DpCloudServerContext\",\n",
-    "                \"local_root\" : \"./\",\n",
-    "                \"remote_profile\":{\n",
-    "                    \"email\": \"\",\n",
-    "                    \"password\": \"\",\n",
-    "                    \"program_id\": \"\",\n",
-    "                    \"input_data\":{\n",
-    "                        \"api_version\":2,\n",
-    "                        \"job_type\": \"container\",\n",
-    "                        \"log_file\": \"tmp_log\",\n",
-    "                        \"grouped\":true,\n",
-    "                        \"job_name\": \"test_rid\",\n",
-    "                        \"disk_size\": 100,\n",
-    "                        \"_instance_group_id\": 4,\n",
-    "                        \"scass_type\":\"c8_m32_1 * NVIDIA V100\",\n",
-    "                        \"platform\": \"ali\",\n",
-    "                        \"image_name\":\"pkufjhdocker/rid-gmx-plumed:latest\",\n",
-    "                        \"on_demand\":0\n",
-    "                        }\n",
-    "                }\n",
-    "            },\n",
-    "            \"resources_dict\":{\n",
-    "                \"number_node\": 1,\n",
-    "                \"cpu_per_node\": 8,\n",
-    "                \"gpu_per_node\": 1,\n",
-    "                \"queue_name\": \"GPU\",\n",
-    "                \"group_size\": 1\n",
-    "            }\n",
-    "        }\n",
-    "        },\n",
-    "        \"bohrium3\": {\n",
-    "            \"executor\":{\n",
-    "            \"machine_dict\":{\n",
-    "                \"batch_type\": \"DpCloudServer\",\n",
-    "                \"context_type\": \"DpCloudServerContext\",\n",
-    "                \"local_root\" : \"./\",\n",
-    "                \"remote_profile\":{\n",
-    "                    \"email\": \"\",\n",
-    "                    \"password\": \"\",\n",
-    "                    \"program_id\": \"\",\n",
-    "                    \"input_data\":{\n",
-    "                        \"api_version\":2,\n",
-    "                        \"job_type\": \"container\",\n",
-    "                        \"log_file\": \"tmp_log\",\n",
-    "                        \"grouped\":true,\n",
-    "                        \"job_name\": \"test_rid\",\n",
-    "                        \"disk_size\": 100,\n",
-    "                        \"_instance_group_id\": 4,\n",
-    "                        \"scass_type\":\"c8_m32_1 * NVIDIA V100\",\n",
-    "                        \"platform\": \"ali\",\n",
-    "                        \"image_name\":\"pkufjhdocker/rid-tf-cpu:latest\",\n",
-    "                        \"on_demand\":0\n",
-    "                        }\n",
-    "                }\n",
-    "            },\n",
-    "            \"resources_dict\":{\n",
-    "                \"number_node\": 1,\n",
-    "                \"cpu_per_node\": 8,\n",
-    "                \"gpu_per_node\": 1,\n",
-    "                \"queue_name\": \"GPU\",\n",
-    "                \"group_size\": 1\n",
-    "            }\n",
-    "        }\n",
-    "        }\n",
-    "    },\n",
+    "If submitting to `Slurm` environment without `k8s` enviroment, a `machine.json` example is in [rid/template/machine_slurm_local.json](https://github.com/PKUfjh/rid-kit/blob/dflow/rid/template/machine_slurm_local.json):\n",
     "\n",
-    "    \"tasks\": {\n",
-    "        \"prep_exploration_config\": \"local_k8s\",\n",
-    "        \"run_exploration_config\": \"bohrium1\",\n",
-    "        \"prep_label_config\": \"local_k8s\",\n",
-    "        \"run_label_config\": \"bohrium2\",\n",
-    "        \"prep_select_config\": \"local_k8s\",\n",
-    "        \"run_select_config\": \"bohrium3\",\n",
-    "        \"prep_data_config\": \"local_k8s\",\n",
-    "        \"run_train_config\": \"bohrium3\",\n",
-    "        \"workflow_steps_config\": \"local_k8s\"\n",
-    "    }\n",
-    "}\n",
-    "```"
+    "If you submit jobs to `Slurm` enviroment, you will have to compile the computation enviroment on `Slurm` server for yourself. While in `Bohrium` cloud platform the computation images are all set, no extra compilation is needed. So we recommend to submit jobs to `Cloud` enviroment like `Bohrium`.\n",
+    "\n",
+    "For more details about setting the machine resources, check [Rid machine resourse](https://github.com/PKUfjh/rid-kit/blob/dflow/docs/source/rid_machine.md)"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Prepare rid configuration JSON.\n",
+    "If you want to use `gromacs` as sampler, [rid/template/rid_gmx_dih.json](https://github.com/PKUfjh/rid-kit/blob/dflow/rid/template/rid_gmx_dih.json) is an example.\n",
+    "\n",
+    "If you want to use `gromacs` with `DeepMD` potential as sampler, [rid/template/rid_gmx_dp.json](https://github.com/PKUfjh/rid-kit/blob/dflow/rid/template/rid_gmx_dp.json) is an example.\n",
+    "\n",
+    "If you want to use `lammps` with `DeepMD` potential as sampler, [rid/template/rid_lmp_dp.json](https://github.com/PKUfjh/rid-kit/blob/dflow/rid/template/rid_lmp_dp.json) is an example.\n",
+    "\n",
+    "For more details about setting the rid configuration, check [Rid configuration](https://github.com/PKUfjh/rid-kit/blob/dflow/docs/source/rid_configuration.md)"
    ]
   },
   {
@@ -465,19 +241,9 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "2022-08-06 19:34:09 | INFO | rid.entrypoint.server | Port \"agro-server\" has been launched and running.\n",
-      "2022-08-06 19:34:09 | INFO | rid.entrypoint.server | Port \"minio-server\" has been launched and running.\n",
-      "2022-08-06 19:34:09 | INFO | rid.entrypoint.server | Port \"minio-ui\" has been launched and running.\n"
-     ]
-    }
-   ],
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "! rid port-forward"
    ]
@@ -489,27 +255,16 @@
     "In this case, we try to explore the phase space of alanine dipeptide. Prepare your initial conformation files in `.gro` format, topology file in `.top` format and configuration file `rid.json`. For convenience, we have prepared on at `rid/template/rid.json`. \n",
     "Remember also provide your own forcefield files. Collect all these files into a directory and feed its path to `rid-kit` by flag `-i`.\n",
     "\n",
-    "A minimum case was prepared in `rid-kit/tests/data/000`. Then run `rid submit`:"
+    "A minimum case was prepared in `rid-kit/tests/data/000`. After configurating your `machine.json`, then run `rid submit`:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "2022-08-06 19:29:53 | INFO | rid.entrypoint.main | Preparing RiD ...\n",
-      "Workflow has been submitted (ID: reinforced-dynamics-cx4rd)\n",
-      "2022-08-06 19:30:17 | INFO | rid.entrypoint.main | The task is displayed on \"https://127.0.0.1:2746\".\n",
-      "2022-08-06 19:30:17 | INFO | rid.entrypoint.main | Artifacts (Files) are listed on \"https://127.0.0.1:9001\".\n"
-     ]
-    }
-   ],
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
-    "! rid submit -i ../tests/data/000 -c ../rid/template/rid.json -m /mnt/vepfs/yanze/dflow_project/test_dflow/template/machine.json"
+    "! rid submit -i /tests/data/000 -c /rid/template/rid_gmx_dih.json -m /rid/template/machine_bohrium_local.json"
    ]
   },
   {
@@ -530,26 +285,9 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "2022-08-06 19:35:45 | INFO | rid.entrypoint.cli | \n",
-      "\n",
-      "\tReinforced Dynamics Workflow\n",
-      "\n",
-      "NAME                        STATUS    AGE   DURATION   PRIORITY\n",
-      "reinforced-dynamics-cx4rd   Running   5m    5m         0\n",
-      "reinforced-dynamics-pi65n   Running   8h    8h         0\n",
-      "reinforced-dynamics-yk3wl   Failed    8h    2m         0\n",
-      "reinforced-dynamics-bsc7j   Failed    9h    31m        0\n",
-      "\n"
-     ]
-    }
-   ],
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "! rid ls"
    ]
@@ -595,11 +333,34 @@
    "source": [
     "! rid resubmit -i your_dir -c path_to_rid.json -m path_to_machine.json Workflow-ID"
    ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "If you want to resubmit from a particular `iteration` and `step`:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "! rid resubmit -i your_dir -c path_to_rid.json -m path_to_machine.json Workflow-ID -t ITERATION-ID -p STEP-KEY"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The `Workflow-ID` is something like `reinforced-dynamics-jq4jn` appeared in the argo UI. If the workflow is archived, its name will appear as somethinig like `a8463748-e15a-4f2c-882b-bfd981a76dac`. If you want to resubmit a archived workflow, you have to provide the archived ID rather than the initial ID. The `ITERATION-ID` is just `n`th iteration the workflow has been executed. The `STEP-KEY` in rid includes the following steps: `prep-exploration`, `run-exploration`, `prep-select`, `run-select`, `prep-label`, `run-label`, `post-label`, `collect`, `merge`, `train`."
+   ]
   }
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3.9.7",
+   "display_name": "Python 3",
    "language": "python",
    "name": "python3"
   },
@@ -613,12 +374,12 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.9.7"
+   "version": "3.8.6 (v3.8.6:db455296be, Sep 23 2020, 13:31:39) \n[Clang 6.0 (clang-600.0.57)]"
   },
   "orig_nbformat": 4,
   "vscode": {
    "interpreter": {
-    "hash": "756c52790f8e5882129aad9694b42ed16f03b52c1b174c5fc0cf3de0242c9f19"
+    "hash": "082e9a3bcad0a290d0001e938aa60b99250c6c2ef33a923c00b70f9826caf4b7"
    }
   }
  },
diff --git a/install/DeePFE.cpp b/install/DeePFE.cpp
index 6964f4e5..f4d44716 100644
--- a/install/DeePFE.cpp
+++ b/install/DeePFE.cpp
@@ -220,10 +220,10 @@ namespace PLMD {
       for(unsigned ii=0; ii<getNumberOfArguments(); ++ii) {
 	const double f=trust * avg_f[ii];
 	// cout << "trust " << trust << " orig f " << avg_f[ii] << " real f "  << f << endl;
-	setOutputForce(ii,f);
+	setOutputForce(ii,-f);
 	totf2+=f*f;
       }
-      setBias(ene);
+      setBias(-ene);
       valueForce2->set(totf2);
       valueFstd->set(avg_std_f);
     }
diff --git a/install/gmx_exploration.dockerfile b/install/gmx_exploration.dockerfile
index 167442f4..ed035ae6 100644
--- a/install/gmx_exploration.dockerfile
+++ b/install/gmx_exploration.dockerfile
@@ -1,4 +1,4 @@
-# Run "docker build --network=host --tag *** ." to build the image
+# Run "docker build -f gmx_exploration.dockerfile --network=host --tag rid-gmx-exploration ." to build the image
 
 # Build from nvidia/cuda iamge
 FROM nvidia/cuda:11.0.3-cudnn8-devel-ubuntu20.04
@@ -20,8 +20,8 @@ ENV CC=/usr/bin/gcc
 ENV CXX=/usr/bin/g++
 
 # Install Miniconda package manager.
-RUN wget -O /root/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
-#ADD Miniconda3-latest-Linux-x86_64.sh /root/miniconda.sh
+#RUN wget -O /root/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
+ADD Miniconda3-latest-Linux-x86_64.sh /root/miniconda.sh
 RUN bash /root/miniconda.sh -b -p /opt/conda
 RUN rm /root/miniconda.sh
 ENV PATH /opt/conda/bin:$PATH
@@ -30,47 +30,52 @@ RUN conda init bash && source /root/.bashrc \
     && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/  \
     && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/  \
     && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge  \
-    && conda install python=3.9 libtensorflow_cc=2.6.2=*cuda110* dpdata nccl mdtraj numpy -y
+    && conda install python=3.9 libtensorflow_cc=2.6.2=*cuda110* tensorflow=2.6.2=*cuda110* dpdata nccl -y
 
 # Solve library inconsistency
 RUN source activate base && rm ${CONDA_PREFIX}/lib/libtinfo.so* && ln -s /usr/lib/x86_64-linux-gnu/libtinfo.so.6 ${CONDA_PREFIX}/lib/libtinfo.so.6 \
     && rm ${CONDA_PREFIX}/lib/libcurl.so* && ln -s /usr/lib/x86_64-linux-gnu/libcurl.so.4 ${CONDA_PREFIX}/lib/libcurl.so.4
 
+# Install necessary packages
+RUN source activate base && pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && pip install matplotlib==3.6.1 cython
+
+# set enviroment variables
+ENV LIBRARY_PATH "/opt/conda/lib:$LIBRARY_PATH"
+ENV LD_LIBRARY_PATH "/opt/conda/lib:$LD_LIBRARY_PATH"
+
 # Compile Plumed from source
-RUN wget -O /root/plumed-2.8.0.tgz https://github.com/plumed/plumed2/releases/download/v2.8.0/plumed-2.8.0.tgz
-RUN tar zxvf /root/plumed-2.8.0.tgz
-#ADD plumed-2.8.0.tgz /root/
-COPY DeePFE.cpp /root/plumed-2.8.0/src/bias/
+RUN wget -O /root/plumed-2.8.1.tgz https://github.com/plumed/plumed2/releases/download/v2.8.1/plumed-2.8.1.tgz
+RUN tar zxvf /root/plumed-2.8.1.tgz
+#ADD plumed-2.8.1.tgz /root/
+COPY DeePFE.cpp /root/plumed-2.8.1/src/bias/
 RUN source activate base \
-    && cd /root/plumed-2.8.0 \
+    && cd /root/plumed-2.8.1 \
     && ./configure --prefix=$CONDA_PREFIX \
-                   --enable-cxx=14 \
-                   CXXFLAGS="-std=gnu++14 -I $CONDA_PREFIX/include/" \
-                   LDFLAGS=" -L$CONDA_PREFIX/lib -ltensorflow_cc -ltensorflow_framework -Wl,-rpath=$CONDA_PREFIX/lib/" \
+                   CXXFLAGS="-O3 -I$CONDA_PREFIX/include/" \
+                   LDFLAGS=" -L$CONDA_PREFIX/lib -ltensorflow_cc -ltensorflow_framework" \
     && make -j 6 \
     && make install \
-    && rm -rf /root/plumed-2.8.0 /root/plumed-2.8.0.tgz
+    && rm -rf /root/plumed-2.8.1 /root/plumed-2.8.1.tgz
 
-ENV LIBRARY_PATH "/opt/conda/lib:$LIBRARY_PATH"
-ENV LD_LIBRARY_PATH "/opt/conda/lib:$LD_LIBRARY_PATH"
 ENV PLUMED_KERNEL "/opt/conda/lib/libplumedKernel.@SOEXT@"
 
 # Compile gromacs from source
-RUN wget -O root/gromacs-2021.4.tar.gz https://github.com/gromacs/gromacs/archive/refs/tags/v2021.4.tar.gz --no-check-certificate
-RUN tar zxvf /root/gromacs-2021.4.tar.gz
-#ADD gromacs-2021.4.tar.gz /root/
+RUN wget -O root/gromacs-2022.4.tar.gz https://github.com/gromacs/gromacs/archive/refs/tags/v2022.4.tar.gz --no-check-certificate
+RUN tar zxvf /root/gromacs-2022.4.tar.gz
+#ADD gromacs-2022.4.tar.gz /root/
 RUN source activate base && echo $PLUMED_KERNEL && ls $CONDA_PREFIX/lib/ | grep plumed \
-    && cd /root/gromacs-2021.4 \
-    && echo -e "3\n" | plumed patch -p \
+    && cd /root/gromacs-2022.4 \
+    && echo -e "4\n" | plumed patch -p \
     && mkdir build \
     && cd build \
     && cmake .. -DGMX_BUILD_OWN_FFTW=ON \
                 -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \
-                -DGMX_GPU=CUDA  \
-                -DGMX_SIMD=avx_512 \
+                -DGMX_GPU=CUDA\
     && make -j 8 \
-    && make install \
-    && rm -rf /root/gromacs-2021.4 /root/gromacs-2021.4.tar.gz \
+    && make install
+
+#delete source files
+RUN  rm -rf /root/gromacs-2022.4 /root/gromacs-2022.4.tar.gz \
     && rm -rf /var/lib/apt/lists/*
 
 # Environment variables set manually since github actions will overwrite entrypoints
diff --git a/install/gmx_plumed.dockerfile b/install/gmx_plumed.dockerfile
index 1980173f..7eeba8ef 100644
--- a/install/gmx_plumed.dockerfile
+++ b/install/gmx_plumed.dockerfile
@@ -1,4 +1,4 @@
-# Run "docker build --network=host --tag *** ." to build the image
+# Run "docker build -f gmx_plumed.dockerfile --network=host --tag rid-gmx-plumed ." to build the image
 
 # Build from nvidia/cuda iamge
 FROM nvidia/cuda:11.0.3-cudnn8-devel-ubuntu20.04
@@ -30,48 +30,49 @@ RUN conda init bash && source /root/.bashrc \
     && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/  \
     && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/  \
     && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge  \
-    && conda install python=3.9 dpdata mdtraj numpy -y
+    && conda install python=3.9 dpdata mdtraj parmed matplotlib numpy -y
 
 # Solve library inconsistency
 RUN source activate base && rm ${CONDA_PREFIX}/lib/libtinfo.so* && ln -s /usr/lib/x86_64-linux-gnu/libtinfo.so.6 ${CONDA_PREFIX}/lib/libtinfo.so.6 \
     && rm ${CONDA_PREFIX}/lib/libcurl.so* && ln -s /usr/lib/x86_64-linux-gnu/libcurl.so.4 ${CONDA_PREFIX}/lib/libcurl.so.4
 
+# Install necessary packages
+RUN source activate base && pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && pip install cython
+
 ENV LIBRARY_PATH "/opt/conda/lib:$LIBRARY_PATH"
 ENV LD_LIBRARY_PATH "/opt/conda/lib:$LD_LIBRARY_PATH"
 
 # Compile Plumed from source
-RUN wget -O /root/plumed-2.8.0.tgz https://github.com/plumed/plumed2/releases/download/v2.8.0/plumed-2.8.0.tgz
-RUN tar zxvf /root/plumed-2.8.0.tgz
-#ADD plumed-2.8.0.tgz /root/
-# RUN tar zxvf /root/plumed-2.8.0.tgz
+RUN wget -O /root/plumed-2.8.1.tgz https://github.com/plumed/plumed2/releases/download/v2.8.1/plumed-2.8.1.tgz
+RUN tar zxvf /root/plumed-2.8.1.tgz
+#ADD plumed-2.8.1.tgz /root/
 RUN source activate base \
-    && cd /root/plumed-2.8.0 \
+    && cd /root/plumed-2.8.1 \
     && ./configure --prefix=$CONDA_PREFIX \
                    --enable-modules=all\
     && make -j 6 \
     && make install \
-    && rm -rf /root/plumed-2.8.0 /root/plumed-2.8.0.tgz
+    && rm -rf /root/plumed-2.8.1 /root/plumed-2.8.1.tgz
 
-ENV LIBRARY_PATH "/opt/conda/lib:$LIBRARY_PATH"
-ENV LD_LIBRARY_PATH "/opt/conda/lib:$LD_LIBRARY_PATH"
 ENV PLUMED_KERNEL "/opt/conda/lib/libplumedKernel.@SOEXT@"
 
 # Compile gromacs from source
-RUN wget -O root/gromacs-2021.4.tar.gz https://github.com/gromacs/gromacs/archive/refs/tags/v2021.4.tar.gz --no-check-certificate
-RUN tar zxvf /root/gromacs-2021.4.tar.gz
-#ADD gromacs-2021.4.tar.gz /root/
+RUN wget -O root/gromacs-2022.4.tar.gz https://github.com/gromacs/gromacs/archive/refs/tags/v2022.4.tar.gz --no-check-certificate
+RUN tar zxvf /root/gromacs-2022.4.tar.gz
+#ADD gromacs-2022.4.tar.gz /root/
 RUN source activate base && echo $PLUMED_KERNEL && ls $CONDA_PREFIX/lib/ | grep plumed \
-    && cd /root/gromacs-2021.4 \
-    && echo -e "3\n" | plumed patch -p \
+    && cd /root/gromacs-2022.4 \
+    && echo -e "4\n" | plumed patch -p \
     && mkdir build \
     && cd build \
     && cmake .. -DGMX_BUILD_OWN_FFTW=ON \
                 -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \
-                -DGMX_GPU=CUDA  \
-                -DGMX_SIMD=avx_512 \
+                -DGMX_GPU=CUDA\
     && make -j 8 \
-    && make install \
-    && rm -rf /root/gromacs-2021.4 /root/gromacs-2021.4.tar.gz \
+    && make install
+
+#delete source files
+RUN  rm -rf /root/gromacs-2022.4 /root/gromacs-2022.4.tar.gz \
     && rm -rf /var/lib/apt/lists/*
 
 # Environment variables set manually since github actions will overwrite entrypoints
diff --git a/install/gmx_tf.Dockerfile b/install/gmx_tf.Dockerfile
new file mode 100644
index 00000000..f636f97b
--- /dev/null
+++ b/install/gmx_tf.Dockerfile
@@ -0,0 +1,21 @@
+# Run "docker build --network=host --tag *** ." to build the image
+
+# Build from gromacs/gromacs iamge
+FROM gromacs/gromacs:2022.2
+SHELL ["/bin/bash", "-c"]
+
+# Add public key of the image to trusted set of keys
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC
+
+# Change mirrors for apt-get to aliyun 
+RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
+
+# Install essentials
+RUN DEBIAN_FRONTEND=noninteractive apt-get update\
+    && DEBIAN_FRONTEND=noninteractive apt-get install -y pip python3-tk
+
+# Install python packages
+RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \
+    && pip install tensorflow dpdata mdtraj scikit-learn numpy parmed
+
+SHELL ["/bin/bash", "-c"]
\ No newline at end of file
diff --git a/install/lmp_exploration_dp.dockerfile b/install/lmp_exploration_dp.dockerfile
new file mode 100644
index 00000000..70393ca2
--- /dev/null
+++ b/install/lmp_exploration_dp.dockerfile
@@ -0,0 +1,89 @@
+# Run "docker build -f lmp_dp.dockerfile --network=host --tag *** ." to build the image
+
+# Build from nvidia/cuda iamge
+FROM nvidia/cuda:11.0.3-cudnn8-devel-ubuntu20.04
+SHELL ["/bin/bash", "-c"]
+
+# Add public key of the image to trusted set of keys
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC
+
+# Change mirrors for apt-get to aliyun 
+RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
+
+# Install essentials
+RUN DEBIAN_FRONTEND=noninteractive apt-get update\
+    && DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common \
+    && DEBIAN_FRONTEND=noninteractive add-apt-repository -y ppa:ubuntu-toolchain-r/test \
+    && DEBIAN_FRONTEND=noninteractive apt-get update \
+    && echo -e "6\n1\n" | DEBIAN_FRONTEND=noninteractive apt-get install -y wget gcc g++ cmake vim libstdc++6 python3-tk
+ENV CC=/usr/bin/gcc
+ENV CXX=/usr/bin/g++
+
+# Install Miniconda package manager.
+#RUN wget -O /root/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
+ADD Miniconda3-latest-Linux-x86_64.sh /root/miniconda.sh
+RUN bash /root/miniconda.sh -b -p /opt/conda
+RUN rm /root/miniconda.sh
+ENV PATH /opt/conda/bin:$PATH
+RUN conda init bash && source /root/.bashrc \
+    && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/  \
+    && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/  \
+    && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/  \
+    && conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge  \
+    && conda install python=3.9 libtensorflow_cc=2.6.2=*cuda110* dpdata nccl numpy -y
+
+# Solve library inconsistency
+RUN source activate base && rm ${CONDA_PREFIX}/lib/libtinfo.so* && ln -s /usr/lib/x86_64-linux-gnu/libtinfo.so.6 ${CONDA_PREFIX}/lib/libtinfo.so.6 \
+    && rm ${CONDA_PREFIX}/lib/libcurl.so* && ln -s /usr/lib/x86_64-linux-gnu/libcurl.so.4 ${CONDA_PREFIX}/lib/libcurl.so.4
+
+ENV LIBRARY_PATH "/opt/conda/lib:$LIBRARY_PATH"
+ENV LD_LIBRARY_PATH "/opt/conda/lib:$LD_LIBRARY_PATH"
+
+# Compile deepmd-kit
+ADD ./deepmd-kit /root/deepmd-kit
+RUN cd /root/deepmd-kit/source\
+    && mkdir ./build\
+    && cd ./build\
+    && cmake -DTENSORFLOW_ROOT=/opt/conda -DCMAKE_INSTALL_PREFIX=/opt/conda -DUSE_CUDA_TOOLKIT=TRUE ..\
+    && make -j4\
+    && make install\
+    && make lammps
+
+# Compile Plumed from source
+# RUN wget -O /root/plumed-2.8.0.tgz https://github.com/plumed/plumed2/releases/download/v2.8.0/plumed-2.8.0.tgz
+# RUN tar zxvf /root/plumed-2.8.0.tgz
+ADD plumed-2.8.0.tgz /root/
+COPY DeePFE.cpp /root/plumed-2.8.0/src/bias/
+RUN source activate base \
+    && cd /root/plumed-2.8.0 \
+    && ./configure --prefix=$CONDA_PREFIX \
+                   --enable-cxx=14 \
+                   CXXFLAGS="-std=gnu++14 -I $CONDA_PREFIX/include/" \
+                   LDFLAGS=" -L$CONDA_PREFIX/lib -ltensorflow_cc -ltensorflow_framework -Wl,-rpath=$CONDA_PREFIX/lib/" \
+    && make -j 6 \
+    && make install \
+    && rm -rf /root/plumed-2.8.0 /root/plumed-2.8.0.tgz
+
+ENV PLUMED_KERNEL "/opt/conda/lib/libplumedKernel.so"
+
+# Compile gromacs from source
+# RUN wget -O root/gromacs-2021.4.tar.gz https://github.com/gromacs/gromacs/archive/refs/tags/v2021.4.tar.gz --no-check-certificate
+# RUN tar zxvf /root/gromacs-2021.4.tar.gz
+ADD stable_23Jun2022_update1.tar.gz /root/
+RUN source activate base && echo $PLUMED_KERNEL && ls $CONDA_PREFIX/lib/ | grep plumed \
+    && cd /root/lammps-stable_23Jun2022_update1/src\
+    && cp -r /root/deepmd-kit/source/build/USER-DEEPMD .\
+    && make yes-user-deepmd\
+    && make yes-extra-fix\
+    && make yes-extra-dump\
+    && make yes-kspace\
+    && make yes-molecule\
+    && make yes-rigid\
+    && make lib-plumed args="-p $CONDA_PREFIX -m runtime"\
+    && make yes-plumed\
+    && make serial -j4
+
+# manage the executables and source code
+RUN source activate base && ln -s /root/lammps-stable_23Jun2022_update1/src/lmp_serial $CONDA_PREFIX/bin/lmp_serial
+
+SHELL ["/bin/bash", "-c"]
\ No newline at end of file
diff --git a/install/tf_cpu.dockerfile b/install/tf_cpu.dockerfile
index 6fe869a0..0e0500cb 100644
--- a/install/tf_cpu.dockerfile
+++ b/install/tf_cpu.dockerfile
@@ -4,7 +4,7 @@ FROM tensorflow/tensorflow:latest
 SHELL ["/bin/bash", "-c"]
 
 RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \
-    && pip install dpdata mdtraj scikit-learn
+    && pip install dpdata matplotlib mdtraj scikit-learn parmed
 
 RUN apt-get install -y python3-tk
 SHELL ["/bin/bash", "-c"]
\ No newline at end of file
diff --git a/install/tf_gpu.Dockerfile b/install/tf_gpu.Dockerfile
new file mode 100644
index 00000000..54abe127
--- /dev/null
+++ b/install/tf_gpu.Dockerfile
@@ -0,0 +1,10 @@
+
+# Build from tensorflow/tensorflow gpu version
+FROM tensorflow/tensorflow:devel-gpu
+SHELL ["/bin/bash", "-c"]
+
+RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \
+    && pip install dpdata matplotlib mdtraj scikit-learn
+
+RUN apt-get install -y python3-tk
+SHELL ["/bin/bash", "-c"]
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index fcb3977d..1781e309 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,11 @@
-numpy
+protobuf==3.20.1
 scikit-learn
 pydflow
-tensorflow==2.6.2
+tensorflow==2.8.0
 google
 mdtraj
 dpdata
+parmed
+dpdispatcher
+lbg
+matplotlib
\ No newline at end of file
diff --git a/rid/common/gromacs/gmx_constant.py b/rid/common/gromacs/gmx_constant.py
index 487d1f2e..fbb9fddb 100644
--- a/rid/common/gromacs/gmx_constant.py
+++ b/rid/common/gromacs/gmx_constant.py
@@ -2,6 +2,7 @@
 gmx_prep_cmd = "gmx grompp"
 gmx_run_cmd = "gmx mdrun"
 gmx_trjconv_cmd = "gmx trjconv"
+gmx_traj_cmd = 'gmx traj'
 
 mdp_parameters = {
     "md": {
diff --git a/rid/common/gromacs/mdp.py b/rid/common/gromacs/mdp.py
index 707b4039..ea459b16 100644
--- a/rid/common/gromacs/mdp.py
+++ b/rid/common/gromacs/mdp.py
@@ -77,7 +77,7 @@ def make_md_mdp_string(
     ):
     update_dict = {}
     for item in gmx_config:
-        if item not in ["nt", "ntmpi", "max_warning", "output_freq", "output_mode"]:
+        if item not in ["method","temperature","nt", "kappas","ntmpi", "max_warning", "output_freq", "output_mode","type","dp_model"]:
             update_dict[item] = gmx_config[item]
     update_dict.update(modify_output(freq = gmx_config["output_freq"], output_mode=gmx_config["output_mode"]))
             
diff --git a/rid/common/gromacs/trjconv.py b/rid/common/gromacs/trjconv.py
index e16b73fa..8429c828 100644
--- a/rid/common/gromacs/trjconv.py
+++ b/rid/common/gromacs/trjconv.py
@@ -1,7 +1,8 @@
 import os, sys
 import logging
-from typing import Sequence
-from rid.common.gromacs.gmx_constant import gmx_trjconv_cmd
+from typing import Optional,Sequence
+from rid.common.gromacs.gmx_constant import gmx_trjconv_cmd, gmx_traj_cmd
+from rid.constants import gmx_coord_name, gmx_force_name
 from rid.utils import list_to_string
 from rid.utils import run_command
 
@@ -14,6 +15,46 @@
 )
 logger = logging.getLogger(__name__)
 
+def generate_coords(
+    trr: str,
+    top: str,
+    out_coord: str = gmx_coord_name,
+    output_group: str  = "System",
+    index: Optional[str] = None
+):
+    cmd_list = gmx_traj_cmd.split(" ")
+    cmd_list += ["-f", str(trr)]
+    cmd_list += ["-s", str(top)]
+    if index is not None:
+        cmd_list += ["-n", str(index)]
+    cmd_list += ["-ox", out_coord]
+    logger.info(list_to_string(cmd_list, " "))
+    return_code, out, err = run_command(
+        cmd_list,
+        stdin=f"{output_group}\n"
+    )
+    assert return_code == 0, err
+    
+    
+def generate_forces(
+    trr: str,
+    top: str,
+    out_force: str = gmx_force_name,
+    output_group: str  = "System",
+    index: Optional[str] = None
+):
+    cmd_list = gmx_traj_cmd.split(" ")
+    cmd_list += ["-f", str(trr)]
+    cmd_list += ["-s", str(top)]
+    if index is not None:
+        cmd_list += ["-n", str(index)]
+    cmd_list += ["-of", out_force]
+    logger.info(list_to_string(cmd_list, " "))
+    return_code, out, err = run_command(
+        cmd_list,
+        stdin=f"{output_group}\n"
+    )
+    assert return_code == 0, err
 
 def slice_trjconv(
         xtc: str,
diff --git a/rid/common/lammps/command.py b/rid/common/lammps/command.py
new file mode 100644
index 00000000..bb027404
--- /dev/null
+++ b/rid/common/lammps/command.py
@@ -0,0 +1,9 @@
+import dpdata
+
+def final_dump(
+    dump: str,
+    selected_idx,
+    output_format:str
+):
+    traj = dpdata.System(dump, fmt="lammps/dump")
+    traj.to('lammps/lmp', output_format, frame_idx=selected_idx)
\ No newline at end of file
diff --git a/rid/common/mol.py b/rid/common/mol.py
index 7413f182..bc9f070c 100644
--- a/rid/common/mol.py
+++ b/rid/common/mol.py
@@ -24,6 +24,12 @@ def _zip_dict(resi_indices, atom_indices):
         dihedral_dict[resi_idx] = atom_indices[idx].tolist()
     return dihedral_dict
 
+def distance(r1,r2):
+    d = 0
+    for index in range(3):
+        d += (r1[index] - r2[index])**2
+    d = np.sqrt(d)
+    return d
 
 def get_dihedral_info(file_path: str):
     traj = md.load(file_path)
@@ -66,6 +72,18 @@ def get_dihedral_from_resid(file_path: str, selected_resid: List[int]) -> Dict:
     logger.info(f"{num_cv} CVs have been created.")
     return selected_dihedral_angle
 
+def get_distance_from_atomid(file_path: str, selected_atomid: List[int]) -> Dict:
+    if len(selected_atomid) == 0:
+        return {}
+    top = md.load(file_path)
+    selected_distance = {}
+    for sid in selected_atomid:
+        assert len(sid) == 2, "No valid distance list created."
+        d_cv = md.compute_distances(top,atom_pairs=np.array([sid[0]-1,sid[1]-1]).reshape(-1,2),periodic=True)[0][0]
+        selected_distance["%s %s"%(sid[0],sid[1])] = d_cv
+    num_cv = len(selected_distance.keys())
+    logger.info(f"{num_cv} CVs have been created.")
+    return selected_distance
 
 def slice_xtc_mdtraj(
         xtc: str,
@@ -132,12 +150,4 @@ def slice_dump(
             output_format=output
         )
     else:
-        raise RuntimeError("Unknown Style for Slicing Trajectory.")
-    
-def final_dump(
-    dump: str,
-    selected_idx,
-    output_format:str
-):
-    traj = dpdata.System(dump, fmt="lammps/dump")
-    traj.to('lammps/lmp', output_format, frame_idx=selected_idx)
\ No newline at end of file
+        raise RuntimeError("Unknown Style for Slicing Trajectory.")
\ No newline at end of file
diff --git a/rid/common/plumed/__init__.py b/rid/common/plumed/__init__.py
index 3daf3f4c..1872c6f8 100644
--- a/rid/common/plumed/__init__.py
+++ b/rid/common/plumed/__init__.py
@@ -1,6 +1,8 @@
 from rid.common.plumed.make_plumed import (
     make_deepfe_plumed,
     make_restraint_plumed,
+    make_constraint_plumed,
+    make_distance_list_from_file,
     get_cv_name
 )
 from rid.common.plumed.check_plumed import check_deepfe_input
\ No newline at end of file
diff --git a/rid/common/plumed/make_plumed.py b/rid/common/plumed/make_plumed.py
index 0025462c..ae2513fc 100644
--- a/rid/common/plumed/make_plumed.py
+++ b/rid/common/plumed/make_plumed.py
@@ -5,14 +5,17 @@
 import numpy as np
 from typing import List, Union, Tuple, Dict, Optional, Sequence
 from rid.utils import list_to_string
-from rid.common.mol import get_dihedral_from_resid
+from rid.common.mol import get_dihedral_from_resid, get_distance_from_atomid
 from rid.common.plumed.plumed_constant import (
     dihedral_name,
+    distance_name,
     dihedral_def_from_atoms,
+    distance_def_from_atoms,
     deepfe_def,
     print_def,
     restraint_def,
-    restraint_prefix
+    upper_def,
+    lower_def
 )
 
 
@@ -78,6 +81,21 @@ def make_deepfe_bias(
         arg = cv_string
     )
 
+def make_print_bias(
+        name_list,
+        stride,
+        file_name,
+        model_list
+    ) -> str:
+    if len(model_list) != 0:
+        name_list.insert(0,"dpfe.bias")
+    else:
+        name_list.insert(0,name_list[0])
+    return print_def.format(
+        stride = stride,
+        arg = list_to_string(name_list, ","),
+        file = file_name
+    )
 
 def make_print(
         name_list,
@@ -102,35 +120,16 @@ def user_plumed_def(cv_file, pstride, pfile):
     logger.info("Custom CVs are created from plumed files.")
     ret = ""
     cv_names = []
-    ispath = False
     print_content = None
+    print("cv_file name",cv_file)
     with open(cv_file, 'r') as fp:
         for line in fp.readlines():
             if ("PRINT" in line) and ("#" not in line):
                 print_content = line + "\n"
+                cv_names = line.split()[2].split("=")[1].split(",")
                 break
-            ret += line + "\n"
-            if (":" in line) and ("#" not in line):
-                cv_type = line.split(":")[1].strip().split(" ")[0]
-                print("CV type!", cv_type)
-                if cv_type != "PATH" and not ispath:
-                    cv_names.append(("{}".format(line.split(":")[0])).strip())
-                elif cv_type == "PATH":
-                    ispath = True
-                    cv_names = []
-                    cv_names.append(("{}.spath".format(line.split(":")[0])).strip())
-                    cv_names.append(("{}.zpath".format(line.split(":")[0])).strip())
-            elif ("LABEL" in line) and ("#" not in line):
-                cv_type = line.split("LABEL=")[0].strip().split(" ")[0]
-                print("CV type!", cv_type)
-                if cv_type != "PATH" and not ispath:
-                    cv_names.append(("{}".format(line.split("LABEL=")[1])).strip())
-                elif cv_type == "PATH":
-                    ispath = True
-                    cv_names = []
-                    cv_names.append(("{}.spath".format(line.split("LABEL=")[1].strip())))
-                    cv_names.append(("{}.zpath".format(line.split("LABEL=")[1].strip())))
-    if ret == "" or cv_names == "":
+            ret += line
+    if ret == "" or cv_names == []:
         raise RuntimeError("Invalid customed plumed files.")
     if print_content is not None:
         assert len(print_content.split(",")) == len(cv_names), "There are {} CVs defined in the plumed file, while {} CVs are printed.".format(len(cv_names), len(print_content.split(",")) )
@@ -152,6 +151,16 @@ def make_torsion(
         a3 = atom_list[2], a4 = atom_list[3],
     )
 
+def make_distance(
+        name: str,
+        atom_list: List[Union[int, str]]
+    ) -> str:
+    assert len(atom_list) == 2, f"Make sure distance defined by 2 atoms, not {len(atom_list)}."
+    return distance_def_from_atoms.format(
+        name = name,
+        a1 = atom_list[0], a2 = atom_list[1]
+    )
+
 
 def make_torsion_name(resid: int, angid: int):
     return dihedral_name.format(
@@ -159,6 +168,11 @@ def make_torsion_name(resid: int, angid: int):
         angid = angid
     )
 
+def make_distance_name(atomids: list):
+    return distance_name.format(
+    atomid1 = int(atomids[0]),
+    atomid2 = int(atomids[1])
+    )
 
 def make_torsion_list(
         dihedral_info: Dict,
@@ -175,6 +189,21 @@ def make_torsion_list(
             ))
     return torsion_list, torsion_name_list
 
+def make_distance_list(
+        distance_info: Dict,
+    ) -> Tuple[List, List]:
+    distance_list = []
+    distance_name_list = []
+    for atomids in distance_info.keys():
+        dis = distance_info[atomids]
+        atom_list = atomids.split(" ")
+        distance_name = make_distance_name(atomids=atom_list)
+        distance_name_list.append(distance_name)
+        distance_list.append(make_distance(
+            name = distance_name,
+            atom_list=atom_list)
+        )
+    return distance_list, distance_name_list
 
 def make_torsion_list_from_file(
         file_path: str,
@@ -185,24 +214,60 @@ def make_torsion_list_from_file(
     assert len(cv_info.keys()) > 0, "No valid CVs created."
     return make_torsion_list(cv_info)
 
+def make_distance_list_from_file(
+        file_path: str,
+        selected_atomid: List[int]
+    ) -> Tuple[List, List]:
+    cv_info = get_distance_from_atomid(file_path, selected_atomid)
+    logger.info("Create CVs (distance) from selected atom ids.")
+    assert len(cv_info.keys()) > 0, "No valid CVs created."
+    return make_distance_list(cv_info)
+
+def make_wall_list(
+    cv_name_list,
+    wall_list,
+    iteration
+):
+    ret = ""
+    for index in range(len(wall_list)):
+        if wall_list[index][0].upper() != "NONE":
+            start = float(wall_list[index][1])
+            end = float(wall_list[index][2])
+            iterations = float(wall_list[index][3])
+            kappa = float(wall_list[index][4])
+            iteration_index = (iteration-1) % (iterations)
+            at = start + (end - start)/(iterations-1)*(iteration_index)
+            if wall_list[index][0].upper() == "UPPER":
+                line = upper_def.format(arg = cv_name_list[index], at=at,kappa=kappa,name="upper%s"%index)
+            elif wall_list[index][0].upper() == "LOWER":
+                line = lower_def.format(arg = cv_name_list[index], at=at,kappa=kappa,name="lower%s"%index)
+            ret += line+"\n"
+    return ret
 
 def make_restraint_plumed(
         conf: Optional[str] = None,
         cv_file: Optional[List[str]] = None,
         selected_resid: Optional[List[int]] = None,
+        selected_atomid: Optional[List[int]] = None,
         kappa: Union[int, float, Sequence, np.ndarray] = 0.5,
         at: Union[int, float, Sequence, np.ndarray] = 1.0,
         stride: int = 100,
         output: str = "plm.out",
-        mode: str = "torsion"    
+        mode: str = "torsion"
     ):
     content_list = []
     if mode == "torsion":
         cv_content_list, cv_name_list = \
             make_torsion_list_from_file(conf, selected_resid)
         content_list += cv_content_list
+    elif mode == "distance":
+        cv_content_list, cv_name_list = \
+            make_distance_list_from_file(conf, selected_atomid)
+        content_list += cv_content_list
     elif mode == "custom":
-        ret, cv_name_list, _ = user_plumed_def(cv_file[0], stride, output)
+        for cv_file_ in cv_file:
+            if not os.path.basename(cv_file_).endswith("pdb"):
+                ret, cv_name_list, _ = user_plumed_def(cv_file_, stride, output)
         content_list.append(ret)
     else:
         raise RuntimeError("Unknown mode for making plumed files.")
@@ -215,34 +280,69 @@ def make_restraint_plumed(
         cv_name_list, kappa, at
     )
     content_list += res_list
-    content_list.append( make_print(cv_name_list, stride, output) )
+    content_list.append(make_print(cv_name_list, stride, output))
     return list_to_string(content_list, split_sign="\n")
 
+def make_constraint_plumed(
+        conf: Optional[str] = None,
+        cv_file: Optional[List[str]] = None,
+        selected_atomid: Optional[List[int]] = None,
+        stride: int = 100,
+        output: str = "plm.out",
+        mode: str = "distance"
+    ):
+    content_list = []
+    if mode == "distance":
+        cv_content_list, cv_name_list = \
+            make_distance_list_from_file(conf, selected_atomid)
+        content_list += cv_content_list
+    elif mode == "custom":
+        for cv_file_ in cv_file:
+            if not os.path.basename(cv_file_).endswith("pdb"):
+                ret, cv_name_list, _ = user_plumed_def(cv_file_, stride, output)
+        content_list.append(ret)
+    else:
+        raise RuntimeError("Unknown mode for making plumed files.")
+
+    content_list.append(make_print(cv_name_list, stride, output))
+    return list_to_string(content_list, split_sign="\n")
 
 def make_deepfe_plumed(
         conf: Optional[str] = None,
         cv_file: Optional[List[str]] = None,
         selected_resid: Optional[List[int]] = None,
+        selected_atomid: Optional[List[int]] = None,
         trust_lvl_1: float = 1.0,
         trust_lvl_2: float = 2.0,
         model_list: List[str] = ["graph.pb"],
         stride: int = 100,
         output: str = "plm.out",
-        mode: str = "torsion"
+        mode: str = "torsion",
+        wall_list: Optional[List[str]] = None,
+        iteration: Optional[str] = None
     ):
     content_list = []
     if mode == "torsion":
         cv_content_list, cv_name_list = \
             make_torsion_list_from_file(conf, selected_resid)
         content_list += cv_content_list
+    elif mode == "distance":
+        cv_content_list, cv_name_list = \
+            make_distance_list_from_file(conf, selected_atomid)
+        content_list += cv_content_list
     elif mode == "custom":
-        ret, cv_name_list, _ = user_plumed_def(cv_file[0], stride, output)
+        for cv_file_ in cv_file:
+            if not os.path.basename(cv_file_).endswith("pdb"):
+                ret, cv_name_list, _ = user_plumed_def(cv_file_, stride, output)
         content_list.append(ret)
     else:
         raise RuntimeError("Unknown mode for making plumed files.")
+    if wall_list is not None:
+        ret = make_wall_list(cv_name_list, wall_list, iteration)
+        content_list.append(ret)
     deepfe_string = make_deepfe_bias(cv_name_list, trust_lvl_1, trust_lvl_2, model_list)
     content_list.append(deepfe_string)
-    content_list.append(make_print(cv_name_list, stride, output))
+    content_list.append(make_print_bias(cv_name_list, stride, output, model_list))
     return list_to_string(content_list, split_sign="\n")
 
 
@@ -250,14 +350,19 @@ def get_cv_name(
         conf: Optional[str] = None,
         cv_file: Optional[List[str]] = None,
         selected_resid: Optional[List[int]] = None,
+        selected_atomid: Optional[List[int]] = None,
         stride: int = 100,
         mode: str = "torsion"
     ):
     if mode == "torsion":
         _, cv_name_list = \
             make_torsion_list_from_file(conf, selected_resid)
+    elif mode == "distance":
+        _, cv_name_list = make_distance_list_from_file(conf, selected_atomid)
     elif mode == "custom":
-        _, cv_name_list, _ = user_plumed_def(cv_file[0], stride, "test.out")
+        for cv_file_ in cv_file:
+            if not os.path.basename(cv_file_).endswith("pdb"):
+                _, cv_name_list, _ = user_plumed_def(cv_file_, stride, "test.out")
     else:
         raise RuntimeError("Unknown mode for making plumed files.")
     return cv_name_list
diff --git a/rid/common/plumed/plumed_constant.py b/rid/common/plumed/plumed_constant.py
index 5509484a..c802ee66 100644
--- a/rid/common/plumed/plumed_constant.py
+++ b/rid/common/plumed/plumed_constant.py
@@ -1,6 +1,10 @@
 dihedral_name = "dih-{resid:03d}-{angid:02d}"
 dihedral_def_from_atoms = "{name}: TORSION ATOMS={a1},{a2},{a3},{a4}"
+distance_name = "dis-{atomid1:05d}-{atomid2:05d}"
+distance_def_from_atoms = "{name}: DISTANCE ATOMS={a1},{a2}"
 deepfe_def = "dpfe: DEEPFE TRUST_LVL_1={trust_lvl_1} TRUST_LVL_2={trust_lvl_2} MODEL={model} ARG={arg}"
 print_def = "PRINT STRIDE={stride} ARG={arg} FILE={file}"
 restraint_def = "{name}: RESTRAINT ARG={arg} KAPPA={kappa} AT={at}"
-restraint_prefix = "res"
\ No newline at end of file
+restraint_prefix = "res"
+upper_def = "UPPER_WALLS ARG={arg} AT={at} KAPPA={kappa} LABEL={name}"
+lower_def = "LOWER_WALLS ARG={arg} AT={at} KAPPA={kappa} LABEL={name}"
\ No newline at end of file
diff --git a/rid/constants.py b/rid/constants.py
index 73b0bec5..cf658bec 100644
--- a/rid/constants.py
+++ b/rid/constants.py
@@ -11,7 +11,7 @@
 cv_init_label = "cv_init_{walker:03d}_{idx:d}.out"
 model_devi_name = "model_devi.txt"
 label_task_pattern = "{:03d}"
-force_out = "forces.out"
+cv_force_out = "cv_forces.out"
 data_new = "data.new.npy"
 data_old = "data.old.npy"
 data_raw = "data.raw.npy"
@@ -38,6 +38,8 @@
 sel_gro_name = "conf_{walker:03d}_{idx:d}.gro"
 sel_gro_name_gmx = "conf_.gro"
 gmx_conf_out = "confout.gro"
+gmx_coord_name = "coord.xvg"
+gmx_force_name = "force.xvg"
 
 # Lammps file names
 lmp_conf_name = "conf.lmp"
@@ -49,6 +51,7 @@
 
 # Tensorflow files
 tf_model_name = "model_{tag}.pb"
+train_log = "log_{tag}"
 dp_model_name = "dp.pb"
 model_tag_fmt = "{idx:03d}"
 N_grid = 100
@@ -56,8 +59,16 @@
 # Dp config file
 dp_config_name = "dp_config"
 
+# figure file name
+mf_fig = "mf_average.png"
+bias_fig = "bias.png"
+model_devi_fig = "model_devi.png"
+cluster_fig = "cluster.png"
+train_fig = "train_{tag}.png"
+
 # Units
-kbT = (8.617343E-5) * 300
+kb = 8.617333E-5
+kbT = (8.617333E-5) * 300
 beta = 1.0 / kbT
 f_cvt = 96.485
 inverse_f_cvt = 1 / f_cvt
diff --git a/rid/entrypoint/info.py b/rid/entrypoint/info.py
index f004cd5d..aa693285 100644
--- a/rid/entrypoint/info.py
+++ b/rid/entrypoint/info.py
@@ -20,6 +20,7 @@
 
 Welcome to reach out to :
     yanze039@mit.edu
+    jiahaofan@pku.edu.cn
     wangdd@dp.tech
     
 """
\ No newline at end of file
diff --git a/rid/entrypoint/label.py b/rid/entrypoint/label.py
new file mode 100644
index 00000000..9e56fe76
--- /dev/null
+++ b/rid/entrypoint/label.py
@@ -0,0 +1,172 @@
+from distutils.command.config import dump_file
+import json
+from pathlib import Path
+from typing import List, Union, Optional
+from rid.utils import load_json
+from copy import deepcopy
+import os
+
+from dflow import (
+    Workflow,
+    Step,
+    upload_artifact
+)
+
+from dflow.python import upload_packages
+from rid import SRC_ROOT
+upload_packages.append(SRC_ROOT)
+
+from rid.utils import normalize_resources
+from rid.superop.label import Label
+from rid.op.prep_label import PrepLabel, CheckLabelInputs
+from rid.op.run_label import RunLabel
+from rid.constants import label_task_pattern
+
+def label_rid(
+        confs: Union[str, List[str]],
+        topology: Optional[str],
+        rid_config: str,
+        machine_config: str,
+        models: Optional[Union[str, List[str]]] = None,
+        forcefield: Optional[str] = None,
+        index_file: Optional[str] = None,
+        dp_files: Optional[List[str]] = [],
+        otherfiles: Optional[List[str]] = None
+    ):
+    with open(machine_config, "r") as mcg:
+        machine_config_dict = json.load(mcg)
+    resources = machine_config_dict["resources"]
+    tasks = machine_config_dict["tasks"]
+    normalized_resources = {}
+    for resource_type in resources.keys():
+        normalized_resources[resource_type] = normalize_resources(resources[resource_type])
+
+    label_op = Label(
+        "label",
+        CheckLabelInputs,
+        PrepLabel,
+        RunLabel,
+        prep_config = normalized_resources[tasks["prep_label_config"]],
+        run_config = normalized_resources[tasks["run_label_config"]],
+        retry_times=1)
+
+    if isinstance(confs, str):
+        confs_artifact = upload_artifact(Path(confs), archive=None)
+    elif isinstance(confs, List):
+        confs_artifact = upload_artifact([Path(p) for p in confs], archive=None)
+    else:
+        raise RuntimeError("Invalid type of `confs`.")
+
+    if index_file is None:
+        index_file_artifact = None
+    else:
+        index_file_artifact = upload_artifact(Path(index_file), archive=None)
+    
+    jdata = deepcopy(load_json(rid_config))
+    cv_config = jdata["CV"]
+    label_config = jdata["LabelMDConfig"]
+    
+    inputfiles = []
+    if "inputfile" in jdata["ExploreMDConfig"]:
+        inputfiles.append(jdata["ExploreMDConfig"]["inputfile"])
+        if "inputfile" in jdata["LabelMDConfig"]:
+            inputfiles.append(jdata["LabelMDConfig"]["inputfile"])
+               
+    fe_models = []
+    assert isinstance(jdata["init_models"],list), "model input should be list."
+    for model in jdata["init_models"]:
+        fe_models.append(model)
+    
+    cvfiles = []
+    if "cv_file" in jdata["CV"]:
+        assert isinstance(jdata["CV"]["cv_file"],list), "CV file input should be list."
+        for file in jdata["CV"]["cv_file"]:
+            cvfiles.append(file)
+            
+    dp_models = []
+    if "dp_model" in jdata["ExploreMDConfig"]:
+        assert isinstance(jdata["ExploreMDConfig"]["dp_model"],list), "model input should be list."
+        for model in jdata["ExploreMDConfig"]["dp_model"]:
+            dp_models.append(model)
+                
+    inputfile_list = []
+    cvfile_list = []
+    model_list = []
+    dpfile_list = []
+    if otherfiles is not None:
+        for file in otherfiles:
+            if os.path.basename(file) in inputfiles:
+                inputfile_list.append(file)
+            elif os.path.basename(file) in cvfiles:
+                cvfile_list.append(file)
+                
+    if dp_files is not None:
+        for dp_file in dp_files:
+            dpfile_list.append(dp_file)
+            
+    if models is not None:
+        for model in models:
+            if os.path.basename(model) in fe_models:
+                model_list.append(model)
+            elif os.path.basename(model) in dp_models:
+                dpfile_list.append(model)
+            
+    if len(inputfile_list) == 0:
+        inputfile_artifact = None
+    else:
+        inputfile_artifact = upload_artifact([Path(p) for p in inputfile_list], archive=None)
+        
+    if len(model_list) == 0:
+        models_artifact = None
+    else:
+        models_artifact = upload_artifact([Path(p) for p in model_list], archive=None)
+        
+    if len(cvfile_list) == 0:
+        cv_file_artifact = None
+    else:
+        cv_file_artifact = upload_artifact([Path(p) for p in cvfile_list], archive=None)
+        
+    if len(dpfile_list) == 0:
+        dp_files_artifact = None
+    elif isinstance(dp_files, List):
+        dp_files_artifact = upload_artifact([Path(p) for p in dpfile_list], archive=None)
+    else:
+        raise RuntimeError("Invalid type of `dp_files`.")
+    
+    if forcefield is None:
+        forcefield_artifact = None
+    else:
+        forcefield_artifact = upload_artifact(Path(forcefield), archive=None)
+        
+    if topology is None:
+        top_artifact = None
+    else:
+        top_artifact = upload_artifact(Path(topology), archive=None)
+
+    conf_tags = []
+    for index in range(len(confs)):
+        conf_tags.append({Path(confs[index]).name:label_task_pattern.format(index)})
+        
+    rid_steps = Step("rid-label",
+            label_op,
+            artifacts={
+                "topology": top_artifact,
+                "models": models_artifact,
+                "forcefield": forcefield_artifact,
+                "inputfile": inputfile_artifact,
+                "confs": confs_artifact,
+                "at": None,
+                "index_file": index_file_artifact,
+                "dp_files": dp_files_artifact,
+                "cv_file": cv_file_artifact
+            },
+            parameters={
+                "label_config": label_config,
+                "cv_config": cv_config,
+                "conf_tags": conf_tags,
+                "block_tag" : "000"
+            },
+        )
+    wf = Workflow("rid-labeling", pod_gc_strategy="OnPodSuccess", parallelism=50)
+    wf.add(rid_steps)
+    wf.submit()
diff --git a/rid/entrypoint/main.py b/rid/entrypoint/main.py
index 25b50ccd..fccb7cc2 100644
--- a/rid/entrypoint/main.py
+++ b/rid/entrypoint/main.py
@@ -7,6 +7,10 @@
 )
 import rid
 
+if os.getenv('DFLOW_DEBUG'):
+    from dflow import config
+    config["mode"] = "debug"
+    
 NUMEXPR_MAX_THREADS = os.getenv("NUMEXPR_MAX_THREADS")
 if NUMEXPR_MAX_THREADS is None:
     NUMEXPR_MAX_THREADS = 8
@@ -23,6 +27,8 @@
 
 from .submit import submit_rid
 from .resubmit import resubmit_rid
+from .label import label_rid
+from .relabel import relabel_rid
 from .info import information
 from .server import forward_ports
 from .cli import rid_ls, rid_rm
@@ -105,7 +111,7 @@ def main_parser() -> argparse.ArgumentParser:
     # resubmit
     parser_rerun = subparsers.add_parser(
         "resubmit",
-        help="Submit RiD workflow",
+        help="Resubmit RiD workflow",
         formatter_class=argparse.ArgumentDefaultsHelpFormatter,
     )
     parser_rerun.add_argument(
@@ -120,6 +126,12 @@ def main_parser() -> argparse.ArgumentParser:
     parser_rerun.add_argument(
         "--machine", "-m", help="Machine configuration.", dest="machine"
     )
+    parser_rerun.add_argument(
+        "--iteration", "-t", help="restart from t-th iteration.", default = None, dest="iteration"
+    )
+    parser_rerun.add_argument(
+        "--pod", "-p", help="restart from the pod.", default = None, dest="pod"
+    )
     
     # Explore
     parser_exp = subparsers.add_parser(
@@ -137,6 +149,41 @@ def main_parser() -> argparse.ArgumentParser:
         "--machine", "-m", help="Machine configuration.", dest="machine"
     )
     
+    # Label
+    parser_label = subparsers.add_parser(
+        "label",
+        help="labeling MD workflow",
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+    )
+    parser_label.add_argument(
+        "--mol", "-i", help="Initial conformation files.", dest="mol",
+    )
+    parser_label.add_argument(
+        "--config", "-c", help="RiD configuration.", dest="config"
+    )
+    parser_label.add_argument(
+        "--machine", "-m", help="Machine configuration.", dest="machine"
+    )
+    
+    # Relabel
+    parser_relabel = subparsers.add_parser(
+        "relabel",
+        help="Resubmit labeling MD workflow",
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+    )
+    parser_relabel.add_argument(
+        "WORKFLOW_ID", help="Workflow ID."
+    )
+    parser_relabel.add_argument(
+        "--mol", "-i", help="Initial conformation files.", dest="mol",
+    )
+    parser_relabel.add_argument(
+        "--config", "-c", help="RiD configuration.", dest="config"
+    )
+    parser_relabel.add_argument(
+        "--machine", "-m", help="Machine configuration.", dest="machine"
+    )
+    
     # train
     parser_train = subparsers.add_parser(
         "train",
@@ -204,6 +251,7 @@ def parse_submit(args):
     index_file = None
     dp_files = []
     models = []
+    data_file = None
     otherfiles = []
     for file in allfiles:
         if os.path.basename(file).endswith("ff"):
@@ -214,6 +262,8 @@ def parse_submit(args):
             top_file = file
         elif os.path.basename(file).endswith("pb"):
             models.append(file)
+        elif os.path.basename(file).endswith("npy"):
+            data_file = file
         elif os.path.basename(file).endswith("ndx"):
             index_file = file
         elif os.path.basename(file).endswith("json") or os.path.basename(file).endswith("raw"):
@@ -221,7 +271,7 @@ def parse_submit(args):
         else:
             otherfiles.append(file)
         
-    return confs, top_file, models, forcefield, index_file, dp_files, otherfiles
+    return confs, top_file, models, forcefield, index_file, data_file, dp_files, otherfiles
 
 
 def log_ui():
@@ -233,7 +283,7 @@ def main():
     args = parse_args()
     if args.command == "submit":
         logger.info("Preparing RiD ...")
-        confs, top_file, models, forcefield, index_file, dp_files, otherfiles = parse_submit(args)
+        confs, top_file, models, forcefield, index_file, data_file, dp_files, otherfiles = parse_submit(args)
         submit_rid(
             confs = confs,
             topology = top_file,
@@ -242,22 +292,26 @@ def main():
             models = models,
             forcefield = forcefield,
             index_file = index_file,
+            data_file = data_file,
             dp_files = dp_files,
             otherfiles = otherfiles
         )
         log_ui()
     elif args.command == "resubmit":
         logger.info("Preparing RiD ...")
-        confs, top_file, models, forcefield, index_file, dp_files, otherfiles = parse_submit(args)
+        confs, top_file, models, forcefield, index_file, data_file, dp_files, otherfiles = parse_submit(args)
         resubmit_rid(
             workflow_id=args.WORKFLOW_ID,
             confs = confs,
             topology = top_file,
             rid_config = args.config,
             machine_config = args.machine,
+            iteration = args.iteration,
+            pod = args.pod,
             models = models,
             forcefield = forcefield,
             index_file = index_file,
+            data_file = data_file,
             dp_files = dp_files,
             otherfiles = otherfiles
         )
@@ -265,6 +319,37 @@ def main():
     elif args.command == "explore":
         logger.info("RiD Exploration.")
         return None
+    elif args.command == "label":
+        logger.info("Labeling MD ...")
+        confs, top_file, models, forcefield, index_file, data_files, dp_files, otherfiles = parse_submit(args)
+        label_rid(
+            confs = confs,
+            topology = top_file,
+            rid_config = args.config,
+            machine_config = args.machine,
+            models = models,
+            forcefield = forcefield,
+            index_file = index_file,
+            dp_files = dp_files,
+            otherfiles = otherfiles
+        )
+        log_ui()
+    elif args.command == "relabel":
+        logger.info("Labeling MD ...")
+        confs, top_file, models, forcefield, index_file, data_files, dp_files, otherfiles = parse_submit(args)
+        relabel_rid(
+            workflow_id=args.WORKFLOW_ID,
+            confs = confs,
+            topology = top_file,
+            rid_config = args.config,
+            machine_config = args.machine,
+            models = models,
+            forcefield = forcefield,
+            index_file = index_file,
+            dp_files = dp_files,
+            otherfiles = otherfiles
+        )
+        log_ui()
     elif args.command == "port-forward":
         forward_ports()
     elif args.command == "ls":
diff --git a/rid/entrypoint/relabel.py b/rid/entrypoint/relabel.py
new file mode 100644
index 00000000..0333a053
--- /dev/null
+++ b/rid/entrypoint/relabel.py
@@ -0,0 +1,183 @@
+from distutils.command.config import dump_file
+import json
+from pathlib import Path
+from typing import List, Union, Optional
+from rid.utils import load_json
+from copy import deepcopy
+import os
+
+from dflow import (
+    Workflow,
+    Step,
+    upload_artifact
+)
+
+from dflow.python import upload_packages
+from rid import SRC_ROOT
+upload_packages.append(SRC_ROOT)
+
+from rid.utils import normalize_resources
+from rid.superop.label import Label
+from rid.op.prep_label import PrepLabel, CheckLabelInputs
+from rid.op.run_label import RunLabel
+from rid.constants import label_task_pattern
+
+def relabel_rid(
+        workflow_id: str,
+        confs: Union[str, List[str]],
+        topology: Optional[str],
+        rid_config: str,
+        machine_config: str,
+        models: Optional[Union[str, List[str]]] = None,
+        forcefield: Optional[str] = None,
+        index_file: Optional[str] = None,
+        dp_files: Optional[List[str]] = [],
+        otherfiles: Optional[List[str]] = None
+    ):
+    with open(machine_config, "r") as mcg:
+        machine_config_dict = json.load(mcg)
+    resources = machine_config_dict["resources"]
+    tasks = machine_config_dict["tasks"]
+    normalized_resources = {}
+    for resource_type in resources.keys():
+        normalized_resources[resource_type] = normalize_resources(resources[resource_type])
+
+    label_op = Label(
+        "label",
+        CheckLabelInputs,
+        PrepLabel,
+        RunLabel,
+        prep_config = normalized_resources[tasks["prep_label_config"]],
+        run_config = normalized_resources[tasks["run_label_config"]],
+        retry_times=None)
+
+    if isinstance(confs, str):
+        confs_artifact = upload_artifact(Path(confs), archive=None)
+    elif isinstance(confs, List):
+        confs_artifact = upload_artifact([Path(p) for p in confs], archive=None)
+    else:
+        raise RuntimeError("Invalid type of `confs`.")
+
+    if index_file is None:
+        index_file_artifact = None
+    else:
+        index_file_artifact = upload_artifact(Path(index_file), archive=None)
+    
+    jdata = deepcopy(load_json(rid_config))
+    cv_config = jdata["CV"]
+    label_config = jdata["LabelMDConfig"]
+    
+    inputfiles = []
+    if "inputfile" in jdata["ExploreMDConfig"]:
+        inputfiles.append(jdata["ExploreMDConfig"]["inputfile"])
+        if "inputfile" in jdata["LabelMDConfig"]:
+            inputfiles.append(jdata["LabelMDConfig"]["inputfile"])
+               
+    fe_models = []
+    assert isinstance(jdata["init_models"],list), "model input should be list."
+    for model in jdata["init_models"]:
+        fe_models.append(model)
+    
+    cvfiles = []
+    if "cv_file" in jdata["CV"]:
+        assert isinstance(jdata["CV"]["cv_file"],list), "CV file input should be list."
+        for file in jdata["CV"]["cv_file"]:
+            cvfiles.append(file)
+            
+    dp_models = []
+    if "dp_model" in jdata["ExploreMDConfig"]:
+        assert isinstance(jdata["ExploreMDConfig"]["dp_model"],list), "model input should be list."
+        for model in jdata["ExploreMDConfig"]["dp_model"]:
+            dp_models.append(model)
+                
+    inputfile_list = []
+    cvfile_list = []
+    model_list = []
+    dpfile_list = []
+    if otherfiles is not None:
+        for file in otherfiles:
+            if os.path.basename(file) in inputfiles:
+                inputfile_list.append(file)
+            elif os.path.basename(file) in cvfiles:
+                cvfile_list.append(file)
+                
+    if dp_files is not None:
+        for dp_file in dp_files:
+            dpfile_list.append(dp_file)
+            
+    if models is not None:
+        for model in models:
+            if os.path.basename(model) in fe_models:
+                model_list.append(model)
+            elif os.path.basename(model) in dp_models:
+                dpfile_list.append(model)
+            
+    if len(inputfile_list) == 0:
+        inputfile_artifact = None
+    else:
+        inputfile_artifact = upload_artifact([Path(p) for p in inputfile_list], archive=None)
+        
+    if len(model_list) == 0:
+        models_artifact = None
+    else:
+        models_artifact = upload_artifact([Path(p) for p in model_list], archive=None)
+        
+    if len(cvfile_list) == 0:
+        cv_file_artifact = None
+    else:
+        cv_file_artifact = upload_artifact([Path(p) for p in cvfile_list], archive=None)
+        
+    if len(dpfile_list) == 0:
+        dp_files_artifact = None
+    elif isinstance(dp_files, List):
+        dp_files_artifact = upload_artifact([Path(p) for p in dpfile_list], archive=None)
+    else:
+        raise RuntimeError("Invalid type of `dp_files`.")
+    
+    if forcefield is None:
+        forcefield_artifact = None
+    else:
+        forcefield_artifact = upload_artifact(Path(forcefield), archive=None)
+        
+    if topology is None:
+        top_artifact = None
+    else:
+        top_artifact = upload_artifact(Path(topology), archive=None)
+
+    conf_tags = []
+    for index in range(len(confs)):
+        conf_tags.append({Path(confs[index]).name:label_task_pattern.format(index)})
+        
+    rid_steps = Step("rid-label",
+            label_op,
+            artifacts={
+                "topology": top_artifact,
+                "models": models_artifact,
+                "forcefield": forcefield_artifact,
+                "inputfile": inputfile_artifact,
+                "confs": confs_artifact,
+                "at": None,
+                "index_file": index_file_artifact,
+                "dp_files": dp_files_artifact,
+                "cv_file": cv_file_artifact
+            },
+            parameters={
+                "label_config": label_config,
+                "cv_config": cv_config,
+                "conf_tags": conf_tags,
+                "block_tag" : "000"
+            },
+        )
+    
+    old_workflow = Workflow(id=workflow_id)
+    all_steps = old_workflow.query_step()
+
+    succeeded_steps = []
+    for step in all_steps:
+        if step["type"] == "Pod":
+            if step["phase"] == "Succeeded":
+                succeeded_steps.append(step)
+                        
+    wf = Workflow("rid-labeling", pod_gc_strategy="OnPodSuccess", parallelism=50)
+    wf.add(rid_steps)
+    wf.submit(reuse_step=succeeded_steps)
diff --git a/rid/entrypoint/resubmit.py b/rid/entrypoint/resubmit.py
index 93f205e4..f6fe7944 100644
--- a/rid/entrypoint/resubmit.py
+++ b/rid/entrypoint/resubmit.py
@@ -24,8 +24,11 @@ def resubmit_rid(
         topology: Optional[str],
         rid_config: str,
         machine_config: str,
+        iteration: Optional[str] = None,
+        pod: Optional[str] = None,
         models: Optional[Union[str, List[str]]] = None,
         index_file: Optional[str] = None,
+        data_file: Optional[str] = None,
         dp_files: Optional[List[str]] = None,
         forcefield: Optional[str] = None,
         otherfiles: Optional[List[str]] = None
@@ -47,7 +50,8 @@ def resubmit_rid(
         run_select_config = normalized_resources[tasks["run_select_config"]],
         prep_data_config = normalized_resources[tasks["prep_data_config"]],
         run_train_config = normalized_resources[tasks["run_train_config"]],
-        workflow_steps_config = normalized_resources[tasks["workflow_steps_config"]]
+        workflow_steps_config = normalized_resources[tasks["workflow_steps_config"]],
+        retry_times=None
     )
 
     if isinstance(confs, str):
@@ -57,58 +61,77 @@ def resubmit_rid(
     else:
         raise RuntimeError("Invalid type of `confs`.")
     
-    if models is None:
-        models_artifact = None
-    elif isinstance(models, str):
-        models_artifact = upload_artifact(Path(models), archive=None)
-    elif isinstance(models, List):
-        models_artifact = upload_artifact([Path(p) for p in models], archive=None)
-    else:
-        raise RuntimeError("Invalid type of `confs`.")
-    
     if index_file is None:
         index_file_artifact = None
     else:
         index_file_artifact = upload_artifact(Path(index_file), archive=None)
     
     jdata = load_json(rid_config)
+    
     inputfiles = []
     if "inputfile" in jdata["ExploreMDConfig"]:
         inputfiles.append(jdata["ExploreMDConfig"]["inputfile"])
         if "inputfile" in jdata["LabelMDConfig"]:
             inputfiles.append(jdata["LabelMDConfig"]["inputfile"])
+               
+    fe_models = []
+    assert isinstance(jdata["init_models"],list), "model input should be list."
+    for model in jdata["init_models"]:
+        fe_models.append(model)
     
     cvfiles = []
     if "cv_file" in jdata["CV"]:
         assert isinstance(jdata["CV"]["cv_file"],list), "CV file input should be list."
         for file in jdata["CV"]["cv_file"]:
             cvfiles.append(file)
-    
+            
+    dp_models = []
+    if "dp_model" in jdata["ExploreMDConfig"]:
+        assert isinstance(jdata["ExploreMDConfig"]["dp_model"],list), "model input should be list."
+        for model in jdata["ExploreMDConfig"]["dp_model"]:
+            dp_models.append(model)
+                
     inputfile_list = []
     cvfile_list = []
+    model_list = []
+    dpfile_list = []
     if otherfiles is not None:
         for file in otherfiles:
             if os.path.basename(file) in inputfiles:
                 inputfile_list.append(file)
             elif os.path.basename(file) in cvfiles:
                 cvfile_list.append(file)
+                
+    if dp_files is not None:
+        for dp_file in dp_files:
+            dpfile_list.append(dp_file)
+            
+    if models is not None:
+        for model in models:
+            if os.path.basename(model) in fe_models:
+                model_list.append(model)
+            elif os.path.basename(model) in dp_models:
+                dpfile_list.append(model)
             
     if len(inputfile_list) == 0:
         inputfile_artifact = None
     else:
         inputfile_artifact = upload_artifact([Path(p) for p in inputfile_list], archive=None)
         
+    if len(model_list) == 0:
+        models_artifact = None
+    else:
+        models_artifact = upload_artifact([Path(p) for p in model_list], archive=None)
+        
     if len(cvfile_list) == 0:
         cv_file_artifact = None
     else:
         cv_file_artifact = upload_artifact([Path(p) for p in cvfile_list], archive=None)
-    
-    if dp_files is None:
+        
+    if len(dpfile_list) == 0:
         dp_files_artifact = None
-    elif isinstance(dp_files, str):
-        dp_files_artifact = upload_artifact(Path(dp_files), archive=None)
     elif isinstance(dp_files, List):
-        dp_files_artifact = upload_artifact([Path(p) for p in dp_files], archive=None)
+        dp_files_artifact = upload_artifact([Path(p) for p in dpfile_list], archive=None)
     else:
         raise RuntimeError("Invalid type of `dp_files`.")
     
@@ -116,11 +139,16 @@ def resubmit_rid(
         forcefield_artifact = None
     else:
         forcefield_artifact = upload_artifact(Path(forcefield), archive=None)
-    
+        
     if topology is None:
         top_artifact = None
     else:
         top_artifact = upload_artifact(Path(topology), archive=None)
+        
+    if data_file is None:
+        data_artifact = None
+    else:
+        data_artifact = upload_artifact(Path(data_file), archive=None)
     rid_config = upload_artifact(Path(rid_config), archive=None)
 
     rid_steps = Step(
@@ -134,6 +162,7 @@ def resubmit_rid(
             "forcefield": forcefield_artifact,
             "index_file": index_file_artifact,
             "inputfile": inputfile_artifact,
+            "data_file": data_artifact,
             "dp_files": dp_files_artifact,
             "cv_file": cv_file_artifact
         },
@@ -143,11 +172,31 @@ def resubmit_rid(
     all_steps = old_workflow.query_step()
 
     succeeded_steps = []
+    restart_flag = 1
     for step in all_steps:
         if step["type"] == "Pod":
-            if step["phase"] == "Succeeded":
-                if step["key"] != "prepare-rid":
+            # if step["phase"] == "Succeeded":
+            if step["key"] != "prepare-rid" and step["key"] != "init-recorder":
+                pod_key = step["key"]
+                if pod_key is not None:
+                    pod_key_list = pod_key.split("-")
+                    pod_iter = int(pod_key_list[1])
+                    pod_step = "-".join(pod_key_list[2:-1])
+                    if iteration is not None:
+                        if pod is not None:
+                            if pod_iter == int(iteration) and pod_step == pod:
+                                restart_flag = 0
+                        else:
+                            if pod_iter == int(iteration):
+                                restart_flag = 0
+                    else:
+                        if step["phase"] != "Succeeded":
+                            restart_flag = 0
+                        else:
+                            restart_flag = 1
+                
+                if restart_flag == 1:
                     succeeded_steps.append(step)
-    wf = Workflow("reinforced-dynamics", pod_gc_strategy="OnPodSuccess", parallelism=30)
+    wf = Workflow("reinforced-dynamics-continue", pod_gc_strategy="OnPodSuccess", parallelism=50)
     wf.add(rid_steps)
     wf.submit(reuse_step=succeeded_steps)
diff --git a/rid/entrypoint/submit.py b/rid/entrypoint/submit.py
index 6056b8d6..618993e1 100644
--- a/rid/entrypoint/submit.py
+++ b/rid/entrypoint/submit.py
@@ -22,7 +22,6 @@
 from rid.superop.label import Label
 from rid.op.prep_label import PrepLabel, CheckLabelInputs
 from rid.op.run_label import RunLabel
-from rid.op.calc_mf import CalcMF
 from rid.superop.selector import Selector
 from rid.op.prep_select import PrepSelect
 from rid.op.run_select import RunSelect
@@ -43,7 +42,8 @@ def prep_rid_op(
     run_select_config,
     prep_data_config,
     run_train_config,
-    workflow_steps_config
+    workflow_steps_config,
+    retry_times
     ):
 
     exploration_op = Exploration(
@@ -51,29 +51,32 @@ def prep_rid_op(
         PrepExplore,
         RunExplore,
         prep_exploration_config,
-        run_exploration_config)
+        run_exploration_config,
+        retry_times=retry_times)
 
     label_op = Label(
         "label",
         CheckLabelInputs,
         PrepLabel,
         RunLabel,
-        CalcMF,
         prep_label_config,
-        run_label_config)
+        run_label_config,
+        retry_times=retry_times)
 
     select_op = Selector(
         "select",
         PrepSelect,
         RunSelect,
         prep_select_config,
-        run_select_config)
+        run_select_config,
+        retry_times=retry_times)
 
     data_op = DataGenerator(
         "gen-data",
         CollectData,
         MergeData,
-        prep_data_config)
+        prep_data_config,
+        retry_times=retry_times)
 
     init_block_op = InitBlock(
         "init-block",
@@ -83,6 +86,7 @@ def prep_rid_op(
         data_op,
         TrainModel,
         run_train_config,
+        retry_times=retry_times
     )
 
     block_op = IterBlock(
@@ -94,7 +98,8 @@ def prep_rid_op(
         AdjustTrustLevel,
         TrainModel,
         workflow_steps_config,
-        run_train_config)
+        run_train_config,
+        retry_times=retry_times)
     
     rid_op = ReinforcedDynamics(
         "reinforced-dynamics",
@@ -104,7 +109,6 @@ def prep_rid_op(
     )
     return rid_op
 
-
 def submit_rid(
         confs: Union[str, List[str]],
         topology: Optional[str],
@@ -113,6 +117,7 @@ def submit_rid(
         models: Optional[Union[str, List[str]]] = None,
         forcefield: Optional[str] = None,
         index_file: Optional[str] = None,
+        data_file: Optional[str] = None,
         dp_files: Optional[List[str]] = [],
         otherfiles: Optional[List[str]] = None
     ):
@@ -133,7 +138,8 @@ def submit_rid(
         run_select_config = normalized_resources[tasks["run_select_config"]],
         prep_data_config = normalized_resources[tasks["prep_data_config"]],
         run_train_config = normalized_resources[tasks["run_train_config"]],
-        workflow_steps_config = normalized_resources[tasks["workflow_steps_config"]]
+        workflow_steps_config = normalized_resources[tasks["workflow_steps_config"]],
+        retry_times=1
     )
 
     if isinstance(confs, str):
@@ -226,6 +232,11 @@ def submit_rid(
         top_artifact = None
     else:
         top_artifact = upload_artifact(Path(topology), archive=None)
+        
+    if data_file is None:
+        data_artifact = None
+    else:
+        data_artifact = upload_artifact(Path(data_file), archive=None)
     rid_config = upload_artifact(Path(rid_config), archive=None)
 
     rid_steps = Step("rid-procedure",
@@ -238,11 +249,12 @@ def submit_rid(
                 "forcefield": forcefield_artifact,
                 "index_file": index_file_artifact,
                 "inputfile": inputfile_artifact,
+                "data_file": data_artifact,
                 "dp_files": dp_files_artifact,
                 "cv_file": cv_file_artifact
             },
             parameters={}
         )
-    wf = Workflow("reinforced-dynamics", pod_gc_strategy="OnPodSuccess", parallelism=30)
+    wf = Workflow("reinforced-dynamics", pod_gc_strategy="OnPodSuccess", parallelism=50)
     wf.add(rid_steps)
     wf.submit()
diff --git a/rid/flow/loop.py b/rid/flow/loop.py
index ee91c5ec..52309abf 100644
--- a/rid/flow/loop.py
+++ b/rid/flow/loop.py
@@ -49,7 +49,6 @@ def __init__(
             "output_freq": InputParameter(type=float, value=2500),
             "slice_mode": InputParameter(type=str, value="gmx"),
             "label_config": InputParameter(type=Dict),
-            "kappas": InputParameter(type=List[float]),
             "tail": InputParameter(type=float, value=0.9),
             "train_config": InputParameter(type=Dict),
             "adjust_amplifier": InputParameter(type=float, value=1.5),
@@ -173,7 +172,6 @@ def _loop (
             "output_freq": steps.inputs.parameters["output_freq"],
             "slice_mode": steps.inputs.parameters["slice_mode"],
             "label_config": steps.inputs.parameters["label_config"],
-            "kappas": steps.inputs.parameters["kappas"],
             "train_config": steps.inputs.parameters["train_config"]
         },
         artifacts={
@@ -215,7 +213,6 @@ def _loop (
             "output_freq": steps.inputs.parameters["output_freq"],
             "slice_mode": steps.inputs.parameters["slice_mode"],
             "label_config": steps.inputs.parameters["label_config"],
-            "kappas": steps.inputs.parameters["kappas"],
             "train_config": steps.inputs.parameters["train_config"]
         },
         artifacts={
@@ -280,6 +277,7 @@ def __init__(
             "confs": InputArtifact(),
             "rid_config": InputArtifact(),
             "index_file": InputArtifact(optional=True),
+            "data_file": InputArtifact(optional=True),
             "dp_files": InputArtifact(optional=True),
             "cv_file": InputArtifact(optional=True)
         }
@@ -414,7 +412,6 @@ def _rid(
             "output_freq": prep_rid.outputs.parameters["output_freq"],
             "slice_mode": prep_rid.outputs.parameters["slice_mode"],
             "label_config": prep_rid.outputs.parameters["label_config"],
-            "kappas": prep_rid.outputs.parameters["kappas"],
             "train_config": prep_rid.outputs.parameters["train_config"]
         },
         artifacts={
@@ -424,6 +421,7 @@ def _rid(
             "inputfile": steps.inputs.artifacts["inputfile"],
             "confs": prep_rid.outputs.artifacts["confs"],
             "index_file": steps.inputs.artifacts["index_file"],
+            "data_old": steps.inputs.artifacts["data_file"],
             "dp_files": steps.inputs.artifacts["dp_files"],
             "cv_file": steps.inputs.artifacts["cv_file"]
         },
@@ -460,7 +458,6 @@ def _rid(
             "output_freq": prep_rid.outputs.parameters["output_freq"],
             "slice_mode": prep_rid.outputs.parameters["slice_mode"],
             "label_config": prep_rid.outputs.parameters["label_config"],
-            "kappas": prep_rid.outputs.parameters["kappas"],
             "train_config": prep_rid.outputs.parameters["train_config"]
         },
         artifacts={
diff --git a/rid/nn/data.py b/rid/nn/data.py
deleted file mode 100644
index 66987fd5..00000000
--- a/rid/nn/data.py
+++ /dev/null
@@ -1,100 +0,0 @@
-def collect_data(iter_index, base_dir):
-    iter_name = make_iter_name(iter_index)
-    train_path = base_dir + iter_name + "/" + train_name + "/"
-    data_path = train_path + "data/"
-    data_file = train_path + "data/data.raw"
-    data_old_file = train_path + "data/data.old.raw"
-    data_new_file = train_path + "data/data.new.raw"
-    cwd = os.getcwd() + "/"
-    # collect data
-    log_task("collect data upto %d" % (iter_index))
-    if iter_index == 0:
-        ii = 0
-        this_raw = base_dir + make_iter_name(ii) + "/" + res_name + "/data.raw"
-        os.chdir(data_path)
-        os.symlink(os.path.relpath(this_raw), os.path.basename(data_new_file))
-        os.symlink(os.path.basename(data_new_file),
-                   os.path.basename(data_file))
-        os.chdir(cwd)
-        open(data_old_file, "w").close()
-    else:
-        prev_iter_index = iter_index - 1
-        prev_data_file = base_dir + \
-            make_iter_name(prev_iter_index) + "/" + \
-            train_name + "/data/data.raw"
-        this_raw = base_dir + \
-            make_iter_name(iter_index) + "/" + res_name + "/data.raw"
-        os.chdir(data_path)
-        os.symlink(os.path.relpath(prev_data_file),
-                   os.path.basename(data_old_file))
-        os.symlink(os.path.relpath(this_raw), os.path.basename(data_new_file))
-        os.chdir(cwd)
-        with open(data_file, "wb") as fo:
-            with open(data_old_file, "rb") as f0, open(data_new_file, "rb") as f1:
-                shutil.copyfileobj(f0, fo)
-                shutil.copyfileobj(f1, fo)
-
-
-def make_train(iter_index,
-               json_file,
-               base_dir="./"):
-    json_file = os.path.abspath(json_file)
-    fp = open(json_file, 'r')
-    jdata = json.load(fp)
-    fp.close()
-    # template_dir = jdata["template_dir"]
-    numb_model = jdata["numb_model"]
-    res_iter = jdata["res_iter"]
-
-    # abs path
-    base_dir = os.path.abspath(base_dir) + "/"
-    iter_name = make_iter_name(iter_index)
-    train_path = base_dir + iter_name + "/" + train_name + "/"
-    data_path = train_path + "data/"
-    cwd = os.getcwd() + "/"
-    create_path(train_path)
-    os.makedirs(data_path)
-    collect_data(iter_index, base_dir)
-
-    # create train dirs
-    log_task("create train dirs")
-    for ii in range(numb_model):
-        work_path = train_path + ("%03d/" % ii)
-        old_model_path = work_path + "old_model/"
-        create_path(work_path)
-        os.chdir(work_path)
-        os.symlink("../data", "./data")
-        os.chdir(cwd)
-        if iter_index >= 1:
-            prev_iter_index = iter_index - 1
-            prev_iter_name = make_iter_name(prev_iter_index)
-            prev_train_path = base_dir + prev_iter_name + "/" + train_name + "/"
-            prev_work_path = prev_train_path + ("%03d/" % ii)
-            prev_model_files = glob.glob(prev_work_path + "model.ckpt.*")
-            prev_model_files = prev_model_files + \
-                [prev_work_path + "checkpoint"]
-            create_path(old_model_path)
-            os.chdir(old_model_path)
-            # why to copy twice.
-            for ii in prev_model_files:
-                os.symlink(os.path.relpath(ii), os.path.basename(ii))
-            os.chdir(cwd)
-            for jj in prev_model_files:
-                shutil.copy(jj, work_path)
-    print("Training files have prepared.")
-
-
-def check_new_data(iter_index, train_path, base_path):
-    # check if new data is empty
-    new_data_file = os.path.join(train_path, 'data/data.new.raw')
-    if os.stat(new_data_file).st_size == 0:
-        prev_iter_index = iter_index - 1
-        prev_train_path = base_path + \
-            make_iter_name(prev_iter_index) + "/" + train_name + "/"
-        prev_models = glob.glob(prev_train_path + "*.pb")
-        for ii in prev_models:
-            model_name = os.path.basename(ii)
-            os.symlink(ii, os.path.join(train_path, model_name))
-        return True
-    else:
-        return False
\ No newline at end of file
diff --git a/rid/nn/model.py b/rid/nn/model.py
index 6a82b840..65a994d6 100644
--- a/rid/nn/model.py
+++ b/rid/nn/model.py
@@ -15,15 +15,6 @@
 tf_precision = tf.float32
 
 
-logging.basicConfig(
-    format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
-    datefmt="%Y-%m-%d %H:%M:%S",
-    level=os.environ.get("LOGLEVEL", "INFO").upper(),
-    stream=sys.stdout,
-)
-logger = logging.getLogger(__name__)
-
-
 class Reader(object):
     def __init__(self, config):
         # copy from config
@@ -132,6 +123,7 @@ def get_data(self):
 
 class Model(object):
     def __init__(self, config, sess):
+        self.log_name = config.log_name
         self.sess = sess
         # copy from config
         self.data_path = config.data_path
@@ -183,8 +175,14 @@ def test_error_mix(self, reader):
         return error_new, error_new2, error_old, error_old2
 
     def train(self, reader):
+        logger = logging.getLogger(__name__)
+        logger.setLevel(level=os.environ.get("LOGLEVEL", "INFO").upper())
+        handler = logging.FileHandler(self.log_name)
+        formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(name)s | %(message)s")
+        handler.setFormatter(fmt=formatter)
+        logger.addHandler(handler)
+        
         reader.prepare()
-
         self.n_input = reader.n_input
         self.inputs_train = tf.placeholder(
             tf_precision, [None, self.n_input + self.cv_dim], name='inputs')
@@ -395,8 +393,9 @@ def build_force(self,
         energy_ = self._final_layer(
             layer, 1, activation_fn=None, name='energy', reuse=reuse, init=init)
         energy = tf.identity(energy_, name='o_energy')
-        forces = - tf.reshape(tf.stack(tf.gradients(energy, cvs)),
-                              [-1, self.cv_dim], name='o_forces')
+        energy_grad = tf.reshape(tf.stack(tf.gradients(energy, cvs)),
+                              [-1, self.cv_dim], name='energy_grad')
+        forces = tf.identity(-energy_grad, name='o_forces')
         force_dif = forces_hat - forces
         forces_norm = tf.reshape(tf.reduce_sum(
             forces * forces, axis=1), [-1, 1])
diff --git a/rid/nn/train_net.py b/rid/nn/train_net.py
index fae15480..6826435b 100644
--- a/rid/nn/train_net.py
+++ b/rid/nn/train_net.py
@@ -10,15 +10,6 @@
 from rid.nn.model import Reader, Model
 
 
-logging.basicConfig(
-    format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
-    datefmt="%Y-%m-%d %H:%M:%S",
-    level=os.environ.get("LOGLEVEL", "INFO").upper(),
-    stream=sys.stdout,
-)
-logger = logging.getLogger(__name__)
-
-
 class Config(object):
     def __init__(self, cv_dim):
         self.batch_size = 64
@@ -51,6 +42,13 @@ def reset_batch_size(config):
 
 
 def print_conf(config, nthreads):
+    logger = logging.getLogger(__name__)
+    logger.setLevel(level=os.environ.get("LOGLEVEL", "INFO").upper())
+    handler = logging.FileHandler(config.log_name)
+    formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(name)s | %(message)s")
+    handler.setFormatter(fmt=formatter)
+    logger.addHandler(handler)
+    
     logger.info("# restart           " + str(config.restart))
     logger.info("# num_threads       %d" % nthreads)
     logger.info("# neurons           " + str(config.n_neuron))
@@ -82,7 +80,8 @@ def set_conf(cv_dim,
              old_ratio=7.0,
              decay_steps_inner=0,
              drop_out_rate=0.5,
-             data_path="./"):
+             data_path="./",
+             log_name = "log"):
     config = Config(cv_dim)
     config.n_neuron = neurons
     config.batch_size = batch_size
@@ -98,6 +97,7 @@ def set_conf(cv_dim,
     config.drop_out_rate = drop_out_rate
     config.angular_mask = angular_mask
     config.data_path = data_path
+    config.log_name = log_name
     return config
 
 
@@ -118,7 +118,8 @@ def train(
         decay_steps_inner=0,
         init_model=None,
         drop_out_rate=0.5,
-        data_path="./"
+        data_path="./",
+        log_name = "log"
     ):
     config = set_conf(cv_dim,
                       angular_mask=angular_mask,
@@ -135,7 +136,8 @@ def train(
                       old_ratio=old_ratio,
                       decay_steps_inner=decay_steps_inner,
                       drop_out_rate = drop_out_rate,
-                      data_path = data_path)
+                      data_path = data_path,
+                      log_name = log_name)
     if init_model is not None:
         if config.restart:
             raise RuntimeError(
diff --git a/rid/op/calc_mf.py b/rid/op/calc_mf.py
deleted file mode 100644
index 28595d29..00000000
--- a/rid/op/calc_mf.py
+++ /dev/null
@@ -1,114 +0,0 @@
-from dflow.python import (
-    OP,
-    OPIO,
-    OPIOSign,
-    Artifact,
-    Parameter
-)
-
-from typing import List, Optional, Union
-from pathlib import Path
-import numpy as np
-from rid.constants import (
-        force_out
-    )
-from rid.utils import load_txt
-
-
-class CalcMF(OP):
-
-    """
-    Calculate mean force with the results of restrained MD. 
-    
-    .. math::
-        MeanForce = ( Average(CVs) - Initial(CVs) ) * kappa
-        
-    CalcMF will handle periodic CVs by `angular_mask`. 
-    To get the mean value of CVs near equilibrium state under restrained MD, only part of outputs of CV values 
-    (the last `tail` part, for example, the last 90% CV values) are used. 
-    
-    """
-
-    @classmethod
-    def get_input_sign(cls):
-        return OPIOSign(
-            {
-                "task_name": str,
-                "plm_out": Artifact(Path),
-                "kappas": List[float],
-                "at": Artifact(Path),
-                "tail": Parameter(float, default=0.9),
-                "angular_mask": Optional[Union[np.ndarray, List]],
-            }
-        )
-
-    @classmethod
-    def get_output_sign(cls):
-        return OPIOSign(
-            {
-                "forces": Artifact(Path)
-            }
-        )
-
-    @OP.exec_sign_check
-    def execute(
-        self,
-        op_in: OPIO,
-    ) -> OPIO:
-        r"""Execute the OP.
-
-        Parameters
-        ----------
-        op_in : dict
-            Input dict with components:
-
-                - `task_name`: (`str`) Task name, used to make sub-directory for the task.
-                - `plm_out`: (`Artifact(Path)`) Outputs of CV values from restrained MD simulations. 
-                    These outputs are generated by PLUMED2.
-                - `kappas`: (`List[float]`) Force constants of harmonic restraints.
-                - `at`: (`Artifact(Path)`) Files containing initial CV values, or CV centers.
-                - `tail`: (`float`) Only the last `tail` of CV values will be used to estimate mean force.
-                - `angular_mask`: (`int`) Mask list for periodic CVs. 1 represents periodic, 0 represents non-periodic.
-                    length of angular_mask equals to the dimension of CVs.
-
-        Returns
-        -------
-            Output dict with components:
-
-                - `forces`: (`Artifact(Path)`) Files containing mean forces.
-
-        """
-        data = load_txt(op_in["plm_out"])
-        data = data[:, 1:]  # removr the first column(time index).
-        centers = load_txt(op_in["at"])
-
-        nframes = data.shape[0]
-        
-        angular_boolean = (np.array(op_in["angular_mask"], dtype=int) == 1)
-        init_angle = data[0][angular_boolean]
-        for ii in range(1, nframes):
-            current_angle = data[ii][angular_boolean]
-            angular_diff = current_angle - init_angle
-            current_angle[angular_diff < -np.pi] += 2 * np.pi
-            current_angle[angular_diff >= np.pi] -= 2 * np.pi
-            data[ii][angular_boolean] = current_angle
-
-        start_f = int(nframes * (1-op_in["tail"]))
-        avgins = np.average(data[start_f:, :], axis=0)
-
-        diff = avgins - centers
-        angular_diff = diff[angular_boolean]
-        angular_diff[angular_diff < -np.pi] += 2 * np.pi
-        angular_diff[angular_diff >  np.pi] -= 2 * np.pi
-        diff[angular_boolean] = angular_diff
-        ff = np.multiply(op_in["kappas"], diff)
-        
-        task_path = Path(op_in["task_name"])
-        task_path.mkdir(exist_ok=True, parents=True)
-        np.savetxt(task_path.joinpath(force_out),  np.reshape(ff, [1, -1]), fmt='%.10e')
-        op_out = OPIO(
-            {
-                "forces": task_path.joinpath(force_out)
-            }
-        )
-        return op_out
diff --git a/rid/op/prep_data.py b/rid/op/prep_data.py
index 71343115..80c7d7f0 100644
--- a/rid/op/prep_data.py
+++ b/rid/op/prep_data.py
@@ -24,8 +24,7 @@ class CollectData(OP):
     def get_input_sign(cls):
         return OPIOSign(
             {
-                "forces": Artifact(List[Path]),
-                "centers": Artifact(List[Path])
+                "cv_forces": Artifact(List[Path])
             }
         )
 
@@ -42,17 +41,13 @@ def execute(
         self,
         op_in: OPIO,
         ) -> OPIO:
-        forces = []
-        centers = []
-        for idx in range(len(op_in["forces"])):
-            force = load_txt(op_in["forces"][idx])
-            center = load_txt(op_in["centers"][idx])
-            forces = np.append(forces, force)
-            centers = np.append(centers, center)
-        if op_in["forces"]:
-            forces = np.reshape(forces, [len(op_in["forces"]), -1])
-            centers = np.reshape(centers, [len(op_in["forces"]), -1])
-            data = np.concatenate((centers, forces), axis=1)
+        cv_forces = []
+        for idx in range(len(op_in["cv_forces"])):
+            cv_force = load_txt(op_in["cv_forces"][idx])
+            cv_forces = np.append(cv_forces, cv_force)
+        if op_in["cv_forces"]:
+            cv_forces = np.reshape(cv_forces, [len(op_in["cv_forces"]), -1])
+            data = cv_forces
         else:
             data = np.array([])
         np.save(data_new, data)
diff --git a/rid/op/prep_exploration.py b/rid/op/prep_exploration.py
index f6d5daa7..a749614c 100644
--- a/rid/op/prep_exploration.py
+++ b/rid/op/prep_exploration.py
@@ -16,10 +16,10 @@
 
 class PrepExplore(OP):
 
-    r"""Prepare files for exploration tasks.
-    Currently, RiD is based on Gromacs with PLUMED2 plugin. Input files must contain .gro for conformations,
-    .top for topology (with pre-defined force fields) and corresponding configuration in Dict formats while `models` 
-    (graph files, .pb) are optional. Exploration step would run biased MD sampling with neural network models or 
+    r"""
+    Prepare files for exploration tasks.
+    Currently, RiD is based on Gromacs/Lammps with PLUMED2 plugin. Provide .gro files and .top files if running Gromacs and .lmp files if running Lammps.
+    Exploration step would run biased MD sampling with neural network models or 
     brute force MD sampling without neural network model provided.
 
     With models provided, the bias forces will be the average value of outputs of these models and tuned by a switching function.
@@ -53,7 +53,8 @@ def get_input_sign(cls):
                 "trust_lvl_2": float,
                 "exploration_config": Dict,
                 "cv_config": Dict,
-                "task_name": str
+                "task_name": str,
+                "block_tag": str
             }
         )
 
@@ -84,9 +85,8 @@ def execute(
             - `trust_lvl_1`: (`float`) Trust level 1.
             - `trust_lvl_2`: (`float`) Trust level 2.
             - `topology`: (`Artifact(Path)`) Topology files (.top) for Gromacs simulations.
-            - `conf`: (`Artifact(Path)`) Conformation files (.gro) for Gromacs simulations.
-            - `exploration_config`: (`Dict`) Configuration in `Dict` format for Gromacs run. Must contains:
-                `dt`, `steps`, `temperature`, `output_freq`.
+            - `conf`: (`Artifact(Path)`) Conformation files (.gro, .lmp) for Gromacs/Lammps simulations.
+            - `exploration_config`: (`Dict`) Configuration in `Dict` format for Gromacs/Lammps run.
             - `cv_config`: (`Dict`) Configuration for CV creation.
             - `task_name`: (`str`) Task name used to make sub-dir for tasks.
            
@@ -99,30 +99,38 @@ def execute(
         """
         cv_file = []
         selected_resid = None
+        selected_atomid = None
         if op_in["cv_config"]["mode"] == "torsion":
             selected_resid = op_in["cv_config"]["selected_resid"]
+        elif op_in["cv_config"]["mode"] == "distance":
+            selected_atomid = op_in["cv_config"]["selected_atomid"]
         elif op_in["cv_config"]["mode"] == "custom":
-            #print("custom!!!")
             cv_file = op_in["cv_file"]
         if op_in["models"] is None:
             models = []
         else:
             models = [str(model.name) for model in op_in["models"]]
 
-        #print("what is cv", cv_file)
+        wall_list = None
+        if "iterative_walls" in op_in["cv_config"]:
+            wall_list = op_in["cv_config"]["iterative_walls"]
         
+        iteration = int(op_in["block_tag"].split("-")[1])
         gmx_task_builder = EnhcMDTaskBuilder(
             conf = op_in["conf"],
             topology = op_in["topology"],
             exploration_config = op_in["exploration_config"],
             cv_file=cv_file,
             selected_resid = selected_resid,
+            selected_atomid = selected_atomid,
             sampler_type = op_in["exploration_config"]["type"],
             trust_lvl_1 = op_in["trust_lvl_1"],
             trust_lvl_2 = op_in["trust_lvl_2"],
             model_list = models,
             plumed_output = plumed_output_name,
-            cv_mode = op_in["cv_config"]["mode"]
+            cv_mode = op_in["cv_config"]["mode"],
+            wall_list = wall_list,
+            iteration = iteration
         )
         cv_dim = gmx_task_builder.get_cv_dim()
         task_path = Path(op_in["task_name"])
diff --git a/rid/op/prep_label.py b/rid/op/prep_label.py
index 7cd5362a..33f52f41 100644
--- a/rid/op/prep_label.py
+++ b/rid/op/prep_label.py
@@ -12,7 +12,7 @@
 from rid.constants import (
         plumed_output_name
     )
-from rid.task.builder import RestrainedMDTaskBuilder
+from rid.task.builder import RestrainedMDTaskBuilder, ConstrainedMDTaskBuilder
 from rid.utils import load_txt
 
 
@@ -94,7 +94,7 @@ class PrepLabel(OP):
     r"""Prepare files for Label steps.
     
     Labels of RiD are mean forces, which are calculated by restrained MD algorithm.
-    Restrained MD simulations are performed by Gromacs with PLUMED2 plugin, so input files are in Gromacs format.
+    Restrained MD simulations are performed by Gromacs/Lammps with PLUMED2 plugin, so input files are in Gromacs/Lammps format.
     """
 
     @classmethod
@@ -107,8 +107,7 @@ def get_input_sign(cls):
                 "label_config": Dict,
                 "cv_config": Dict,
                 "task_name": str,
-                "kappas": List[float],
-                "at": Artifact(Path)
+                "at": Artifact(Path, optional=True)
             }
         )
 
@@ -134,11 +133,9 @@ def execute(
             Input dict with components:
         
             - `topology`: (`Artifact(Path)`) Topology files (.top) for Restrained MD simulations.
-            - `conf`: (`Artifact(Path)`) Conformation files (.gro) for Restrained MD simulations.
-            - `label_config`: (`Dict`) Configuration in `Dict` format for Gromacs run. Must contains:
-                `dt`, `steps`, `temperature`, `output_freq`.
+            - `conf`: (`Artifact(Path)`) Conformation files (.gro, .lmp) for Restrained MD simulations.
+            - `label_config`: (`Dict`) Configuration in `Dict` format for Gromacs/Lammps run.
             - `cv_config`: (`Dict`) Configuration for CV creation.
-            - `kappas`: (`List[float]`) Force constants of harmonic restraints.
             - `at`: (`Artifact(Path)`) Files containing initial CV values, or CV centers.
             - `task_name`: (`str`) Task name used to make sub-dir for tasks.
            
@@ -151,27 +148,47 @@ def execute(
 
         cv_file = []
         selected_resid = None
+        selected_atomid = None
         if op_in["cv_config"]["mode"] == "torsion":
             selected_resid = op_in["cv_config"]["selected_resid"]
+        elif op_in["cv_config"]["mode"] == "distance":
+            selected_atomid = op_in["cv_config"]["selected_atomid"]
         elif op_in["cv_config"]["mode"] == "custom":
-            print("custom!!!")
+            if "selected_resid" in op_in["cv_config"]:
+                selected_resid = op_in["cv_config"]["selected_resid"]
+            elif "selected_atomid" in op_in["cv_config"]:
+                selected_atomid = op_in["cv_config"]["selected_atomid"]
             cv_file = op_in["cv_file"]
-        at = load_txt(op_in["at"])
         
         #print("what is cv", cv_file)
-        
-        gmx_task_builder = RestrainedMDTaskBuilder(
-            conf = op_in["conf"],
-            topology = op_in["topology"],
-            label_config = op_in["label_config"],
-            cv_file = cv_file,
-            selected_resid = selected_resid,
-            sampler_type = op_in["label_config"]["type"],
-            kappa = op_in["kappas"],
-            at = at,
-            plumed_output = plumed_output_name,
-            cv_mode = op_in["cv_config"]["mode"]
-        )
+        if op_in["label_config"]["method"] == "restrained":
+            at = 0.0
+            if op_in["at"] is not None:
+                at = load_txt(op_in["at"])
+            gmx_task_builder = RestrainedMDTaskBuilder(
+                conf = op_in["conf"],
+                topology = op_in["topology"],
+                label_config = op_in["label_config"],
+                cv_file = cv_file,
+                selected_resid = selected_resid,
+                selected_atomid = selected_atomid,
+                sampler_type = op_in["label_config"]["type"],
+                kappa = op_in["label_config"]["kappas"],
+                at = at,
+                plumed_output = plumed_output_name,
+                cv_mode = op_in["cv_config"]["mode"]
+            )
+        elif op_in["label_config"]["method"] == "constrained":
+            gmx_task_builder = ConstrainedMDTaskBuilder(
+                conf = op_in["conf"],
+                topology = op_in["topology"],
+                label_config = op_in["label_config"],
+                cv_file = cv_file,
+                selected_atomid = selected_atomid,
+                sampler_type = op_in["label_config"]["type"],
+                plumed_output = plumed_output_name,
+                cv_mode = op_in["cv_config"]["mode"]
+            )
         gmx_task = gmx_task_builder.build()
         task_path = Path(op_in["task_name"])
         task_path.mkdir(exist_ok=True, parents=True)
diff --git a/rid/op/prep_rid.py b/rid/op/prep_rid.py
index bdafbc2d..320d7b2a 100644
--- a/rid/op/prep_rid.py
+++ b/rid/op/prep_rid.py
@@ -84,7 +84,6 @@ def get_output_sign(cls):
                 "output_freq": float,
                 "slice_mode": str,
                 "label_config": Dict,
-                "kappas": List,
                 "train_config": Dict
             }
         )
@@ -156,13 +155,12 @@ def execute(
         dt = exploration_config["dt"]
         output_freq = exploration_config["output_freq"]
         cv_config = jdata.pop("CV")
-        angular_mask = cv_config.pop("angular_mask")
-        weights = cv_config.pop("weights")
+        angular_mask = cv_config["angular_mask"]
+        weights = cv_config["weights"]
         
         selection_config = jdata.pop("SelectorConfig")
 
         label_config = jdata.pop("LabelMDConfig")
-        kappas = label_config.pop("kappas")
         
         trust_lvl_1 = jdata.pop("trust_lvl_1")
         trust_lvl_2 = jdata.pop("trust_lvl_2")
@@ -196,7 +194,6 @@ def execute(
                 "output_freq": output_freq,
                 "slice_mode": selection_config.pop("slice_mode"),
                 "label_config": label_config,
-                "kappas": kappas,
                 "train_config": train_config
             }
         )
diff --git a/rid/op/prep_select.py b/rid/op/prep_select.py
index 00e2730f..88c058bd 100644
--- a/rid/op/prep_select.py
+++ b/rid/op/prep_select.py
@@ -12,7 +12,8 @@
 from rid.utils import save_txt, set_directory
 from rid.constants import (
     cluster_selection_data_name, 
-    cluster_selection_index_name
+    cluster_selection_index_name,
+    cluster_fig
 )
 import numpy as np
 
@@ -52,6 +53,7 @@ def get_output_sign(cls):
             {
                 "numb_cluster": int,
                 "cluster_threshold": float,
+                "cluster_fig": Artifact(Path),
                 "cluster_selection_index": Artifact(Path),
                 "cluster_selection_data": Artifact(Path)
             }
@@ -91,11 +93,12 @@ def execute(
             - `cluster_selection_index`: (`Artifact(Path)`) Indice of chosen representive frames of clusters in trajectories.
             - `cluster_selection_data`: (`Artifact(Path)`) Collective variable values of chosen representive frames of clusters.
         """
-
-        # the first column of plm_out is time index
-        data = np.loadtxt(op_in["plm_out"])[:,1:]
+        task_path = Path(op_in["task_name"])
+        task_path.mkdir(exist_ok=True, parents=True)
+        # the first column of plm_out is time index, the second columnn is biased potential
+        data = np.loadtxt(op_in["plm_out"])[:,2:]
         cv_cluster = Cluster(
-            data, op_in["cluster_threshold"], angular_mask=op_in["angular_mask"], 
+            data, op_in["cluster_threshold"], op_in["task_name"], angular_mask=op_in["angular_mask"], 
             weights=op_in["weights"], max_selection=op_in["max_selection"])
         if op_in["if_make_threshold"]:
             assert (op_in["numb_cluster_lower"] is not None) and (op_in["numb_cluster_upper"] is not None), \
@@ -107,8 +110,6 @@ def execute(
         selected_data = data[cls_sel_idx]
         numb_cluster = len(cls_sel_idx)
 
-        task_path = Path(op_in["task_name"])
-        task_path.mkdir(exist_ok=True, parents=True)
         with set_directory(task_path):
             np.save(cluster_selection_index_name, cls_sel_idx)
             np.save(cluster_selection_data_name, selected_data)
@@ -116,6 +117,7 @@ def execute(
         op_out = OPIO({
                 "cluster_threshold": threshold,
                 "numb_cluster": numb_cluster,
+                "cluster_fig": task_path.joinpath(cluster_fig),
                 "cluster_selection_index": task_path.joinpath(cluster_selection_index_name),
                 "cluster_selection_data": task_path.joinpath(cluster_selection_data_name)
             })
diff --git a/rid/op/run_exploration.py b/rid/op/run_exploration.py
index 2d63321a..65df3a07 100644
--- a/rid/op/run_exploration.py
+++ b/rid/op/run_exploration.py
@@ -2,6 +2,8 @@
 import logging
 from typing import List, Dict, Union
 from pathlib import Path
+import numpy as np
+from matplotlib import pyplot as plt
 from dflow.python import (
     OP,
     OPIO,
@@ -20,11 +22,14 @@
         gmx_xtc_name,
         gmx_conf_out,
         lmp_conf_out,
+        bias_fig,
+        model_devi_fig,
         lmp_input_name
     )
 from rid.utils import run_command, set_directory, list_to_string
-from rid.common.mol import final_dump
+from rid.common.lammps.command import final_dump
 from rid.common.sampler.command import get_grompp_cmd, get_mdrun_cmd
+from rid.select.model_devi import make_std
 
 
 logging.basicConfig(
@@ -39,8 +44,8 @@
 class RunExplore(OP):
 
     """Run (biased or brute-force) MD simulations with files provided by `PrepExplore` OP.
-    RiD-kit emploies Gromacs as MD engine with PLUMED2 plugin (with or without MPI-implement). Make sure work environment 
-    has properly installed Gromacs with patched PLUMED2. Also, PLUMED2 need an additional `DeePFE.cpp` patch to use bias potential of RiD.
+    RiD-kit emploies Gromacs/Lammps as MD engine with PLUMED2 plugin (with or without MPI-implement). Make sure work environment 
+    has properly installed Gromacs/Lammps with patched PLUMED2. Also, PLUMED2 need an additional `DeePFE.cpp` patch to use bias potential of RiD.
     See `install` in the home page for details.
     """
 
@@ -64,6 +69,8 @@ def get_output_sign(cls):
         return OPIOSign(
             {
                 "plm_out": Artifact(Path),
+                "bias_fig": Artifact(Path, optional=True),
+                "model_devi_fig": Artifact(Path,optional=True),
                 "md_log": Artifact(Path),
                 "trajectory": Artifact(Path),
                 "conf_out": Artifact(Path)
@@ -83,8 +90,8 @@ def execute(
         op_in : dict
             Input dict with components:
 
-            - `task_path`: (`Artifact(Path)`) A directory path containing files for Gromacs MD simulations.
-            - `exploration_config`: (`Dict`) Configuration of Gromacs simulations in exploration steps.
+            - `task_path`: (`Artifact(Path)`) A directory path containing files for Gromacs/Lammps MD simulations.
+            - `exploration_config`: (`Dict`) Configuration of Gromacs/Lammps simulations in exploration steps.
             - `models`: (`Artifact(List[Path])`) Optional. Neural network model files (`.pb`) used to bias the simulation. 
                 Run brute force MD simulations if not provided.
           
@@ -93,7 +100,7 @@ def execute(
             Output dict with components:
         
             - `plm_out`: (`Artifact(Path)`) Outputs of CV values (`plumed.out` by default) from exploration steps.
-            - `md_log`: (`Artifact(Path)`) Log files of Gromacs `mdrun` commands.
+            - `md_log`: (`Artifact(Path)`) Log files of Gromacs/lammps `mdrun` commands.
             - `trajectory`: (`Artifact(Path)`) Trajectory files (`.xtc`). The output frequency is defined in `exploration_config`.
             - `conf_out`: (`Artifact(Path)`) Final frames of conformations in simulations.
         """
@@ -142,26 +149,35 @@ def execute(
 
         with set_directory(op_in["task_path"]):
             if op_in["forcefield"] is not None:
-                os.symlink(op_in["forcefield"], op_in["forcefield"].name)
+                if not os.path.islink(op_in["forcefield"].name):
+                    os.symlink(op_in["forcefield"], op_in["forcefield"].name)
             if op_in["models"] is not None:
                 for model in op_in["models"]:
-                    os.symlink(model, model.name)
+                    if not os.path.islink(model.name):
+                        os.symlink(model, model.name)
             if op_in["dp_files"] is not None:
                 for file in op_in["dp_files"]:
-                    os.symlink(file, file.name)
+                    if not os.path.islink(file.name):
+                        os.symlink(file, file.name)
             if op_in["index_file"] is not None:
-                os.symlink(op_in["index_file"], op_in["index_file"].name)
+                if not os.path.islink(op_in["index_file"].name):
+                    os.symlink(op_in["index_file"], op_in["index_file"].name)
             if op_in["cv_file"] is not None:
                 for file in op_in["cv_file"]:
                     if file.name != "colvar":
-                        os.symlink(file, file.name)
+                        if not os.path.islink(file.name):
+                            os.symlink(file, file.name)
             if op_in["inputfile"] is not None:
                 for file in op_in["inputfile"]:
                     if file.name == inputfile_name:
-                        os.symlink(file, file.name)
+                        if not os.path.islink(file.name):
+                            os.symlink(file, file.name)
             
             if op_in["dp_files"] is not None:
-                os.environ["GMX_DEEPMD_INPUT_JSON"] = "./input.json"
+                for dp_file in op_in["dp_files"]:
+                    if (dp_file.name).endswith("json"):
+                        os.environ["GMX_DEEPMD_INPUT_JSON"] = dp_file.name
+                        
             if grompp_cmd is not None:
                 logger.info(list_to_string(grompp_cmd, " "))
                 return_code, out, err = run_command(grompp_cmd)
@@ -174,7 +190,34 @@ def execute(
                 logger.info(err)
             if op_in["exploration_config"]["type"] == "lmp":
                 final_dump(dump="out.dump",selected_idx=-1,output_format=lmp_conf_out)
-        
+            
+            bias_fig_file = None
+            model_devi_fig_file = None
+            if op_in["models"] is not None:
+                # plot bias during simulation
+                bias = np.loadtxt(plumed_output_name)[:,1]
+                dt = op_in["exploration_config"]["dt"]*op_in["exploration_config"]["output_freq"]
+                xlist = [i*dt for i in range(len(bias))]
+                plt.figure(figsize=(10, 8), dpi=100)
+                plt.scatter(xlist,np.log(bias+0.001))
+                plt.xlabel("simulation time (ps)")
+                plt.ylabel("log of bias (kj/(mol))")
+                plt.title("log bias for the simulation")
+                plt.savefig(op_in["task_path"].joinpath(bias_fig))
+                # plot model deviation during simulation
+                cv_list = np.loadtxt(plumed_output_name)[:,2:]
+                stds = make_std(cv_list, op_in["models"])
+                plt.figure(figsize=(10, 8), dpi=100)
+                plt.scatter(xlist,stds)
+                plt.xlabel("simulation time (ps)")
+                plt.ylabel("force std (KJ/(mol*nm))")
+                plt.title("model deviation during simulation")
+                plt.savefig(op_in["task_path"].joinpath(model_devi_fig))
+                
+                bias_fig_file = op_in["task_path"].joinpath(bias_fig)
+                model_devi_fig_file = op_in["task_path"].joinpath(model_devi_fig)
+                
+                        
         if op_in["exploration_config"]["type"] == "gmx":
             mdrun_log = gmx_mdrun_log
             traj_name = gmx_xtc_name
@@ -189,6 +232,8 @@ def execute(
         op_out = OPIO(
             {
                 "plm_out": op_in["task_path"].joinpath(plumed_output_name),
+                "bias_fig": bias_fig_file,
+                "model_devi_fig": model_devi_fig_file,
                 "md_log": op_in["task_path"].joinpath(mdrun_log),
                 "trajectory": op_in["task_path"].joinpath(traj_name),
                 "conf_out": op_in["task_path"].joinpath(conf_out),
diff --git a/rid/op/run_label.py b/rid/op/run_label.py
index 19060c0a..a1edc951 100644
--- a/rid/op/run_label.py
+++ b/rid/op/run_label.py
@@ -6,7 +6,8 @@
     OP,
     OPIO,
     OPIOSign,
-    Artifact
+    Artifact,
+    Parameter
 )
 from rid.constants import (
         gmx_conf_name,
@@ -17,10 +18,18 @@
         plumed_output_name,
         gmx_mdrun_log,
         lmp_mdrun_log,
-        lmp_input_name
+        gmx_xtc_name,
+        gmx_trr_name,
+        gmx_coord_name,
+        gmx_force_name,
+        mf_fig
     )
+
 from rid.utils import run_command, set_directory, list_to_string
 from rid.common.sampler.command import get_grompp_cmd, get_mdrun_cmd
+from rid.common.gromacs.trjconv import generate_coords, generate_forces
+from rid.tools.estimator import CalcMF
+import numpy as np
 
 logging.basicConfig(
     format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
@@ -35,8 +44,8 @@ class RunLabel(OP):
 
     """
     In `RunLabel`, labeling processes are achieved by restrained MD simulations 
-    wehre harmonnic restraints are exerted on collective variables.
-    `RunLabel` is able to run in a standard Gromacs-PLUMED2 env.
+    where harmonnic restraints are exerted on collective variables.
+    `RunLabel` is able to run in a standard Gromacs-PLUMED2 or Lammps-PLUMED2 env.
     """
 
     @classmethod
@@ -46,6 +55,10 @@ def get_input_sign(cls):
                 "task_path": Artifact(Path),
                 "forcefield": Artifact(Path, optional=True),
                 "label_config": Dict,
+                "cv_config": Dict,
+                "at": Artifact(Path, optional=True),
+                "task_name": str,
+                "tail": Parameter(float, default=0.9),
                 "index_file": Artifact(Path, optional=True),
                 "dp_files": Artifact(List[Path], optional=True),
                 "cv_file": Artifact(List[Path], optional=True),
@@ -58,6 +71,9 @@ def get_output_sign(cls):
         return OPIOSign(
             {
                 "plm_out": Artifact(Path),
+                "cv_forces": Artifact(Path),
+                "mf_info": Artifact(Path,optional=True),
+                "mf_fig": Artifact(Path),
                 "md_log": Artifact(Path)
             }
         )
@@ -75,15 +91,16 @@ def execute(
         op_in : dict
             Input dict with components:
 
-            - `task_path`: (`Artifact(Path)`) A directory path containing files for Gromacs MD simulations.
-            - `label_config`: (`Dict`) Configuration of Gromacs simulations in label steps.
+            - `task_path`: (`Artifact(Path)`) A directory path containing files for Gromacs/Lammps MD simulations.
+            - `label_config`: (`Dict`) Configuration of Gromacs/Lammps simulations in label steps.
+            - `cv_config`: (`Dict`) Configuration of CV in MD simulations.
           
         Returns
         -------
             Output dict with components:
         
-            - `plm_out`: (`Artifact(Path)`) Outputs of CV values (`plumed.out` by default) from label steps.
-            - `md_log`: (`Artifact(Path)`) Log files of Gromacs `mdrun` commands.
+            - `forces`: (`Artifact(Path)`) mean forces value of restrained/constrained estimator.
+            - `mf_info`: (`Artifact(Path)`) mean force value and std information of restrained/constrained estimator.
         """
         
         if op_in["index_file"] is None:
@@ -116,23 +133,30 @@ def execute(
 
         with set_directory(op_in["task_path"]):
             if op_in["forcefield"] is not None:
-                os.symlink(op_in["forcefield"], op_in["forcefield"].name)
+                if not os.path.islink(op_in["forcefield"].name):
+                    os.symlink(op_in["forcefield"], op_in["forcefield"].name)
             if op_in["dp_files"] is not None:
                 for file in op_in["dp_files"]:
-                    os.symlink(file, file.name)
+                    if not os.path.islink(file.name):
+                        os.symlink(file, file.name)
             if op_in["index_file"] is not None:
-                os.symlink(op_in["index_file"], op_in["index_file"].name)
+                if not os.path.islink(op_in["index_file"].name):
+                    os.symlink(op_in["index_file"], op_in["index_file"].name)
             if op_in["cv_file"] is not None:
                 for file in op_in["cv_file"]:
                     if file.name != "colvar":
-                        os.symlink(file, file.name)
+                        if not os.path.islink(file.name):
+                            os.symlink(file, file.name)
             if op_in["inputfile"] is not None:
                 for file in op_in["inputfile"]:
                     if file.name == inputfile_name:
-                        os.symlink(file, file.name)
+                        if not os.path.islink(file.name):
+                            os.symlink(file, file.name)
             
             if op_in["dp_files"] is not None:
-                os.environ["GMX_DEEPMD_INPUT_JSON"] = "./input.json"
+                for dp_file in op_in["dp_files"]:
+                    if (dp_file.name).endswith("json"):
+                        os.environ["GMX_DEEPMD_INPUT_JSON"] = dp_file.name
                 
             if grompp_cmd is not None:
                 logger.info(list_to_string(grompp_cmd, " "))
@@ -144,15 +168,59 @@ def execute(
                 return_code, out, err = run_command(run_cmd)
                 assert return_code == 0, err
                 logger.info(err)
-    
+            
+            if op_in["label_config"]["method"] == "constrained":
+                select_system = "System"
+                if "system" in op_in["label_config"]:
+                    select_system = op_in["label_config"]["system"]
+                generate_coords(output_group = select_system, trr = gmx_trr_name, top = op_in["task_path"].joinpath(gmx_conf_name), index = op_in["index_file"], out_coord=gmx_coord_name)
+                generate_forces(output_group = select_system, trr = gmx_trr_name, top = op_in["task_path"].joinpath(gmx_conf_name), index = op_in["index_file"], out_force=gmx_force_name)
+
+        frame_coords = None
+        frame_forces = None
+        traj_path = None
         if op_in["label_config"]["type"] == "gmx":
             mdrun_log = gmx_mdrun_log
+            if os.path.exists(op_in["task_path"].joinpath(gmx_xtc_name)):
+                traj_path = op_in["task_path"].joinpath(gmx_xtc_name)
+            if op_in["label_config"]["method"] == "constrained":
+                frame_coords = op_in["task_path"].joinpath(gmx_coord_name)
+                frame_forces = op_in["task_path"].joinpath(gmx_force_name)
         elif op_in["label_config"]["type"] == "lmp":
             mdrun_log = lmp_mdrun_log
+            if os.path.exists(op_in["task_path"].joinpath("out.dump")):
+                traj_path = op_in["task_path"].joinpath("out.dump")
+        
+        conf = None
+        topology = None
+        plm_out = None
+        
+        if os.path.exists(op_in["task_path"].joinpath(gmx_conf_name)):
+            conf = op_in["task_path"].joinpath(gmx_conf_name)
+        if os.path.exists(op_in["task_path"].joinpath(gmx_top_name)):
+            topology = op_in["task_path"].joinpath(gmx_top_name)
+        if os.path.exists(op_in["task_path"].joinpath(plumed_output_name)):
+            plm_out = op_in["task_path"].joinpath(plumed_output_name)
+            
+        mf_out = CalcMF(
+            conf = conf,
+            task_name = op_in["task_name"],
+            plm_out = plm_out,
+            cv_config = op_in["cv_config"],
+            label_config = op_in["label_config"],
+            tail = op_in["tail"],
+            topology = topology,
+            frame_coords = frame_coords,
+            frame_forces = frame_forces,
+            at = op_in["at"]
+        )
             
         op_out = OPIO(
             {
-                "plm_out": op_in["task_path"].joinpath(plumed_output_name),
+                "plm_out": plm_out,
+                "cv_forces": mf_out["cv_forces"],
+                "mf_info":  mf_out["mf_info"],
+                "mf_fig": Path(op_in["task_name"]).joinpath(mf_fig),
                 "md_log": op_in["task_path"].joinpath(mdrun_log)
             }
         )
diff --git a/rid/op/run_train.py b/rid/op/run_train.py
index 9125f746..0e519150 100644
--- a/rid/op/run_train.py
+++ b/rid/op/run_train.py
@@ -8,9 +8,11 @@
     Artifact,
     Parameter
 )
-from rid.constants import tf_model_name
+from rid.constants import tf_model_name, train_fig, train_log
 from rid.nn.train_net import train
 from rid.nn.freeze import freeze_model
+from matplotlib import pyplot as plt
+from rid.utils import set_directory
 
 
 class TrainModel(OP):
@@ -34,7 +36,9 @@ def get_input_sign(cls):
     def get_output_sign(cls):
         return OPIOSign(
             {
-                "model": Artifact(Path)
+                "model": Artifact(Path),
+                "train_log": Artifact(Path),
+                "train_fig": Artifact(Path)
             }
         )
 
@@ -67,30 +71,62 @@ def execute(
         data_shape = np.load(op_in["data"]).shape
         cv_dim = int(data_shape[1] // 2)
         train_config = op_in["train_config"]
-        train(
-            cv_dim=cv_dim,
-            neurons=train_config["neurons"],
-            angular_mask=op_in["angular_mask"],
-            numb_threads=train_config.get("numb_threads", 8),
-            resnet=train_config["resnet"],
-            use_mix=train_config["use_mix"],
-            restart=train_config.get("restart", False),
-            batch_size=train_config["batch_size"],
-            epoches=train_config["epoches"],
-            lr=train_config["init_lr"],
-            decay_steps=train_config["decay_steps"],
-            decay_rate=train_config["decay_rate"],
-            drop_out_rate=train_config["drop_out_rate"],
-            data_path=str(op_in["data"])
-        )
-        out_put_name = tf_model_name.format(tag=op_in["model_tag"])
-        freeze_model(
-            model_folder=".",
-            output=out_put_name
-        )
+        task_path = Path(op_in["model_tag"])
+        task_path.mkdir(exist_ok=True, parents=True)
+        train_log_name = train_log.format(tag=op_in["model_tag"])
+        with set_directory(task_path):
+            train(
+                cv_dim=cv_dim,
+                neurons=train_config["neurons"],
+                angular_mask=op_in["angular_mask"],
+                numb_threads=train_config.get("numb_threads", 8),
+                resnet=train_config["resnet"],
+                use_mix=train_config["use_mix"],
+                restart=train_config.get("restart", False),
+                batch_size=train_config["batch_size"],
+                epoches=train_config["epoches"],
+                lr=train_config["init_lr"],
+                decay_steps=train_config["decay_steps"],
+                decay_rate=train_config["decay_rate"],
+                drop_out_rate=train_config["drop_out_rate"],
+                data_path=str(op_in["data"]),
+                log_name = train_log_name
+            )
+            out_put_name = tf_model_name.format(tag=op_in["model_tag"])
+            train_fig_name = train_fig.format(tag=op_in["model_tag"])
+            # plot loglog loss png
+            loss_list = []
+            epoch_list = []
+            with open(train_log_name, "r") as f:
+                while True:
+                    line = f.readline()
+                    if "running time" in line:
+                        break
+                    if "rid.nn.model" in line:
+                        data = line.split(" ")
+                        if len(loss_list) == 0:
+                            epoch_list.append(int(data[10][:-1]))
+                            loss_list.append(float(data[14][:-1]))
+                        else:
+                            epoch_list.append(int(data[8][:-1]))
+                            loss_list.append(float(data[12][:-1]))
+
+            plt.figure(figsize=(10, 8), dpi=100)
+            plt.loglog(epoch_list,loss_list)
+            plt.xlabel("log of training epoches")
+            plt.ylabel("log of relative error")
+            plt.title("loglog fig of training")
+            plt.savefig(train_fig_name)
+            
+            freeze_model(
+                model_folder=".",
+                output=out_put_name
+            )
         op_out = OPIO(
             {
-                "model": Path(out_put_name)
+                "model": task_path.joinpath(out_put_name),
+                "train_log": task_path.joinpath(train_log_name),
+                "train_fig": task_path.joinpath(train_fig_name)
             }
         )
         return op_out
diff --git a/rid/select/cluster.py b/rid/select/cluster.py
index b45c7e33..e43924c0 100644
--- a/rid/select/cluster.py
+++ b/rid/select/cluster.py
@@ -3,6 +3,9 @@
 import logging
 import numpy as np
 import sklearn.cluster as skcluster
+from matplotlib import pyplot as plt
+from rid.constants import cluster_fig
+from pathlib import Path
 
 
 logging.basicConfig(
@@ -19,6 +22,7 @@ def __init__(
         self,
         cvs: Union[np.ndarray, List],
         threshold: float,
+        task_name: str,
         angular_mask: Optional[Union[np.ndarray, List]] = None, 
         weights: Optional[Union[np.ndarray, List]] = None,
         max_search_step: int = 500,
@@ -30,6 +34,7 @@ def __init__(
             weights = np.ones(shape=(cvs[1],))
         self.angular_mask = angular_mask
         self.weights = weights
+        self.task_name = task_name
         self.max_search_step = max_search_step
         self.threshold = threshold
         self.cvs = cvs
@@ -47,7 +52,7 @@ def make_threshold(self, numb_cluster_lower, numb_cluster_upper):
         while current_iter < self.max_search_step:
             logger.info(f"making threshold attempt {current_iter}")
             cls_sel = sel_from_cluster(
-                self.cvs, self.threshold, angular_mask=self.angular_mask, 
+                self.cvs, self.threshold, Path(self.task_name),angular_mask=self.angular_mask, 
                 weights=self.weights, max_selection=self.max_selection)
             test_numb_cluster = len(set(cls_sel))
             if test_numb_cluster < numb_cluster_lower:
@@ -64,7 +69,7 @@ def make_threshold(self, numb_cluster_lower, numb_cluster_upper):
     def get_cluster_selection(self):
         if self.cls_sel is None:
             self.cls_sel = sel_from_cluster(
-                self.cvs, self.threshold, angular_mask=self.angular_mask, 
+                self.cvs, self.threshold, Path(self.task_name),angular_mask=self.angular_mask, 
                 weights=self.weights, max_selection=self.max_selection)
         return self.cls_sel
         
@@ -100,12 +105,20 @@ def mk_cluster(dist, distance_threshold):
     return cluster.labels_
 
 
-def sel_from_cluster(cvs, threshold, angular_mask=None, weights=None, max_selection=1000):
+def sel_from_cluster(cvs, threshold, task_path, angular_mask=None, weights=None, max_selection=1000):
     if len(cvs) <= 1:
         return cvs
     weights = np.array(weights)
     dist = mk_dist(cvs, angular_mask, weights) 
     labels = mk_cluster(dist, threshold)
+    # plot clustering distributions
+    xlist = [i for i in range(len(labels))]
+    plt.figure(figsize=(10, 8), dpi=100)
+    plt.xlabel("trajectory frames")
+    plt.ylabel("cluster index")
+    plt.title("cluster distributions along trajectories")
+    plt.scatter(xlist, labels, s = 5)
+    plt.savefig(task_path.joinpath(cluster_fig))
     # make cluster map
     _cls_map = []
     for _ in range(len(set(labels))):
diff --git a/rid/superop/blocks.py b/rid/superop/blocks.py
index 2060f96d..51ec9bac 100755
--- a/rid/superop/blocks.py
+++ b/rid/superop/blocks.py
@@ -28,6 +28,10 @@
 
 
 class InitBlock(Steps):
+    
+    r"""Initial Block SuperOP
+    This SuperOP is the first iteration of the rid-kit cycle.
+    """
     def __init__(
         self,
         name: str,
@@ -37,7 +41,8 @@ def __init__(
         data_op: OP,
         train_op: OP,
         train_config: Dict,
-        upload_python_package = None
+        upload_python_package = None,
+        retry_times = None
     ):
 
         self._input_parameters = {
@@ -58,7 +63,6 @@ def __init__(
             "output_freq": InputParameter(type=float, value=2500),
             "slice_mode": InputParameter(type=str, value="gmx"),
             "label_config": InputParameter(type=Dict),
-            "kappas": InputParameter(type=List[float]),
             "tail": InputParameter(type=float, value=0.9),
             "train_config": InputParameter(type=Dict)
         }        
@@ -69,6 +73,7 @@ def __init__(
             "inputfile": InputArtifact(optional=True),
             "confs" : InputArtifact(),
             "index_file": InputArtifact(optional=True),
+            "data_old": InputArtifact(optional=True),
             "dp_files": InputArtifact(optional=True),
             "cv_file": InputArtifact(optional=True)
         }
@@ -104,6 +109,7 @@ def __init__(
             train_op,
             train_config,
             upload_python_package = upload_python_package,
+            retry_times = retry_times
         )            
     
     @property
@@ -136,6 +142,7 @@ def _first_run_block(
         train_op: OP,
         train_config : Dict,
         upload_python_package : str = None,
+        retry_times: int = None
     ):
     exploration = Step(
         "Exploration",
@@ -195,10 +202,8 @@ def _first_run_block(
         "Label",
         template=label_op,
         parameters={
-            "angular_mask": block_steps.inputs.parameters['angular_mask'],
             "label_config": block_steps.inputs.parameters['label_config'],
             "cv_config": block_steps.inputs.parameters['cv_config'],
-            "kappas": block_steps.inputs.parameters['kappas'],
             "tail": block_steps.inputs.parameters['tail'],
             "conf_tags" : selection.outputs.parameters['selected_conf_tags'],
             "block_tag" : block_steps.inputs.parameters['block_tag'],
@@ -222,8 +227,8 @@ def _first_run_block(
         template=data_op,
         parameters={"block_tag" : block_steps.inputs.parameters['block_tag']},
         artifacts={
-            "forces": label.outputs.artifacts["forces"],
-            "centers": selection.outputs.artifacts["selected_cv_init"]
+            "cv_forces": label.outputs.artifacts["cv_forces"],
+            "data_old": block_steps.inputs.artifacts['data_old']
         },
         key = '{}-gen-data'.format(block_steps.inputs.parameters['block_tag']),
     )
@@ -237,9 +242,10 @@ def _first_run_block(
         template=PythonOPTemplate(
             train_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices("{{item}}",
                 input_parameter=["model_tag"],
-                output_artifact=["model"]),
+                output_artifact=["model","train_fig"]),
             **train_template_config,
         ),
         parameters={
@@ -269,6 +275,10 @@ def _first_run_block(
 
 
 class IterBlock(Steps):
+    
+    r"""Iterative Block SuperOP.
+    This SuperOP is the iterations after the inital iteration of the rid-kit cycle.
+    """
     def __init__(
         self,
         name: str,
@@ -280,7 +290,8 @@ def __init__(
         train_op: OP,  
         adjust_lvl_config: Dict,
         train_config: Dict,
-        upload_python_package = None
+        upload_python_package = None,
+        retry_times = None
     ):
 
         self._input_parameters = {
@@ -302,7 +313,6 @@ def __init__(
             "output_freq": InputParameter(type=float, value=2500),
             "slice_mode": InputParameter(type=str, value="gmx"),
             "label_config": InputParameter(type=Dict),
-            "kappas": InputParameter(type=List[float]),
             "tail": InputParameter(type=float, value=0.9),
             "train_config": InputParameter(type=Dict),
             "adjust_amplifier": InputParameter(type=float, value=1.5),
@@ -356,6 +366,7 @@ def __init__(
             adjust_lvl_config,
             train_config,
             upload_python_package = upload_python_package,
+            retry_times = retry_times
         )            
     
     @property
@@ -390,6 +401,7 @@ def _iter_block(
         adjust_lvl_config : Dict,
         train_config : Dict,
         upload_python_package : str = None,
+        retry_times: int = None
     ):
 
     exploration = Step(
@@ -453,8 +465,10 @@ def _iter_block(
         template=PythonOPTemplate(
             adjust_lvl_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices("{{item}}",
-                input_parameter=["trust_lvl_1", "trust_lvl_2", "numb_cluster", "init_trust_lvl_1", "init_trust_lvl_2"]
+                input_parameter=["trust_lvl_1", "trust_lvl_2", "numb_cluster", "init_trust_lvl_1", "init_trust_lvl_2"],
+                output_parameter=["adjust_trust_lvl_1", "adjust_trust_lvl_2"]
                 ),
             **adjust_lvl_template_config,
         ),
@@ -480,10 +494,8 @@ def _iter_block(
         "Label",
         template=label_op,
         parameters={
-            "angular_mask": block_steps.inputs.parameters['angular_mask'],
             "label_config": block_steps.inputs.parameters['label_config'],
             "cv_config": block_steps.inputs.parameters['cv_config'],
-            "kappas": block_steps.inputs.parameters['kappas'],
             "tail": block_steps.inputs.parameters['tail'],
             "conf_tags" : selection.outputs.parameters['selected_conf_tags'],
             "block_tag" : block_steps.inputs.parameters['block_tag'],
@@ -507,8 +519,7 @@ def _iter_block(
         template=data_op,
         parameters={"block_tag" : block_steps.inputs.parameters['block_tag']},
         artifacts={
-            "forces": label.outputs.artifacts["forces"],
-            "centers": selection.outputs.artifacts["selected_cv_init"],
+            "cv_forces": label.outputs.artifacts["cv_forces"],
             "data_old": block_steps.inputs.artifacts['data_old']
         },
         key = '{}-gen-data'.format(block_steps.inputs.parameters['block_tag']),
@@ -523,9 +534,10 @@ def _iter_block(
         template=PythonOPTemplate(
             train_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices("{{item}}",
                 input_parameter=["model_tag"],
-                output_artifact=["model"]),
+                output_artifact=["model","train_fig"]),
             **train_template_config,
         ),
         parameters={
diff --git a/rid/superop/data.py b/rid/superop/data.py
index b547099e..758a0218 100644
--- a/rid/superop/data.py
+++ b/rid/superop/data.py
@@ -1,4 +1,4 @@
-from typing import Dict, List
+from typing import Dict
 from copy import deepcopy
 from dflow import (
     InputParameter,
@@ -17,20 +17,24 @@
 
 
 class DataGenerator(Steps):
+    
+    r""" Date generator SuperOP.
+    This SuperpOP combines CollectData OP and MergeData OP to process data for training.
+    """
     def __init__(
         self,
         name: str,
         collect_op: OP,
         merge_op: OP,
         run_config: Dict,
-        upload_python_package = None
+        upload_python_package = None,
+        retry_times = None
     ):
         self._input_parameters = {
             "block_tag" : InputParameter(type=str, value="")
         }        
         self._input_artifacts = {
-            "forces": InputArtifact(),
-            "centers": InputArtifact(),
+            "cv_forces": InputArtifact(),
             "data_old": InputArtifact(optional=True)
         }
         self._output_parameters = {}
@@ -56,6 +60,7 @@ def __init__(
             merge_op,
             run_config = run_config,
             upload_python_package = upload_python_package,
+            retry_times = retry_times
         )            
     
     @property
@@ -85,6 +90,7 @@ def _gen_data(
         merge_op : OP,
         run_config : Dict,
         upload_python_package : str = None,
+        retry_times: int = None
     ):
     run_config = deepcopy(run_config)
     run_template_config = run_config.pop('template_config')
@@ -95,12 +101,12 @@ def _gen_data(
         template=PythonOPTemplate(
             collect_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             **run_template_config,
         ),
         parameters={},
         artifacts={
-            "forces": data_steps.inputs.artifacts['forces'],
-            "centers": data_steps.inputs.artifacts['centers']
+            "cv_forces": data_steps.inputs.artifacts['cv_forces'],
         },
         key = '{}-collect-data'.format(data_steps.inputs.parameters["block_tag"]),
         executor = run_executor,
@@ -113,6 +119,7 @@ def _gen_data(
         template=PythonOPTemplate(
             merge_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             **run_template_config,
         ),
         parameters={},
diff --git a/rid/superop/exploration.py b/rid/superop/exploration.py
index 8fa89662..7a85eb5c 100755
--- a/rid/superop/exploration.py
+++ b/rid/superop/exploration.py
@@ -21,6 +21,10 @@
 
 
 class Exploration(Steps):
+    
+    r"""" Exploration SuperOP.
+    This SuperOP combines PrepExplore OP and RunExplore OP.
+    """
     def __init__(
         self,
         name: str,
@@ -28,7 +32,8 @@ def __init__(
         run_op: OP,
         prep_config: Dict,
         run_config: Dict,
-        upload_python_package = None
+        upload_python_package = None,
+        retry_times = None
     ):
         self._input_parameters = {
             "trust_lvl_1" : InputParameter(type=List[float], value=2.0),
@@ -83,6 +88,7 @@ def __init__(
             prep_config = prep_config,
             run_config = run_config,
             upload_python_package = upload_python_package,
+            retry_times = retry_times
         )            
     
     @property
@@ -114,6 +120,7 @@ def _exploration(
         prep_config : Dict,
         run_config : Dict,
         upload_python_package : str = None,
+        retry_times: int = None
     ):
     prep_config = deepcopy(prep_config)
     run_config = deepcopy(run_config)
@@ -126,6 +133,7 @@ def _exploration(
         template=PythonOPTemplate(
             prep_exploration_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices("{{item}}",
                 input_parameter=["task_name", "trust_lvl_1", "trust_lvl_2"],
                 input_artifact=["conf"],
@@ -139,7 +147,8 @@ def _exploration(
             "trust_lvl_2": exploration_steps.inputs.parameters['trust_lvl_2'],
             "exploration_config" : exploration_steps.inputs.parameters['exploration_config'],
             "cv_config" : exploration_steps.inputs.parameters['cv_config'],
-            "task_name": exploration_steps.inputs.parameters['task_names']
+            "task_name": exploration_steps.inputs.parameters['task_names'],
+            "block_tag": exploration_steps.inputs.parameters["block_tag"]
         },
         artifacts={
             "models" : exploration_steps.inputs.artifacts['models'],
@@ -159,9 +168,10 @@ def _exploration(
         template=PythonOPTemplate(
             run_exploration_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices("{{item}}",
                 input_artifact=["task_path"],
-                output_artifact=["plm_out", "trajectory", "md_log", "conf_out"]
+                output_artifact=["plm_out", "bias_fig","model_devi_fig", "trajectory", "md_log", "conf_out"]
             ),
             **run_template_config,
         ),
diff --git a/rid/superop/label.py b/rid/superop/label.py
index 52fccde4..48545ca8 100755
--- a/rid/superop/label.py
+++ b/rid/superop/label.py
@@ -20,23 +20,25 @@
 
 
 class Label(Steps):
+    
+    r"""" Label SuperOP.
+    This SuperOP combines CheckLabelInputs OP, PrepLabel OP and RunLabel OP.   
+    """
     def __init__(
         self,
         name: str,
         check_input_op: OP,
         prep_op: OP,
         run_op: OP,
-        post_op: OP,
         prep_config: Dict,
         run_config: Dict,
-        upload_python_package = None
+        upload_python_package = None,
+        retry_times = None
     ):
 
         self._input_parameters = {
             "label_config": InputParameter(type=Dict),
             "cv_config": InputParameter(type=Dict),
-            "kappas": InputParameter(type=List[float]),
-            "angular_mask": InputParameter(type=List),
             "tail": InputParameter(type=float, value=0.9),
             "conf_tags": InputParameter(type=List),
             "block_tag" : InputParameter(type=str, value="")
@@ -47,7 +49,7 @@ def __init__(
             "forcefield" : InputArtifact(optional=True),
             "inputfile": InputArtifact(optional=True),
             "confs": InputArtifact(),
-            "at": InputArtifact(),
+            "at": InputArtifact(optional=True),
             "index_file": InputArtifact(optional=True),
             "dp_files": InputArtifact(optional=True),
             "cv_file": InputArtifact(optional=True)
@@ -56,7 +58,7 @@ def __init__(
         }
         self._output_artifacts = {
             "md_log": OutputArtifact(),
-            "forces": OutputArtifact()
+            "cv_forces": OutputArtifact()
         }
 
         super().__init__(        
@@ -75,7 +77,6 @@ def __init__(
             "check_label_inputs": "{}-check-label-inputs".format(self.inputs.parameters["block_tag"]),
             "prep_label": "{}-prep-label".format(self.inputs.parameters["block_tag"]),
             "run_label": "{}-run-label".format(self.inputs.parameters["block_tag"]),
-            "post_label": "{}-post-label".format(self.inputs.parameters["block_tag"])
         }
 
         self = _label(
@@ -84,11 +85,10 @@ def __init__(
             check_input_op,
             prep_op,
             run_op,
-            post_op,
             prep_config = prep_config,
             run_config = run_config,
-            post_config = prep_config,
             upload_python_package = upload_python_package,
+            retry_times = retry_times
         )            
     
     @property
@@ -118,29 +118,26 @@ def _label(
         check_label_input_op : OP,
         prep_label_op : OP,
         run_label_op : OP,
-        post_label_op : OP,
         prep_config : Dict,
         run_config : Dict,
-        post_config : Dict,
         upload_python_package : str = None,
+        retry_times: int = None
     ):
     prep_config = deepcopy(prep_config)
     run_config = deepcopy(run_config)
-    post_config = deepcopy(post_config)
 
     prep_template_config = prep_config.pop('template_config')
     run_template_config = run_config.pop('template_config')
-    post_template_config = post_config.pop('template_config')
 
     prep_executor = init_executor(prep_config.pop('executor'))
     run_executor = init_executor(run_config.pop('executor'))
-    post_executor = init_executor(post_config.pop('executor'))
 
     check_label_inputs = Step(
         'check-label-inputs',
         template=PythonOPTemplate(
             check_label_input_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             **prep_template_config,
         ),
         parameters={
@@ -160,6 +157,7 @@ def _label(
         template=PythonOPTemplate(
             prep_label_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices("{{item}}",
                 input_parameter=["task_name"],
                 input_artifact=["conf", "at"],
@@ -169,8 +167,7 @@ def _label(
         parameters={
             "label_config": label_steps.inputs.parameters['label_config'],
             "cv_config": label_steps.inputs.parameters['cv_config'],
-            "task_name": check_label_inputs.outputs.parameters['conf_tags'],
-            "kappas": label_steps.inputs.parameters['kappas']
+            "task_name": check_label_inputs.outputs.parameters['conf_tags']
         },
         artifacts={
             "topology": label_steps.inputs.artifacts['topology'],
@@ -191,13 +188,18 @@ def _label(
         template=PythonOPTemplate(
             run_label_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices("{{item}}",
-                input_artifact=["task_path"],
-                output_artifact=["plm_out", "md_log"]),
+                input_parameter=["task_name"],
+                input_artifact=["task_path","at"],
+                output_artifact=["plm_out","cv_forces","mf_info","mf_fig","md_log"]),
             **run_template_config,
         ),
         parameters={
-            "label_config": label_steps.inputs.parameters["label_config"]
+            "label_config": label_steps.inputs.parameters["label_config"],
+            "cv_config": label_steps.inputs.parameters['cv_config'],
+            "task_name": check_label_inputs.outputs.parameters['conf_tags'],
+            "tail": label_steps.inputs.parameters['tail']
         },
         artifacts={
             "forcefield": label_steps.inputs.artifacts['forcefield'],
@@ -205,44 +207,18 @@ def _label(
             "index_file": label_steps.inputs.artifacts['index_file'],
             "dp_files": label_steps.inputs.artifacts['dp_files'],
             "cv_file": label_steps.inputs.artifacts['cv_file'],
-            "inputfile": label_steps.inputs.artifacts['inputfile']
+            "inputfile": label_steps.inputs.artifacts['inputfile'],
+            "at": label_steps.inputs.artifacts['at']
         },
         key = step_keys['run_label']+"-{{item}}",
         executor = run_executor,
         with_param=argo_range(argo_len(check_label_inputs.outputs.parameters['conf_tags'])),
+        continue_on_success_ratio = 0.75,
         **run_config,
     )
     label_steps.add(run_label)
 
-    post_label = Step(
-        'post-label',
-        template=PythonOPTemplate(
-            post_label_op,
-            python_packages = upload_python_package,
-            slices=Slices("{{item}}",
-                input_parameter=["task_name"],
-                input_artifact=["plm_out", "at"],
-                output_artifact=["forces"]),
-            **post_template_config,
-        ),
-        parameters={
-            "task_name": check_label_inputs.outputs.parameters['conf_tags'],
-            "kappas": label_steps.inputs.parameters['kappas'],
-            "tail": label_steps.inputs.parameters['tail'],
-            "angular_mask": label_steps.inputs.parameters['angular_mask']
-        },
-        artifacts={
-            "plm_out": run_label.outputs.artifacts["plm_out"],
-            "at": label_steps.inputs.artifacts['at']
-        },
-        key = step_keys['post_label']+"-{{item}}",
-        executor = post_executor,
-        with_param=argo_range(argo_len(check_label_inputs.outputs.parameters['conf_tags'])),
-        **post_config,
-    )
-    label_steps.add(post_label)
-
-    label_steps.outputs.artifacts["forces"]._from = post_label.outputs.artifacts["forces"]
+    label_steps.outputs.artifacts["cv_forces"]._from = run_label.outputs.artifacts["cv_forces"]
     label_steps.outputs.artifacts["md_log"]._from = run_label.outputs.artifacts["md_log"]
     
     return label_steps
diff --git a/rid/superop/selector.py b/rid/superop/selector.py
index 4a3e6e9f..8b819b0d 100755
--- a/rid/superop/selector.py
+++ b/rid/superop/selector.py
@@ -22,6 +22,10 @@
 
 
 class Selector(Steps):
+    
+    r""" Selector SuperOP.
+    This SuperOP combines PrepSelect OP and RunSelect OP.    
+    """
     def __init__(
         self,
         name: str,
@@ -29,7 +33,8 @@ def __init__(
         run_op: OP,
         prep_config: Dict,
         run_config: Dict,
-        upload_python_package = None
+        upload_python_package = None,
+        retry_times = None
     ):
         self._input_parameters = {
             "trust_lvl_1" : InputParameter(type=List[float], value=2.0),
@@ -92,6 +97,7 @@ def __init__(
             prep_config = prep_config,
             run_config = run_config,
             upload_python_package = upload_python_package,
+            retry_times = retry_times
         )            
     
     @property
@@ -123,6 +129,7 @@ def _select(
         prep_config : Dict,
         run_config : Dict,
         upload_python_package : str = None,
+        retry_times: int = None
     ):
     prep_config = deepcopy(prep_config)
     run_config = deepcopy(run_config)
@@ -136,11 +143,12 @@ def _select(
         template=PythonOPTemplate(
             prep_select_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices(
                 "{{item}}",
                 input_parameter=["cluster_threshold", "task_name"],
                 input_artifact=["plm_out"],
-                output_artifact=["cluster_selection_index", "cluster_selection_data"],
+                output_artifact=["cluster_fig","cluster_selection_index", "cluster_selection_data"],
                 output_parameter=["cluster_threshold", "numb_cluster"]
                 ),
                 
@@ -171,11 +179,13 @@ def _select(
         template=PythonOPTemplate(
             run_select_op,
             python_packages = upload_python_package,
+            retry_on_transient_error = retry_times,
             slices=Slices(
                 "int({{item}})",
                 input_parameter=["task_name", "trust_lvl_1", "trust_lvl_2"],
                 input_artifact=["cluster_selection_index", "cluster_selection_data", "xtc_traj", "topology"],
-                output_artifact=["selected_confs", "selected_cv_init", "model_devi", "selected_indices"]
+                output_artifact=["selected_confs", "selected_cv_init", "model_devi", "selected_indices"],
+                output_parameter=["selected_conf_tags"]
             ),
             **run_template_config,
         ),
diff --git a/rid/task/builder.py b/rid/task/builder.py
index e364f45c..7e0fbad8 100644
--- a/rid/task/builder.py
+++ b/rid/task/builder.py
@@ -11,8 +11,9 @@
         plumed_input_name,
     )
 from rid.utils import read_txt
+from rid.common.mol import get_distance_from_atomid
 from rid.common.gromacs import make_md_mdp_string
-from rid.common.plumed import make_deepfe_plumed, make_restraint_plumed, get_cv_name
+from rid.common.plumed import make_deepfe_plumed, make_restraint_plumed, make_constraint_plumed, get_cv_name,make_distance_list_from_file
 
 
 class TaskBuilder(ABC):
@@ -30,12 +31,15 @@ def __init__(
         exploration_config: Dict,
         cv_file: Optional[List[str]] = None,
         selected_resid: Optional[List[int]] = None,
+        selected_atomid: Optional[List[int]] = None,
         sampler_type: str = "gmx",
         trust_lvl_1: float = 1.0,
         trust_lvl_2: float = 2.0,
         model_list: List[str] = ["graph.pb"],
         plumed_output: str = "plm.out",
-        cv_mode: str = "torsion"
+        cv_mode: str = "torsion",
+        wall_list: Optional[List[str]] = None,
+        iteration: Optional[str] = None
     ):
         super().__init__()
         self.conf = conf
@@ -44,6 +48,7 @@ def __init__(
         self.stride = self.exploration_config["output_freq"]
         self.cv_file = cv_file
         self.selected_resid = selected_resid
+        self.selected_atomid = selected_atomid
         self.sampler_type = sampler_type
         self.trust_lvl_1 = trust_lvl_1
         self.trust_lvl_2 = trust_lvl_2
@@ -51,10 +56,13 @@ def __init__(
         self.model_list = model_list
         self.plumed_output = plumed_output
         self.cv_mode = cv_mode
+        self.wall_list = wall_list
+        self.iteration = iteration
         self.task = Task()
         self.cv_names = get_cv_name(
             conf=self.conf, cv_file=self.cv_file,
             selected_resid=self.selected_resid,
+            selected_atomid=self.selected_atomid,
             stride=self.stride,
             mode=self.cv_mode
         )
@@ -83,9 +91,10 @@ def build_lmp(self):
     def build_plumed(self):
         return build_plumed_dict(
             conf=self.conf, cv_file=self.cv_file, selected_resid=self.selected_resid,
+            selected_atomid=self.selected_atomid,
             trust_lvl_1=self.trust_lvl_1, trust_lvl_2=self.trust_lvl_2,
             model_list=self.model_list, stride=self.stride, output=self.plumed_output,
-            mode=self.cv_mode
+            mode=self.cv_mode, wall_list = self.wall_list, iteration=self.iteration
         )
     
     def get_cv_dim(self):
@@ -100,6 +109,7 @@ def __init__(
         label_config: Dict,
         cv_file: Optional[List[str]] = None,
         selected_resid: Optional[List[int]] = None,
+        selected_atomid: Optional[List[int]] = None,
         sampler_type: str = "gmx",
         kappa: Union[int, float, List[Union[int, float]]] = 0.5,
         at: Union[int, float, List[Union[int, float]]] = 1.0,
@@ -113,6 +123,7 @@ def __init__(
         self.stride = self.label_config["output_freq"]
         self.cv_file = cv_file
         self.selected_resid = selected_resid
+        self.selected_atomid = selected_atomid
         self.plumed_output = plumed_output
         self.cv_mode = cv_mode
         self.sampler_type = sampler_type
@@ -143,10 +154,59 @@ def build_lmp(self):
     def build_plumed(self):
         return build_plumed_restraint_dict(
             conf=self.conf, cv_file=self.cv_file, selected_resid=self.selected_resid,
-            kappa=self.kappa, at=self.at,
+            selected_atomid=self.selected_atomid, kappa=self.kappa, at=self.at,
             stride=self.stride, output=self.plumed_output, mode=self.cv_mode
         )
 
+class ConstrainedMDTaskBuilder(TaskBuilder):
+    def __init__(
+        self,
+        conf: str,
+        topology: Optional[str],
+        label_config: Dict,
+        cv_file: Optional[List[str]] = None,
+        selected_atomid: Optional[List[int]] = None,
+        sampler_type: str = "gmx",
+        plumed_output: str = "plm.out",
+        cv_mode: str = "distance"
+    ):
+        super().__init__()
+        self.conf = conf
+        self.topology = topology
+        self.label_config = label_config
+        self.stride = self.label_config["output_freq"]
+        self.cv_file = cv_file
+        self.selected_atomid = selected_atomid
+        self.plumed_output = plumed_output
+        self.cv_mode = cv_mode
+        self.sampler_type = sampler_type
+        self.task = Task()
+    
+    def build(self) -> Task:
+        task_dict = {}
+        if self.sampler_type == "gmx":
+            task_dict.update(self.build_gmx())
+        elif self.sampler_type == "lmp":
+            task_dict.update(self.build_lmp())
+        task_dict.update(self.build_plumed())
+        for fname, fconts in task_dict.items():
+            self.task.add_file(fname, fconts)
+        return self.task
+    
+    def get_task(self):
+        return self.task
+     
+    def build_gmx(self):
+        return build_gmx_constraint_dict(self.conf, self.topology, self.label_config, self.selected_atomid)
+    
+    def build_lmp(self):
+        return build_lmp_dict(self.conf)
+        
+    def build_plumed(self):
+        return build_plumed_constraint_dict(
+            conf=self.conf, cv_file=self.cv_file, selected_atomid=self.selected_atomid,
+            stride=self.stride, output=self.plumed_output, mode=self.cv_mode
+        )
 
 def build_gmx_dict(
         conf: str,
@@ -160,6 +220,30 @@ def build_gmx_dict(
     gmx_task_files[gmx_mdp_name]  = (mdp_string, "w")
     return gmx_task_files
 
+def build_gmx_constraint_dict(
+        conf: str,
+        topology: str,
+        gmx_config: Dict,
+        selected_atomid: Optional[List[int]] = None
+    ):
+    gmx_task_files = {}
+    gmx_task_files[gmx_conf_name] = (read_txt(conf), "w")
+    cv_info = get_distance_from_atomid(conf, selected_atomid)
+    ret = ""
+    with open(topology, "r") as f:
+        for line in f.readlines():
+            ret += line
+            if "constraints" in line:
+                print("constrained md operating normally!\n")
+                ret += "; atom1 atom2    funct   dis\n"
+                for dis_id in range(len(selected_atomid)):
+                    ret += "%s %s 2 %s\n"%(selected_atomid[dis_id][0], selected_atomid[dis_id][1],\
+                        cv_info["%s %s"%(selected_atomid[dis_id][0],selected_atomid[dis_id][1])])        
+    gmx_task_files[gmx_top_name]  = (ret, "w")
+    mdp_string = make_md_mdp_string(gmx_config)
+    gmx_task_files[gmx_mdp_name]  = (mdp_string, "w")
+    return gmx_task_files
+
 def build_lmp_dict(
     conf: str
 ):
@@ -171,19 +255,23 @@ def build_plumed_dict(
         conf: Optional[str] = None,
         cv_file: Optional[str] = None,
         selected_resid: Optional[List[int]] = None,
+        selected_atomid: Optional[List[int]] = None,
         trust_lvl_1: float = 1.0,
         trust_lvl_2: float = 2.0,
         model_list: List[str] = ["graph.pb"],
         stride: int = 100,
         output: str = "plm.out",
-        mode: str = "torsion"    
+        mode: str = "torsion",
+        wall_list: Optional[List[str]] = None,
+        iteration: Optional[str] = None
     ):
     plumed_task_files = {}
     plm_content = make_deepfe_plumed(
         conf=conf, cv_file=cv_file, selected_resid=selected_resid,
+        selected_atomid = selected_atomid,
         trust_lvl_1=trust_lvl_1, trust_lvl_2=trust_lvl_2,
         model_list=model_list, stride=stride,
-        output=output, mode=mode
+        output=output, mode=mode, wall_list=wall_list, iteration=iteration
     )
     plumed_task_files[plumed_input_name] = (plm_content, "w")
     return plumed_task_files
@@ -192,17 +280,39 @@ def build_plumed_restraint_dict(
         conf: Optional[str] = None,
         cv_file: Optional[str] = None,
         selected_resid: Optional[List[int]] = None,
+        selected_atomid: Optional[List[int]] = None,
         kappa: Union[int, float, Sequence, np.ndarray] = 0.5,
         at: Union[int, float, Sequence, np.ndarray] = 1.0,
         stride: int = 100,
         output: str = "plm.out",
-        mode: str = "torsion"    
+        mode: str = "torsion"
     ):
     plumed_task_files = {}
+    if selected_atomid is not None:
+        at = []
+        cv_info = get_distance_from_atomid(conf, selected_atomid)
+        for dis_id in range(len(selected_atomid)):
+            at.append(cv_info["%s %s"%(selected_atomid[dis_id][0],selected_atomid[dis_id][1])])
     plm_content = make_restraint_plumed(
-        conf=conf, cv_file=cv_file, selected_resid=selected_resid,
+        conf=conf, cv_file=cv_file, selected_resid=selected_resid,selected_atomid = selected_atomid,
         kappa=kappa, at=at, stride=stride,
         output=output, mode=mode
     )
     plumed_task_files[plumed_input_name] = (plm_content, "w")
+    return plumed_task_files
+
+def build_plumed_constraint_dict(
+        conf: Optional[str] = None,
+        cv_file: Optional[str] = None,
+        selected_atomid: Optional[List[int]] = None,
+        stride: int = 100,
+        output: str = "plm.out",
+        mode: str = "distance"
+    ):
+    plumed_task_files = {}
+    plm_content = make_constraint_plumed(
+        conf=conf, cv_file=cv_file, selected_atomid=selected_atomid,
+        stride=stride, output=output, mode=mode
+    )
+    plumed_task_files[plumed_input_name] = (plm_content, "w")
     return plumed_task_files
\ No newline at end of file
diff --git a/rid/template/machine_bohrium.json b/rid/template/machine_bohrium.json
index 647ea1f3..888069de 100755
--- a/rid/template/machine_bohrium.json
+++ b/rid/template/machine_bohrium.json
@@ -1,13 +1,11 @@
 {
     "resources": {
-        "local_k8s": {
-            "template_config" : {
-                "image": "pkufjhdocker/rid-tf-cpu:latest", 
-                "image_pull_policy" : "IfNotPresent"
-            }
-        },
         "bohrium1": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
             "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
             "machine_dict":{
                 "batch_type": "DpCloudServer",
                 "context_type": "DpCloudServerContext",
@@ -20,28 +18,24 @@
                         "api_version":2,
                         "job_type": "container",
                         "log_file": "tmp_log",
-                        "grouped":true,
-                        "job_name": "test_rid",
-                        "disk_size": 100,
-                        "_instance_group_id": 4,
-                        "scass_type":"c8_m32_1 * NVIDIA V100",
+                        "job_name": "rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
                         "platform": "ali",
-                        "image_name":"pkufjhdocker/rid-gmx-exploration:latest",
-                        "on_demand":0
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-exploration:stable"
                         }
                 }
             },
             "resources_dict":{
-                "number_node": 1,
-                "cpu_per_node": 8,
-                "gpu_per_node": 1,
-                "queue_name": "GPU",
-                "group_size": 1
+                "batch_type:": "Bohrium"
             }
         }
         },
         "bohrium2": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
             "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
             "machine_dict":{
                 "batch_type": "DpCloudServer",
                 "context_type": "DpCloudServerContext",
@@ -54,28 +48,24 @@
                         "api_version":2,
                         "job_type": "container",
                         "log_file": "tmp_log",
-                        "grouped":true,
                         "job_name": "test_rid",
-                        "disk_size": 100,
-                        "_instance_group_id": 4,
-                        "scass_type":"c8_m32_1 * NVIDIA V100",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
                         "platform": "ali",
-                        "image_name":"pkufjhdocker/rid-gmx-plumed:latest",
-                        "on_demand":0
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-plumed:stable"
                         }
                 }
             },
             "resources_dict":{
-                "number_node": 1,
-                "cpu_per_node": 8,
-                "gpu_per_node": 1,
-                "queue_name": "GPU",
-                "group_size": 1
+                "batch_type:": "Bohrium"
             }
         }
         },
         "bohrium3": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
             "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
             "machine_dict":{
                 "batch_type": "DpCloudServer",
                 "context_type": "DpCloudServerContext",
@@ -88,37 +78,89 @@
                         "api_version":2,
                         "job_type": "container",
                         "log_file": "tmp_log",
-                        "grouped":true,
-                        "job_name": "test_rid",
-                        "disk_size": 100,
-                        "_instance_group_id": 4,
-                        "scass_type":"c8_m32_1 * NVIDIA V100",
+                        "job_name": "rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-tf-cpu:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium4": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-tf:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium5": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
                         "platform": "ali",
-                        "image_name":"pkufjhdocker/rid-tf-cpu:latest",
-                        "on_demand":0
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-tf-gpu:stable"
                         }
                 }
             },
             "resources_dict":{
-                "number_node": 1,
-                "cpu_per_node": 8,
-                "gpu_per_node": 1,
-                "queue_name": "GPU",
-                "group_size": 1
+                "batch_type:": "Bohrium"
             }
         }
         }
     },
 
     "tasks": {
-        "prep_exploration_config": "local_k8s",
+        "prep_exploration_config": "bohrium3",
         "run_exploration_config": "bohrium1",
-        "prep_label_config": "local_k8s",
+        "prep_label_config": "bohrium3",
         "run_label_config": "bohrium2",
-        "prep_select_config": "local_k8s",
-        "run_select_config": "bohrium3",
-        "prep_data_config": "local_k8s",
-        "run_train_config": "bohrium3",
-        "workflow_steps_config": "local_k8s"
+        "prep_select_config": "bohrium3",
+        "run_select_config": "bohrium4",
+        "prep_data_config": "bohrium3",
+        "run_train_config": "bohrium5",
+        "workflow_steps_config": "bohrium3"
     }
 }
\ No newline at end of file
diff --git a/rid/template/machine_bohrium_k8s.json b/rid/template/machine_bohrium_k8s.json
new file mode 100755
index 00000000..b4378035
--- /dev/null
+++ b/rid/template/machine_bohrium_k8s.json
@@ -0,0 +1,143 @@
+{
+    "resources": {
+        "local_k8s": {
+            "template_config" : {
+                "image": "registry.dp.tech/public/pkufjhdocker/rid-tf-cpu:stable", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "bohrium1": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "test_rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-exploration:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium2": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "test_rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-plumed:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium3": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "test_rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-gmx-tf:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        },
+        "bohrium4": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "DpCloudServer",
+                "context_type": "DpCloudServerContext",
+                "local_root" : "./",
+                "remote_profile":{
+                    "email": "",
+                    "password": "",
+                    "program_id": "",
+                    "input_data":{
+                        "api_version":2,
+                        "job_type": "container",
+                        "log_file": "tmp_log",
+                        "job_name": "test_rid",
+                        "scass_type":"c12_m92_1 * NVIDIA V100",
+                        "platform": "ali",
+                        "image_name":"registry.dp.tech/public/pkufjhdocker/rid-tf-gpu:stable"
+                        }
+                }
+            },
+            "resources_dict":{
+                "batch_type:": "Bohrium"
+            }
+        }
+        }
+    },
+
+    "tasks": {
+        "prep_exploration_config": "local_k8s",
+        "run_exploration_config": "bohrium1",
+        "prep_label_config": "local_k8s",
+        "run_label_config": "bohrium2",
+        "prep_select_config": "local_k8s",
+        "run_select_config": "bohrium3",
+        "prep_data_config": "local_k8s",
+        "run_train_config": "bohrium4",
+        "workflow_steps_config": "local_k8s"
+    }
+}
\ No newline at end of file
diff --git a/rid/template/machine_local_k8s.json b/rid/template/machine_local_k8s.json
new file mode 100644
index 00000000..e8dbefe2
--- /dev/null
+++ b/rid/template/machine_local_k8s.json
@@ -0,0 +1,51 @@
+{
+    "resources": {
+        "local_k8s_1": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-gmx-exploration:stable", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "local_k8s_2": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-gmx-plumed:stable", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "local_k8s_3": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-tf-cpu:stable", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "local_k8s_4": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-gmx-tf:stable", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "local_k8s_5": {
+            "template_config" : {
+                "image": "pkufjhdocker/rid-tf-gpu:stable", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        }
+    },
+
+    "tasks": {
+        "prep_exploration_config": "local_k8s_3",
+        "run_exploration_config": "local_k8s_1",
+        "prep_label_config": "local_k8s_3",
+        "run_label_config": "local_k8s_2",
+        "prep_select_config": "local_k8s_3",
+        "run_select_config": "local_k8s_4",
+        "prep_data_config": "local_k8s_3",
+        "run_train_config": "local_k8s_5",
+        "workflow_steps_config": "local_k8s_3"
+    }
+}
\ No newline at end of file
diff --git a/rid/template/machine_slurm.json b/rid/template/machine_slurm.json
deleted file mode 100644
index 5fc0ee16..00000000
--- a/rid/template/machine_slurm.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
-    "resources": {
-        "local_k8s": {
-            "template_config" : {
-                "image": "pkufjhdocker/rid-tf-cpu:latest", 
-                "image_pull_policy" : "IfNotPresent"
-            }
-        },
-        "remote_slurm": {
-            "executor": {
-                "type": "slurm",
-                "host": "",
-                "port": 22,
-                "username": "",
-                "password": "",
-                "header": [
-                    "#!/bin/bash",
-                    "#SBATCH --partition GPU_2080Ti",
-                    "#SBATCH -N 1",
-                    "#SBATCH --ntasks-per-node 8",
-                    "#SBATCH -t 120:0:0",
-                    "#SBATCH --gres=gpu:1",
-                    "conda init bash",
-                    "source ~/.bashrc",
-                    "source /path/to/rid.env"
-                ]
-            }
-        }
-    },
-
-    "tasks": {
-        "prep_exploration_config": "local_k8s",
-        "run_exploration_config": "remote_slurm",
-        "prep_label_config": "local_k8s",
-        "run_label_config": "remote_slurm",
-        "prep_select_config": "local_k8s",
-        "run_select_config": "local_k8s",
-        "prep_data_config": "local_k8s",
-        "run_train_config": "remote_slurm",
-        "workflow_steps_config": "local_k8s"
-    }
-}
\ No newline at end of file
diff --git a/rid/template/machine_slurm_k8s.json b/rid/template/machine_slurm_k8s.json
new file mode 100644
index 00000000..b23d36e7
--- /dev/null
+++ b/rid/template/machine_slurm_k8s.json
@@ -0,0 +1,56 @@
+{
+    "resources": {
+        "local_k8s": {
+            "template_config" : {
+                "image": "registry.dp.tech/public/pkufjhdocker/rid-tf-cpu:stable", 
+                "image_pull_policy" : "IfNotPresent",
+                "requests" : {"ephemeral-storage": "1Gi"}
+            }
+        },
+        "remote_slurm": {
+            "template_config":{
+                "requests" : {"ephemeral-storage": "1Gi"}
+            },
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "machine_dict":{
+                "batch_type": "Slurm",
+                "context_type": "SSHContext",
+                "local_root" : "./",
+                "remote_root": "",
+                "remote_profile":{
+                    "hostname": "",
+                    "username": "",
+                    "password": "",
+                    "port": "",
+                    "timeout": 20
+                }
+            },
+            "resources_dict":{
+                "number_node": 1,
+                "cpu_per_node": 8,
+                "gpu_per_node": 1,
+                "queue_name": "",
+                "group_size": 1,
+                "custom_flags": [
+                    "#SBATCH --time=120:00:00",
+                    "#SBATCH --exclude=3090-[000,007]"
+                   ],
+                "source_list": ["/path/to/rid.env"]
+            }
+        }
+        }
+    },
+
+    "tasks": {
+        "prep_exploration_config": "local_k8s",
+        "run_exploration_config": "remote_slurm",
+        "prep_label_config": "local_k8s",
+        "run_label_config": "remote_slurm",
+        "prep_select_config": "local_k8s",
+        "run_select_config": "remote_slurm",
+        "prep_data_config": "local_k8s",
+        "run_train_config": "remote_slurm",
+        "workflow_steps_config": "local_k8s"
+    }
+}
\ No newline at end of file
diff --git a/rid/template/machine_slurm_local.json b/rid/template/machine_slurm_local.json
new file mode 100644
index 00000000..fc29c60e
--- /dev/null
+++ b/rid/template/machine_slurm_local.json
@@ -0,0 +1,62 @@
+{
+    "resources": {
+        "local_machine": {
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "merge_sliced_step": "True",
+            "machine_dict":{
+                "batch_type": "Slurm",
+                "context_type": "Local",
+                "local_root" : "./",
+                "remote_root": ""
+            },
+            "resources_dict":{
+                "number_node": 1,
+                "cpu_per_node": 8,
+                "gpu_per_node": 1,
+                "queue_name": "GPU",
+                "group_size": 1,
+                "custom_flags": [
+                    "#SBATCH --time=120:00:00"
+                   ],
+                "source_list": ["/path/to/rid.env"]
+            }
+        }
+        },
+        "local_machine_cpu": {
+            "executor":{
+            "image": "dptechnology/dpdispatcher:latest",
+            "merge_sliced_step": "True",
+            "machine_dict":{
+                "batch_type": "Slurm",
+                "context_type": "Local",
+                "local_root" : "./",
+                "remote_root": ""
+            },
+            "resources_dict":{
+                "number_node": 1,
+                "cpu_per_node": 8,
+                "gpu_per_node": 0,
+                "queue_name": "C032M0128G",
+                "group_size": 1,
+                "custom_flags": [
+                    "#SBATCH --time=120:00:00"
+                   ],
+                "source_list": ["/path/to/rid.env"]
+            }
+        }
+        }
+    },
+
+    "tasks": {
+        "prep_exploration_config": "local_machine_cpu",
+        "run_exploration_config": "local_machine",
+        "prep_label_config": "local_machine_cpu",
+        "run_label_config": "local_machine",
+        "prep_select_config": "local_machine",
+        "run_select_config": "local_machine",
+        "prep_data_config": "local_machine",
+        "run_train_config": "local_machine",
+        "workflow_steps_config": "local_machine_cpu"
+    }
+}
\ No newline at end of file
diff --git a/rid/template/rid.json b/rid/template/rid.json
deleted file mode 100644
index 02a6d824..00000000
--- a/rid/template/rid.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-    "name": "test",
-    "numb_walkers": 2,
-    "numb_iters": 20,
-    "trust_lvl_1": 2,
-    "trust_lvl_2": 3,
-    "init_models": [],
-    
-    "CV": {
-        "mode": "torsion",
-        "selected_resid": [ 1, 2 ],
-        "angular_mask": [ 1, 1 ],
-        "weights": [ 1, 1 ],
-        "cv_file":""
-    },
-
-    "ExploreMDConfig": {
-        "type": "gmx",
-        "nsteps": 25000,
-        "output_freq": 25,
-        "ref-t": "300 300",
-        "tc-grps": "water non-water",
-        "dt": 0.002,
-        "output_mode": "both",
-        "ntmpi": 1,
-        "nt": 8,
-        "max_warning": 0
-    },
-
-    "SelectorConfig": {
-        "numb_cluster_lower": 16,
-        "numb_cluster_upper": 26,
-        "cluster_threshold": 1,
-        "max_selection": 30,
-        "numb_cluster_threshold": 8,
-        "slice_mode": "gmx"
-    },
-
-    "LabelMDConfig": {
-        "type": "gmx",
-        "nsteps": 25000,
-        "output_freq": 50,
-        "ref-t": "300 300",
-        "tc-grps": "water non-water",
-        "dt": 0.002,
-        "output_mode": "both",
-        "ntmpi": 1,
-        "nt": 8,
-        "max_warning": 0,
-        "kappas": [ 500, 500 ]
-    },
-
-    "Train": {
-        "numb_models": 4,
-        "neurons": [ 50, 50, 50, 50 ],
-        "resnet": true,
-        "batch_size": 32,
-        "epoches": 2000,
-        "init_lr": 0.0008,
-        "decay_steps": 120,
-        "decay_rate": 0.96,
-        "drop_out_rate": 0.1,
-        "numb_threads": 8,
-        "use_mix": false,
-        "restart": false
-    }
-}
\ No newline at end of file
diff --git a/rid/template/rid_gmx_dih.json b/rid/template/rid_gmx_dih.json
new file mode 100755
index 00000000..7f5524b0
--- /dev/null
+++ b/rid/template/rid_gmx_dih.json
@@ -0,0 +1,86 @@
+{
+    "name": "test",
+    "numb_walkers": 2,
+    "numb_iters": 2,
+    "trust_lvl_1": 2,
+    "trust_lvl_2": 3,
+    "init_models": [],
+    
+    "CV": {
+        "mode": "torsion",
+        "selected_resid": [ 1, 2 ],
+        "angular_mask": [ 1, 1 ],
+        "weights": [ 1, 1 ],
+        "cv_file":[""]
+    },
+
+    "ExploreMDConfig": {
+        "nsteps": 50000,
+        "type": "gmx",
+        "temperature": 300,
+        "output_freq": 500,
+        "ref-t": "300 300",
+        "verlet-buffer-tolerance":"-1",
+        "rlist": 1,
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
+        "dt": 0.002,
+        "fourierspacing": "0.12",
+        "output_mode": "single",
+        "ntmpi": 1,
+        "nt": 8,
+        "max_warning": 2
+    },
+
+    "SelectorConfig": {
+        "numb_cluster_lower": 12,
+        "numb_cluster_upper": 22,
+        "cluster_threshold": 1.5,
+        "max_selection": 30,
+        "numb_cluster_threshold": 6,
+        "slice_mode": "gmx"
+    },
+
+    "LabelMDConfig": {
+        "nsteps": 50000,
+        "temperature":300,
+        "method": "restrained",
+        "type": "gmx",
+        "output_freq": 100,
+        "ref-t": "300 300",
+        "rlist": 1,
+        "verlet-buffer-tolerance":"-1",
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
+        "dt": 0.002,
+        "fourierspacing": "0.12",
+        "output_mode": "single",
+        "ntmpi": 1,
+        "nt": 8,
+        "max_warning": 2,
+        "kappas": [ 500, 500 ]
+    },
+
+    "Train": {
+        "numb_models": 4,
+        "neurons": [ 200, 200, 200, 200 ],
+        "resnet": true,
+        "batch_size": 128,
+        "epoches": 8000,
+        "init_lr": 0.0008,
+        "decay_steps": 120,
+        "decay_rate": 0.96,
+        "drop_out_rate": 0,
+        "numb_threads": 8,
+        "use_mix": false,
+        "restart": false
+    }
+}
\ No newline at end of file
diff --git a/rid/template/rid_gmx_dis.json b/rid/template/rid_gmx_dis.json
new file mode 100755
index 00000000..b2a747fe
--- /dev/null
+++ b/rid/template/rid_gmx_dis.json
@@ -0,0 +1,86 @@
+{
+    "name": "test",
+    "numb_walkers": 2,
+    "numb_iters": 2,
+    "trust_lvl_1": 2,
+    "trust_lvl_2": 3,
+    "init_models": [],
+    
+    "CV": {
+        "mode": "distance",
+        "selected_atomid": [[2,5],[5,7]],
+        "angular_mask": [0,0],
+        "weights": [1,1],
+        "cv_file":[""]
+    },
+
+    "ExploreMDConfig": {
+        "nsteps": 50000,
+        "type": "gmx",
+        "temperature": 300,
+        "output_freq": 500,
+        "ref-t": "300 300",
+        "verlet-buffer-tolerance":"-1",
+        "rlist": 1,
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
+        "dt": 0.002,
+        "fourierspacing": "0.12",
+        "output_mode": "single",
+        "ntmpi": 1,
+        "nt": 8,
+        "max_warning": 2
+    },
+
+    "SelectorConfig": {
+        "numb_cluster_lower": 12,
+        "numb_cluster_upper": 22,
+        "cluster_threshold": 1.5,
+        "max_selection": 30,
+        "numb_cluster_threshold": 6,
+        "slice_mode": "gmx"
+    },
+
+    "LabelMDConfig": {
+        "nsteps": 50000,
+        "temperature":300,
+        "method": "constrained",
+        "system": "Protein",
+        "type": "gmx",
+        "output_freq": 100,
+        "ref-t": "300 300",
+        "rlist": 1,
+        "verlet-buffer-tolerance":"-1",
+        "rvdw": 0.9,
+        "rvdw-switch": 0,
+        "rcoulomb": 0.9,
+        "rcoulomb-switch": 0,
+        "epsilon-r":1,
+        "epsilon-rf":80,
+        "dt": 0.001,
+        "fourierspacing": "0.12",
+        "output_mode": "both",
+        "ntmpi": 1,
+        "nt": 8,
+        "max_warning": 2
+    },
+
+    "Train": {
+        "numb_models": 4,
+        "neurons": [ 200, 200, 200, 200 ],
+        "resnet": true,
+        "batch_size": 128,
+        "epoches": 8000,
+        "init_lr": 0.0008,
+        "decay_steps": 120,
+        "decay_rate": 0.96,
+        "drop_out_rate": 0,
+        "numb_threads": 8,
+        "use_mix": false,
+        "restart": false
+    }
+}
\ No newline at end of file
diff --git a/rid/template/rid_gmx.json b/rid/template/rid_gmx_dp.json
similarity index 96%
rename from rid/template/rid_gmx.json
rename to rid/template/rid_gmx_dp.json
index 8918275e..a43acc9f 100755
--- a/rid/template/rid_gmx.json
+++ b/rid/template/rid_gmx_dp.json
@@ -24,6 +24,7 @@
 
     "ExploreMDConfig": {
         "type": "gmx",
+        "temperature": 300,
         "dp_model": ["dp.000.pb"],
         "nsteps": 2000,
         "ref-t": "300 300",
@@ -53,6 +54,8 @@
 
     "LabelMDConfig": {
         "type": "gmx",
+        "method": "constrained",
+        "temperature": 300,
         "dp_model": ["dp.000.pb"],
         "nsteps": 1000,
         "ref-t": "300 300",
diff --git a/rid/template/rid_lmp.json b/rid/template/rid_lmp_dp.json
similarity index 97%
rename from rid/template/rid_lmp.json
rename to rid/template/rid_lmp_dp.json
index b707ea46..3f9cd2dd 100755
--- a/rid/template/rid_lmp.json
+++ b/rid/template/rid_lmp_dp.json
@@ -35,6 +35,7 @@
 
     "LabelMDConfig": {
         "type":"lmp",
+        "method": "restrained",
         "inputfile":"input_label.lammps",
         "dp_model":["dp003.pb"],
         "dt": 0.002,
diff --git a/rid/tools/estimator.py b/rid/tools/estimator.py
new file mode 100644
index 00000000..9161dffc
--- /dev/null
+++ b/rid/tools/estimator.py
@@ -0,0 +1,294 @@
+from dflow.python import (
+    OP,
+    OPIO,
+    OPIOSign,
+    Artifact,
+    Parameter
+)
+
+from typing import List, Optional, Union, Dict
+from pathlib import Path
+import numpy as np
+from rid.constants import (
+        cv_force_out
+    )
+from rid.common.gromacs.trjconv import generate_coords, generate_forces
+from rid.utils import load_txt, set_directory
+from rid.constants import gmx_coord_name, gmx_force_name,f_cvt,kb,mf_fig
+from matplotlib import pyplot as plt
+import os
+from parmed import gromacs
+import parmed as pmd
+
+def plot_mf_average_all(mf_average, task_path, dt):
+    plt.figure(figsize=(10, 8), dpi=100)
+    xlist = [i*dt for i in range(len(mf_average))]
+    for index in range(mf_average.shape[1]):
+        plt.scatter(xlist, mf_average[:,index],label="d%s"%(index+1))
+    plt.title("running average of mean force")
+    plt.xlabel("constrained simulation time (ps)")
+    plt.ylabel("mean force (KJ/(mol*nm))")
+    plt.legend()
+    plt.savefig(task_path.joinpath(mf_fig))
+
+def phase_factor(r_cv, cv_init, selected_atomid_simple, mass_list):
+    C_m = np.zeros(shape=(len(cv_init), len(cv_init)))
+    C_list = np.zeros(shape=(len(cv_init), len(r_cv)))
+    for index in range(len(cv_init)):
+        atom_id1 = selected_atomid_simple[index][0]
+        atom_id2 = selected_atomid_simple[index][1]
+        C_list[index][atom_id1*3] = (r_cv[atom_id1*3] - r_cv[atom_id2*3])/cv_init[index]
+        C_list[index][atom_id1*3+1] = (r_cv[atom_id1*3+1] - r_cv[atom_id2*3+1])/cv_init[index]
+        C_list[index][atom_id1*3+2] = (r_cv[atom_id1*3+2] - r_cv[atom_id2*3+2])/cv_init[index]
+        C_list[index][atom_id2*3] = -(r_cv[atom_id1*3] - r_cv[atom_id2*3])/cv_init[index]
+        C_list[index][atom_id2*3+1] = -(r_cv[atom_id1*3+1] - r_cv[atom_id2*3+1])/cv_init[index]
+        C_list[index][atom_id2*3+2] = -(r_cv[atom_id1*3+2] - r_cv[atom_id2*3+2])/cv_init[index]
+    
+    Mass = np.diag(mass_list)
+    Mass = np.linalg.inv(Mass)
+    for index_i in range(len(cv_init)):
+        for index_j in range(len(cv_init)):
+            C_m[index_i,index_j] = np.dot(np.dot(C_list[index_i],Mass),C_list[index_j])
+    
+    A = np.linalg.det(C_m)
+    return A
+
+def pseudo_inv(r_cv, cv_init, selected_atomid_simple):
+    A = np.zeros((len(r_cv),len(cv_init)))
+    for index in range(len(cv_init)):
+        atom_id1 = selected_atomid_simple[index][0]
+        atom_id2 = selected_atomid_simple[index][1]
+        A[atom_id1*3,index] = (r_cv[atom_id1*3] - r_cv[atom_id2*3])/cv_init[index]
+        A[atom_id1*3+1,index] = (r_cv[atom_id1*3+1] - r_cv[atom_id2*3+1])/cv_init[index]
+        A[atom_id1*3+2,index] = (r_cv[atom_id1*3+2] - r_cv[atom_id2*3+2])/cv_init[index]
+        A[atom_id2*3,index] = -(r_cv[atom_id1*3] - r_cv[atom_id2*3])/cv_init[index]
+        A[atom_id2*3+1,index] = -(r_cv[atom_id1*3+1] - r_cv[atom_id2*3+1])/cv_init[index]
+        A[atom_id2*3+2,index] = -(r_cv[atom_id1*3+2] - r_cv[atom_id2*3+2])/cv_init[index]
+    
+    U, S, Vh = np.linalg.svd(A, full_matrices = False)
+    B = np.matmul(np.matmul(np.transpose(Vh),np.linalg.inv(np.diag(S))),np.transpose(U))
+    
+    return B
+
+if os.path.exists("/gromacs/share/gromacs/top"):
+    gromacs.GROMACS_TOPDIR = "/gromacs/share/gromacs/top"
+elif os.path.exists("/opt/conda/share/gromacs/top"):
+    gromacs.GROMACS_TOPDIR = "/opt/conda/share/gromacs/top"
+
+def CalcMF(
+    conf: str,
+    task_name: str,
+    plm_out: str,
+    cv_config: dict,
+    label_config: dict,
+    tail: float = 0.9,
+    topology: Optional[str] = None,
+    frame_coords: Optional[str] = None,
+    frame_forces: Optional[str] = None,
+    at: Optional[str] = None
+):
+    """
+    Calculate mean force with the results of restrained or constrained MD. 
+
+    CalcMF will handle periodic CVs by `angular_mask`. 
+    To get the mean value of CVs near equilibrium state under restrained or constrained MD, only part of outputs of CV values 
+    (the last `tail` part, for example, the last 90% CV values) are used. 
+    """
+    
+    if label_config["method"] == "restrained":
+        data = load_txt(plm_out)
+        data = data[:, 1:]  # removr the first column(time index).
+        centers = data[0,:]
+
+        nframes = data.shape[0]
+        
+        angular_boolean = (np.array(cv_config["angular_mask"], dtype=int) == 1)
+        init_angle = data[0][angular_boolean]
+        for ii in range(1, nframes):
+            current_angle = data[ii][angular_boolean]
+            angular_diff = current_angle - init_angle
+            current_angle[angular_diff < -np.pi] += 2 * np.pi
+            current_angle[angular_diff >= np.pi] -= 2 * np.pi
+            data[ii][angular_boolean] = current_angle
+
+        start_f = int(nframes * (1-tail))
+        avgins = np.average(data[start_f:, :], axis=0)
+        
+        mf_avg_list = []
+        for simu_frames in range(int(nframes*0.1), nframes,1):
+            start_f = int(simu_frames*(1-0.9))
+            avgins_ = np.average(data[start_f:simu_frames, :], axis=0)
+            diff_ = avgins_ - centers
+            angular_diff_ = diff_[angular_boolean]
+            angular_diff_[angular_diff_ < -np.pi] += 2 * np.pi
+            angular_diff_[angular_diff_ >  np.pi] -= 2 * np.pi
+            diff_[angular_boolean] = angular_diff_
+            ff_ = np.multiply(label_config["kappas"], diff_)
+            mf_avg_list.append(ff_)
+
+        mf_avg_list = np.array(mf_avg_list)
+        mid_f = int(nframes * 0.5)
+        mf_std = np.std(mf_avg_list[mid_f:,:], axis=0)
+        
+        diff = avgins - centers
+        angular_diff = diff[angular_boolean]
+        angular_diff[angular_diff < -np.pi] += 2 * np.pi
+        angular_diff[angular_diff >  np.pi] -= 2 * np.pi
+        diff[angular_boolean] = angular_diff
+        ff = np.multiply(label_config["kappas"], diff)
+        
+        task_path = Path(task_name)
+        task_path.mkdir(exist_ok=True, parents=True)
+        cv_forces = np.concatenate((centers, ff))
+        np.savetxt(task_path.joinpath(cv_force_out),  np.reshape(cv_forces, [1, -1]), fmt='%.10f')
+        plot_mf_average_all(mf_avg_list, task_path, dt = label_config["dt"]*label_config["output_freq"])
+        
+        with open(task_path.joinpath("mf_info.out"),"w") as f:
+            f.write("cv list value      ")
+            for cv_ in centers:
+                f.write("%.4f "%cv_)
+            f.write("\n")
+            f.write("mean force value   ")
+            for mf_ in ff:
+                f.write("%.4f "%mf_)
+            f.write("\n")
+            f.write("mean force std     ")
+            for mf_std_ in mf_std:
+                f.write("%.4f "%mf_std_)
+                
+    elif label_config["method"] == "constrained":
+        data = load_txt(plm_out)
+        data = data[:, 1:]  # removr the first column(time index).
+        centers = data[0,:]
+        
+        # Kb to KJ/mol
+        KB = kb*f_cvt
+        T = float(label_config["temperature"])
+        coords = np.loadtxt(frame_coords,comments=["#","@"])
+        
+        if "units" in cv_config:
+            length_units = cv_config["units"]
+        else:
+            length_units = None
+        if length_units == None or length_units == "nm":
+            # coords units nm
+            coords = coords[:,1:]
+            forces = np.loadtxt(frame_forces,comments=["#","@"])
+            # forces units to KJ/(mol*nm)
+            forces = forces[:,1:]
+        elif length_units == "A" or length_units == "Angstrom":
+            # coords units nm
+            coords = coords[:,1:]*10
+            forces = np.loadtxt(frame_forces,comments=["#","@"])
+            # forces units to KJ/(mol*nm)
+            forces = forces[:,1:]/10
+        else:
+            raise ValueError("Valid length units must be nm or A")
+        
+        mflist = []
+        mflist_phase = []
+        phase_list = []
+        mf_average_phase = []
+        selected_atomid = cv_config["selected_atomid"]
+        selected_atoms = list(set([item for sublist in selected_atomid for item in sublist]))
+        selected_atomid_simple = []
+        for atom_pairs in selected_atomid:
+            selected_atomid_simple.append([selected_atoms.index(i) for i in atom_pairs])
+            
+        # calculate mass matrix of the system
+        system = pmd.load_file(os.path.abspath(topology))
+        mass_list = [system.atoms[i].mass for i in range(len(system.atoms))]
+        mass_list_simple = []
+        for atom_id in selected_atoms:
+            atom_id -= 1
+            mass_list_simple.append(mass_list[atom_id])
+            mass_list_simple.append(mass_list[atom_id])
+            mass_list_simple.append(mass_list[atom_id])
+        
+        # calculate mean force via singular value decomposition(SVD)
+        eps = 0.0001
+        for index in range(np.shape(coords)[0]):
+            coordlist = coords[index]
+            forcelist = forces[index]
+            r_cv = []
+            f_cv = []
+            for atom_id in selected_atoms:
+                atom_id -= 1
+                r_cv.append(coordlist[atom_id*3])
+                r_cv.append(coordlist[atom_id*3+1])
+                r_cv.append(coordlist[atom_id*3+2])
+                f_cv.append(forcelist[atom_id*3])
+                f_cv.append(forcelist[atom_id*3+1])
+                f_cv.append(forcelist[atom_id*3+2])
+            B = pseudo_inv(r_cv, centers, selected_atomid_simple)
+            # print(B.shape)
+            dBdx = []
+            for index in range(len(r_cv)):
+                r1 = r_cv.copy()
+                r1[index] += eps
+                B1 = pseudo_inv(r1,centers,selected_atomid_simple)
+                r2 = r_cv.copy()
+                r2[index] -= eps
+                B2 = pseudo_inv(r2, centers,selected_atomid_simple)
+                dBdx.append((B1-B2)/(2*eps))
+            dBdx = np.array(dBdx)
+            phase = phase_factor(r_cv, centers, selected_atomid_simple, mass_list_simple)
+            mf = np.matmul(B,f_cv) + KB*T*np.trace(dBdx, axis1=0, axis2=2)
+            
+            phase_list.append(1/(phase**0.5))
+            mflist.append(mf)
+            mflist_phase.append(mf/(phase**0.5))
+        
+        mflist = np.array(mflist)
+        mflist_phase = np.array(mflist_phase)
+        nframes = np.shape(coords)[0]            
+        
+        start_f = int(nframes * (1-tail))
+        mid_f = int(nframes * 0.5)
+        mf_avg_without_norm = np.average(mflist_phase[start_f:, :], axis=0)
+        phase_avg = np.average(phase_list[start_f:])
+        mf_avg_without_phase = np.average(mflist[start_f:, :], axis=0)
+        avg_force = mf_avg_without_norm/phase_avg
+        
+        for simu_frames in range(int(nframes*0.1), nframes,1):
+            start_f = int(simu_frames*(1-0.9))
+            mf_average_phase.append(np.average(mflist_phase[start_f:simu_frames, :], axis=0)/np.average(phase_list[start_f:simu_frames]))
+        
+        # calculate mean force statistics
+        mf_average_phase = np.array(mf_average_phase)
+        mf_difference = avg_force - mf_avg_without_phase
+        mf_std = np.std(mf_average_phase[mid_f:,:], axis=0)
+        
+        task_path = Path(task_name)
+        task_path.mkdir(exist_ok=True, parents=True)
+        cv_forces = np.concatenate((centers, avg_force))
+        np.savetxt(task_path.joinpath(cv_force_out),  np.reshape(cv_forces, [1, -1]), fmt='%.10f')
+        plot_mf_average_all(mf_average_phase, task_path, dt = label_config["dt"]*label_config["output_freq"])
+        
+        with open(task_path.joinpath("mf_info.out"),"w") as f:
+            f.write("cv list value      ")
+            for cv_ in centers:
+                f.write("%.4f "%cv_)
+            f.write("\n")
+            f.write("mean force value   ")
+            for mf_ in avg_force:
+                f.write("%.4f "%mf_)
+            f.write("\n")
+            f.write("diff with phase    ")
+            for mf_diff_ in mf_difference:
+                f.write("%.4f "%mf_diff_)
+            f.write("\n")
+            f.write("mean force std     ")
+            for mf_std_ in mf_std:
+                f.write("%.4f "%mf_std_)
+        
+    mf_info = None
+    if os.path.exists(task_path.joinpath("mf_info.out")):
+        mf_info = task_path.joinpath("mf_info.out")
+            
+    op_out = {
+                "cv_forces": task_path.joinpath(cv_force_out),
+                "mf_info": mf_info
+            }
+
+    return op_out
\ No newline at end of file
diff --git a/rid/utils/set_config.py b/rid/utils/set_config.py
index be773f56..3855c111 100644
--- a/rid/utils/set_config.py
+++ b/rid/utils/set_config.py
@@ -2,6 +2,7 @@
 from dflow.plugins.lebesgue import LebesgueExecutor
 from dflow.plugins.dispatcher import DispatcherExecutor
 from dflow import SlurmRemoteExecutor
+import os
 
 
 def init_executor(


Test patch:
diff --git a/tests/data/000/grompp.mdp b/tests/data/000/grompp.mdp
deleted file mode 100644
index 4ff65426..00000000
--- a/tests/data/000/grompp.mdp
+++ /dev/null
@@ -1,88 +0,0 @@
-DispCorr 	= EnerPres
-Shake-SOR 	= no
-comm-grps 	= 
-comm-mode 	= Linear
-compressibility 	= 4.5e-5
-constraint-algorithm 	= Lincs
-constraints 	= all-bonds
-continuation 	= no
-coulombtype 	= pme
-cutoff-scheme 	= Verlet
-define 	= 
-dt 	= 0.002
-energygrp-excl 	= 
-energygrp-table 	= 
-energygrps 	= 
-epsilon-r 	= 1
-epsilon-rf 	= 80
-epsilon-surface 	= 0
-ewald-geometry 	= 3d
-ewald-rtol 	= 1e-05
-fourier-nx 	= 0
-fourier-ny 	= 0
-fourier-nz 	= 0
-fourierspacing 	= 0.12
-gen-seed 	= 173529
-gen-temp 	= 300
-gen-vel 	= no
-include 	= 
-init-step 	= 0
-integrator 	= md
-lincs-iter 	= 1
-lincs-order 	= 4
-lincs-warnangle 	= 30
-morse 	= no
-ns-type 	= Grid
-nstcalcenergy 	= 10000
-nstcomm 	= 1000
-nstenergy 	= 1
-nsteps 	= 50
-nstfout 	= 1
-nstlist 	= 10
-nstlog 	= 0
-nstvout 	= 1
-nstxout 	= 1
-nstxtcout 	= 1
-nwall 	= 0
-optimize-fft 	= no
-pbc 	= xyz
-pcoupl 	= parrinello-rahman
-pcoupltype 	= Isotropic
-periodic-molecules 	= no
-pme-order 	= 4
-pull 	= no
-rcoulomb 	= 0.9
-ref-p 	= 1.0
-ref-t 	= 300 300
-refcoord-scaling 	= No
-rlist 	= 1
-rotation 	= no
-rvdw 	= 0.9
-shake-tol 	= 0.0001
-simulation-part 	= 1
-table-extension 	= 1
-tau-p 	= 1.5
-tau-t 	= 0.2 0.2
-tc-grps 	= water non-water
-tcoupl 	= v-rescale
-tinit 	= 0
-user1-grps 	= 
-user2-grps 	= 
-userint1 	= 0
-userint2 	= 0
-userint3 	= 0
-userint4 	= 0
-userreal1 	= 0
-userreal2 	= 0
-userreal3 	= 0
-userreal4 	= 0
-vdw-modifier 	= Potential-shift-Verlet
-vdw-type 	= Cut-off
-verlet-buffer-drift 	= -1
-wall-atomtype 	= 
-wall-density 	= 
-wall-ewald-zfac 	= 3
-wall-r-linpot 	= -1
-wall-type 	= 9-3
-xtc-grps 	= 
-xtc-precision 	= 1000
\ No newline at end of file
diff --git a/tests/data/000/plumed.dat b/tests/data/000/plumed.dat
deleted file mode 100644
index a5e029a6..00000000
--- a/tests/data/000/plumed.dat
+++ /dev/null
@@ -1,4 +0,0 @@
-dih-002-00: TORSION ATOMS=5,7,9,15
-dih-002-01: TORSION ATOMS=7,9,15,17
-dpfe: DEEPFE TRUST_LVL_1=2.0 TRUST_LVL_2=3.0 MODEL=data/model_000.pb,data/model_001.pb,data/model_002.pb ARG=dih-002-00,dih-002-01
-PRINT STRIDE=1 ARG=dih-002-00,dih-002-01 FILE=plm.out
\ No newline at end of file
diff --git a/tests/data/000/topol.top b/tests/data/000/topol.top
index 0e201f0d..e7c7fc2e 100644
--- a/tests/data/000/topol.top
+++ b/tests/data/000/topol.top
@@ -210,6 +210,8 @@ Protein             3
     9    17    15    16     4 
    15    19    17    18     4 
 
+[ constraints ]
+
 ; Include Position restraint file
 #ifdef POSRES
 #include "posre.itp"
diff --git a/tests/data/centers.out b/tests/data/centers.out
old mode 100755
new mode 100644
diff --git a/tests/data/cv_forces.out b/tests/data/cv_forces.out
new file mode 100755
index 00000000..6eaf08f5
--- /dev/null
+++ b/tests/data/cv_forces.out
@@ -0,0 +1,1 @@
+-1.579864 2.829999 6.6811884701e+00 -7.2972172949e+00
\ No newline at end of file
diff --git a/tests/data/forces.out b/tests/data/forces.out
deleted file mode 100755
index 2339e6a4..00000000
--- a/tests/data/forces.out
+++ /dev/null
@@ -1,1 +0,0 @@
-6.6811884701e+00 -7.2972172949e+00
\ No newline at end of file
diff --git a/tests/data/plm.out b/tests/data/plm.out
index 76ff2420..5bf6ec02 100755
--- a/tests/data/plm.out
+++ b/tests/data/plm.out
@@ -1,506 +1,106 @@
-#! FIELDS time dih-001-00 dih-001-01
-#! SET min_dih-001-00 -pi
-#! SET max_dih-001-00 pi
-#! SET min_dih-001-01 -pi
-#! SET max_dih-001-01 pi
- 0.000000 -1.573703 2.834138
- 0.100000 -1.602249 2.757467
- 0.200000 -1.575558 2.731321
- 0.300000 -1.469029 2.818376
- 0.400000 -1.580683 2.907789
- 0.500000 -1.471608 2.808753
- 0.600000 -1.581004 2.852557
- 0.700000 -1.610640 2.858022
- 0.800000 -1.644463 2.789792
- 0.900000 -1.581272 2.921269
- 1.000000 -1.543793 2.758632
- 1.100000 -1.560677 2.975571
- 1.200000 -1.672983 2.888138
- 1.300000 -1.501856 2.842334
- 1.400000 -1.713061 2.781256
- 1.500000 -1.575902 2.841180
- 1.600000 -1.475226 2.880840
- 1.700000 -1.474805 2.817046
- 1.800000 -1.704228 2.786048
- 1.900000 -1.593912 2.806487
- 2.000000 -1.502672 2.801456
- 2.100000 -1.547970 2.929669
- 2.200000 -1.507121 2.785697
- 2.300000 -1.675012 2.809972
- 2.400000 -1.740008 2.908716
- 2.500000 -1.609971 2.693344
- 2.600000 -1.650406 2.888315
- 2.700000 -1.571238 2.813369
- 2.800000 -1.605310 2.789668
- 2.900000 -1.451887 2.675998
- 3.000000 -1.610442 2.737293
- 3.100000 -1.576544 2.753690
- 3.200000 -1.635018 2.616414
- 3.300000 -1.529538 2.776685
- 3.400000 -1.452691 2.968223
- 3.500000 -1.612495 2.802194
- 3.600000 -1.646799 2.820691
- 3.700000 -1.634317 2.917648
- 3.800000 -1.652177 2.836451
- 3.900000 -1.625077 2.808048
- 4.000000 -1.511919 2.781294
- 4.100000 -1.635063 2.828927
- 4.200000 -1.560932 2.854641
- 4.300000 -1.583035 2.678219
- 4.400000 -1.492723 2.921949
- 4.500000 -1.633663 2.751753
- 4.600000 -1.643949 2.909244
- 4.700000 -1.645778 2.673680
- 4.800000 -1.513049 2.825840
- 4.900000 -1.626768 2.941461
- 5.000000 -1.600651 2.718165
- 5.100000 -1.487175 2.884238
- 5.200000 -1.586143 2.767543
- 5.300000 -1.486750 2.910811
- 5.400000 -1.453000 2.726897
- 5.500000 -1.679085 2.799095
- 5.600000 -1.558839 2.808033
- 5.700000 -1.594526 2.960171
- 5.800000 -1.466782 2.785500
- 5.900000 -1.647956 2.782872
- 6.000000 -1.556968 2.871406
- 6.100000 -1.519409 2.856792
- 6.200000 -1.530586 2.694329
- 6.300000 -1.535691 2.942227
- 6.400000 -1.630501 2.764578
- 6.500000 -1.534021 2.884438
- 6.600000 -1.603215 2.801898
- 6.700000 -1.524566 2.778475
- 6.800000 -1.465336 2.835800
- 6.900000 -1.558327 2.791077
- 7.000000 -1.543969 2.859471
- 7.100000 -1.565408 2.729555
- 7.200000 -1.778848 2.804227
- 7.300000 -1.439269 2.770892
- 7.400000 -1.544403 2.827725
- 7.500000 -1.571556 2.710275
- 7.600000 -1.539700 2.807113
- 7.700000 -1.628896 2.706032
- 7.800000 -1.578800 2.838041
- 7.900000 -1.589944 2.669310
- 8.000000 -1.467004 2.818754
- 8.100000 -1.502541 2.811408
- 8.200000 -1.660325 2.820573
- 8.300000 -1.599892 2.681824
- 8.400000 -1.511240 2.906761
- 8.500000 -1.526599 2.672652
- 8.600000 -1.529190 2.876512
- 8.700000 -1.654565 2.713328
- 8.800000 -1.584006 2.823973
- 8.900000 -1.564551 2.620555
- 9.000000 -1.657775 2.898007
- 9.100000 -1.424989 2.848100
- 9.200000 -1.562984 2.816094
- 9.300000 -1.602063 2.847070
- 9.400000 -1.516650 2.940790
- 9.500000 -1.612912 2.835714
- 9.600000 -1.437892 2.803566
- 9.700000 -1.568913 2.762401
- 9.800000 -1.593609 2.835260
- 9.900000 -1.719039 2.830004
- 10.000000 -1.523508 2.848110
- 10.100000 -1.469559 2.839951
- 10.200000 -1.513146 2.878548
- 10.300000 -1.627307 2.883383
- 10.400000 -1.607746 2.811524
- 10.500000 -1.608169 2.789265
- 10.600000 -1.471105 2.745307
- 10.700000 -1.449025 2.741206
- 10.800000 -1.589087 2.742321
- 10.900000 -1.550275 2.820952
- 11.000000 -1.477771 2.848302
- 11.100000 -1.700847 2.883283
- 11.200000 -1.627458 2.755752
- 11.300000 -1.579057 2.744470
- 11.400000 -1.419974 2.726188
- 11.500000 -1.588100 2.867149
- 11.600000 -1.597658 2.882397
- 11.700000 -1.483236 2.751081
- 11.800000 -1.644828 2.855379
- 11.900000 -1.568490 2.740549
- 12.000000 -1.471865 2.915553
- 12.100000 -1.535369 2.807024
- 12.200000 -1.613207 2.739459
- 12.300000 -1.530406 2.740145
- 12.400000 -1.664998 2.959291
- 12.500000 -1.451895 2.732268
- 12.600000 -1.668052 2.780818
- 12.700000 -1.664139 2.766634
- 12.800000 -1.544531 2.886342
- 12.900000 -1.418209 2.635978
- 13.000000 -1.583926 2.795760
- 13.100000 -1.617074 2.735887
- 13.200000 -1.574768 2.852899
- 13.300000 -1.655733 2.756883
- 13.400000 -1.529511 2.798617
- 13.500000 -1.612987 2.863980
- 13.600000 -1.528888 2.796087
- 13.700000 -1.553111 2.788551
- 13.800000 -1.514431 2.774510
- 13.900000 -1.447699 2.830221
- 14.000000 -1.427783 2.825930
- 14.100000 -1.502457 2.754611
- 14.200000 -1.594403 2.848558
- 14.300000 -1.603351 2.674178
- 14.400000 -1.529411 2.821027
- 14.500000 -1.626395 2.931288
- 14.600000 -1.599199 2.650718
- 14.700000 -1.463144 2.829574
- 14.800000 -1.597401 2.728386
- 14.900000 -1.600859 2.831551
- 15.000000 -1.666939 2.764989
- 15.100000 -1.591385 2.795414
- 15.200000 -1.613630 2.645281
- 15.300000 -1.581775 2.916727
- 15.400000 -1.484336 2.788366
- 15.500000 -1.589601 2.826434
- 15.600000 -1.551432 2.897994
- 15.700000 -1.604313 2.789529
- 15.800000 -1.600324 2.851402
- 15.900000 -1.531078 2.787330
- 16.000000 -1.614857 2.790882
- 16.100000 -1.538903 2.931378
- 16.200000 -1.565955 2.879159
- 16.300000 -1.649747 2.858331
- 16.400000 -1.488173 2.636288
- 16.500000 -1.490197 2.877071
- 16.600000 -1.463983 2.648273
- 16.700000 -1.626078 2.808914
- 16.800000 -1.545404 2.899494
- 16.900000 -1.609862 2.804397
- 17.000000 -1.512401 2.773274
- 17.100000 -1.553561 2.762966
- 17.200000 -1.565637 2.859572
- 17.300000 -1.541917 2.785034
- 17.400000 -1.574801 2.819289
- 17.500000 -1.542756 2.773380
- 17.600000 -1.491923 2.780973
- 17.700000 -1.529264 2.780125
- 17.800000 -1.584408 2.829460
- 17.900000 -1.517723 2.696753
- 18.000000 -1.618454 2.884489
- 18.100000 -1.485475 2.854651
- 18.200000 -1.672112 2.786463
- 18.300000 -1.509055 2.855143
- 18.400000 -1.662358 2.702630
- 18.500000 -1.613747 2.844103
- 18.600000 -1.544165 2.773676
- 18.700000 -1.503034 2.915597
- 18.800000 -1.522774 2.797733
- 18.900000 -1.611329 2.839961
- 19.000000 -1.669440 2.717207
- 19.100000 -1.534092 2.887680
- 19.200000 -1.551041 2.829705
- 19.300000 -1.451373 2.821281
- 19.400000 -1.630904 2.725797
- 19.500000 -1.597301 2.747030
- 19.600000 -1.616791 2.805009
- 19.700000 -1.590495 2.881226
- 19.800000 -1.455209 2.741880
- 19.900000 -1.590033 2.838655
- 20.000000 -1.685538 2.745727
- 20.100000 -1.634451 2.791200
- 20.200000 -1.538699 2.820373
- 20.300000 -1.534363 2.911418
- 20.400000 -1.531734 2.718602
- 20.500000 -1.747959 2.859091
- 20.600000 -1.580019 2.678844
- 20.700000 -1.596637 2.882269
- 20.800000 -1.539423 2.752524
- 20.900000 -1.571265 2.841365
- 21.000000 -1.493317 2.707542
- 21.100000 -1.703774 2.918298
- 21.200000 -1.460211 2.710806
- 21.300000 -1.651898 2.893928
- 21.400000 -1.547608 2.720961
- 21.500000 -1.635600 2.796382
- 21.600000 -1.501134 2.764414
- 21.700000 -1.637578 2.889496
- 21.800000 -1.532893 2.836505
- 21.900000 -1.519577 2.815743
- 22.000000 -1.519788 2.759939
- 22.100000 -1.677135 2.752440
- 22.200000 -1.444130 2.767146
- 22.300000 -1.533895 2.754642
- 22.400000 -1.543449 2.890191
- 22.500000 -1.661427 2.697611
- 22.600000 -1.522332 2.852241
- 22.700000 -1.569910 2.811987
- 22.800000 -1.533115 2.838939
- 22.900000 -1.581930 2.782678
- 23.000000 -1.597383 2.805362
- 23.100000 -1.527909 2.790643
- 23.200000 -1.668889 2.893103
- 23.300000 -1.505693 2.735497
- 23.400000 -1.679513 2.808523
- 23.500000 -1.678435 2.763790
- 23.600000 -1.428443 2.877349
- 23.700000 -1.499363 2.844531
- 23.800000 -1.616670 2.879922
- 23.900000 -1.619305 2.728794
- 24.000000 -1.451703 2.852077
- 24.100000 -1.477109 2.821829
- 24.200000 -1.498872 2.790414
- 24.300000 -1.662103 2.762933
- 24.400000 -1.505380 2.797511
- 24.500000 -1.609625 2.858136
- 24.600000 -1.462592 2.845250
- 24.700000 -1.470172 2.849409
- 24.800000 -1.676391 2.836983
- 24.900000 -1.553530 2.834896
- 25.000000 -1.739379 2.814328
- 25.100000 -1.577085 2.870236
- 25.200000 -1.537247 2.861347
- 25.300000 -1.583380 2.826203
- 25.400000 -1.688733 2.823579
- 25.500000 -1.651222 2.802819
- 25.600000 -1.525477 2.819930
- 25.700000 -1.537845 2.916662
- 25.800000 -1.590091 2.823983
- 25.900000 -1.668777 2.790700
- 26.000000 -1.662459 2.805665
- 26.100000 -1.554040 2.812920
- 26.200000 -1.514604 2.841434
- 26.300000 -1.578965 2.762369
- 26.400000 -1.628648 2.763189
- 26.500000 -1.646773 2.787069
- 26.600000 -1.464287 2.834383
- 26.700000 -1.554960 2.893950
- 26.800000 -1.612205 2.805741
- 26.900000 -1.499576 2.722657
- 27.000000 -1.559992 2.959509
- 27.100000 -1.517560 2.810023
- 27.200000 -1.535447 2.802883
- 27.300000 -1.480957 2.781330
- 27.400000 -1.494498 2.862641
- 27.500000 -1.502521 2.899088
- 27.600000 -1.491039 2.723504
- 27.700000 -1.589809 2.898377
- 27.800000 -1.628913 2.840807
- 27.900000 -1.438767 2.838790
- 28.000000 -1.524884 2.836380
- 28.100000 -1.670055 2.737636
- 28.200000 -1.620384 2.864281
- 28.300000 -1.613606 2.770837
- 28.400000 -1.526301 2.918635
- 28.500000 -1.446847 2.700663
- 28.600000 -1.719648 2.962757
- 28.700000 -1.549540 2.720178
- 28.800000 -1.581585 2.957538
- 28.900000 -1.573865 2.789689
- 29.000000 -1.486683 2.856773
- 29.100000 -1.587458 2.772803
- 29.200000 -1.635177 2.805968
- 29.300000 -1.531046 2.754643
- 29.400000 -1.579833 2.824228
- 29.500000 -1.493670 2.864244
- 29.600000 -1.487798 2.836063
- 29.700000 -1.630963 2.909262
- 29.800000 -1.697741 2.714471
- 29.900000 -1.647941 2.915908
- 30.000000 -1.609084 2.800245
- 30.100000 -1.494464 2.906273
- 30.200000 -1.581397 2.761834
- 30.300000 -1.646716 2.968127
- 30.400000 -1.650801 2.719117
- 30.500000 -1.528284 2.808307
- 30.600000 -1.573897 2.684164
- 30.700000 -1.539914 2.907918
- 30.800000 -1.565888 2.720673
- 30.900000 -1.646852 2.853539
- 31.000000 -1.593919 2.847458
- 31.100000 -1.456500 2.862206
- 31.200000 -1.497995 2.922421
- 31.300000 -1.594450 2.830037
- 31.400000 -1.595432 2.767090
- 31.500000 -1.578031 2.830444
- 31.600000 -1.558855 2.884167
- 31.700000 -1.492191 2.809302
- 31.800000 -1.538135 2.829623
- 31.900000 -1.700944 2.755914
- 32.000000 -1.551834 2.860364
- 32.100000 -1.610838 2.841855
- 32.200000 -1.384318 2.813536
- 32.300000 -1.453166 2.667105
- 32.400000 -1.503002 2.899827
- 32.500000 -1.505139 2.657443
- 32.600000 -1.603725 2.848268
- 32.700000 -1.406648 2.680487
- 32.800000 -1.579080 2.811434
- 32.900000 -1.628699 2.808942
- 33.000000 -1.518143 2.774563
- 33.100000 -1.569150 2.798716
- 33.200000 -1.539972 2.824591
- 33.300000 -1.558633 2.785745
- 33.400000 -1.587166 2.865564
- 33.500000 -1.452189 2.774530
- 33.600000 -1.591318 2.916649
- 33.700000 -1.681671 2.745940
- 33.800000 -1.455868 2.744506
- 33.900000 -1.601624 2.914062
- 34.000000 -1.559403 2.774022
- 34.100000 -1.571780 2.805041
- 34.200000 -1.581602 2.742785
- 34.300000 -1.490863 2.923517
- 34.400000 -1.436873 2.774938
- 34.500000 -1.516780 2.866474
- 34.600000 -1.535147 2.777449
- 34.700000 -1.514106 2.850192
- 34.800000 -1.456297 2.887945
- 34.900000 -1.529572 2.930990
- 35.000000 -1.526470 2.721523
- 35.100000 -1.631861 2.840536
- 35.200000 -1.614421 2.741025
- 35.300000 -1.636720 2.863001
- 35.400000 -1.533146 2.807974
- 35.500000 -1.454679 2.879578
- 35.600000 -1.520955 2.959925
- 35.700000 -1.641715 2.816317
- 35.800000 -1.569634 2.755983
- 35.900000 -1.626369 2.848058
- 36.000000 -1.521068 2.838276
- 36.100000 -1.411983 2.896631
- 36.200000 -1.422626 2.794889
- 36.300000 -1.622135 2.919318
- 36.400000 -1.453759 2.884552
- 36.500000 -1.508282 2.804218
- 36.600000 -1.609927 2.789227
- 36.700000 -1.624261 2.825937
- 36.800000 -1.635657 2.870155
- 36.900000 -1.561395 2.928999
- 37.000000 -1.625264 2.728492
- 37.100000 -1.536049 2.891534
- 37.200000 -1.676885 2.911178
- 37.300000 -1.592408 2.791211
- 37.400000 -1.598967 2.912502
- 37.500000 -1.519509 2.880882
- 37.600000 -1.557280 2.763824
- 37.700000 -1.548938 2.941903
- 37.800000 -1.614000 2.865842
- 37.900000 -1.669183 2.874389
- 38.000000 -1.582634 2.847357
- 38.100000 -1.524368 2.868125
- 38.200000 -1.562766 2.735507
- 38.300000 -1.723015 2.784861
- 38.400000 -1.587219 2.860056
- 38.500000 -1.550019 2.824917
- 38.600000 -1.478190 2.787834
- 38.700000 -1.572283 2.806933
- 38.800000 -1.630416 2.740722
- 38.900000 -1.634439 2.801381
- 39.000000 -1.571912 2.950461
- 39.100000 -1.608500 2.802842
- 39.200000 -1.509944 2.813550
- 39.300000 -1.695352 2.811509
- 39.400000 -1.694087 2.818046
- 39.500000 -1.602180 2.848527
- 39.600000 -1.603539 2.927597
- 39.700000 -1.637163 2.834497
- 39.800000 -1.512233 2.793954
- 39.900000 -1.636292 2.756501
- 40.000000 -1.520927 2.823887
- 40.100000 -1.515823 2.831025
- 40.200000 -1.532691 2.679594
- 40.300000 -1.603997 2.716941
- 40.400000 -1.527164 2.794208
- 40.500000 -1.619806 2.817828
- 40.600000 -1.519794 2.828913
- 40.700000 -1.617378 2.758753
- 40.800000 -1.751491 2.845289
- 40.900000 -1.479075 2.840134
- 41.000000 -1.469091 2.811982
- 41.100000 -1.560066 2.820354
- 41.200000 -1.593051 2.874742
- 41.300000 -1.623808 2.846032
- 41.400000 -1.634383 2.695611
- 41.500000 -1.561866 2.754850
- 41.600000 -1.480222 2.730917
- 41.700000 -1.624833 2.876494
- 41.800000 -1.582788 2.783620
- 41.900000 -1.645393 2.814684
- 42.000000 -1.546279 2.767785
- 42.100000 -1.484083 2.840316
- 42.200000 -1.553558 2.882969
- 42.300000 -1.603249 2.675725
- 42.400000 -1.552075 2.811006
- 42.500000 -1.594556 2.888674
- 42.600000 -1.549145 2.910637
- 42.700000 -1.537448 2.829652
- 42.800000 -1.649167 2.833494
- 42.900000 -1.599353 2.795984
- 43.000000 -1.462204 2.904084
- 43.100000 -1.475113 2.884129
- 43.200000 -1.535582 2.741463
- 43.300000 -1.594959 2.705771
- 43.400000 -1.667480 2.838558
- 43.500000 -1.558847 2.907960
- 43.600000 -1.485126 2.758747
- 43.700000 -1.525768 2.875152
- 43.800000 -1.521359 2.782060
- 43.900000 -1.557087 2.767371
- 44.000000 -1.658731 2.923562
- 44.100000 -1.568502 2.735643
- 44.200000 -1.577606 2.928489
- 44.300000 -1.606533 2.732112
- 44.400000 -1.461113 2.760110
- 44.500000 -1.589517 3.000216
- 44.600000 -1.629565 2.773675
- 44.700000 -1.589763 2.644432
- 44.800000 -1.692835 2.867334
- 44.900000 -1.465700 2.699215
- 45.000000 -1.534544 2.851475
- 45.100000 -1.563183 2.731231
- 45.200000 -1.562224 2.821542
- 45.300000 -1.524030 2.817562
- 45.400000 -1.678398 2.870397
- 45.500000 -1.500340 2.810857
- 45.600000 -1.518869 2.898415
- 45.700000 -1.643633 2.855149
- 45.800000 -1.581342 2.878110
- 45.900000 -1.632937 2.872456
- 46.000000 -1.459718 2.879704
- 46.100000 -1.487222 2.821096
- 46.200000 -1.616372 2.872559
- 46.300000 -1.715971 2.676994
- 46.400000 -1.634482 2.890704
- 46.500000 -1.556935 2.769133
- 46.600000 -1.555029 2.896059
- 46.700000 -1.503285 2.762898
- 46.800000 -1.558852 2.835841
- 46.900000 -1.705358 2.818415
- 47.000000 -1.528385 2.730547
- 47.100000 -1.553946 2.887803
- 47.200000 -1.557178 2.808177
- 47.300000 -1.651676 2.822568
- 47.400000 -1.623266 2.976755
- 47.500000 -1.533943 2.735053
- 47.600000 -1.501365 2.911821
- 47.700000 -1.557692 2.811228
- 47.800000 -1.641684 2.853582
- 47.900000 -1.450683 2.841214
- 48.000000 -1.649832 2.835453
- 48.100000 -1.542146 2.914830
- 48.200000 -1.692493 2.772623
- 48.300000 -1.553976 2.863882
- 48.400000 -1.561122 2.830849
- 48.500000 -1.585955 2.798304
- 48.600000 -1.446283 2.725394
- 48.700000 -1.647795 2.936292
- 48.800000 -1.591391 2.799398
- 48.900000 -1.590609 2.886796
- 49.000000 -1.506058 2.865969
- 49.100000 -1.602266 2.873414
- 49.200000 -1.662143 2.803013
- 49.300000 -1.558560 2.682490
- 49.400000 -1.563604 2.845476
- 49.500000 -1.597037 2.910009
- 49.600000 -1.555609 2.838402
- 49.700000 -1.648982 2.869384
- 49.800000 -1.595926 2.838330
- 49.900000 -1.583325 2.817529
- 50.000000 -1.617874 2.802800
+#! FIELDS time dpfe.bias dih-002-00 dih-002-01
+#! SET min_dih-002-00 -pi
+#! SET max_dih-002-00 pi
+#! SET min_dih-002-01 -pi
+#! SET max_dih-002-01 pi
+ 0.000000 -0.000000 -2.286903 2.342227
+ 1.000000 0.982336 -2.590318 -2.907458
+ 2.000000 134.408237 -2.535056 2.655377
+ 3.000000 134.301292 -2.586927 2.672677
+ 4.000000 129.382143 -2.281741 -3.130028
+ 5.000000 -0.000000 -1.145238 2.405027
+ 6.000000 -0.000000 -2.211540 2.297145
+ 7.000000 -0.000000 -2.686546 2.317400
+ 8.000000 -0.000000 -2.254887 2.241899
+ 9.000000 129.852843 -1.243895 2.186333
+ 10.000000 -0.000000 -1.541280 1.901745
+ 11.000000 -0.000000 -1.201476 0.878371
+ 12.000000 -0.000000 -1.699005 1.904510
+ 13.000000 -0.000000 -1.952783 1.950998
+ 14.000000 129.313995 -1.268216 2.158864
+ 15.000000 36.409981 -0.926425 2.583049
+ 16.000000 -0.000000 -1.107945 2.344170
+ 17.000000 -0.000000 -1.183574 2.555801
+ 18.000000 -0.000000 -1.407162 2.346067
+ 19.000000 131.456050 -1.955011 2.831180
+ 20.000000 -0.000000 -2.450348 -2.752739
+ 21.000000 125.859574 -2.057225 -2.976054
+ 22.000000 130.624217 -2.440401 -3.131118
+ 23.000000 -0.000000 -2.807278 -2.920934
+ 24.000000 128.607415 -1.925259 3.047273
+ 25.000000 17.850328 -2.294047 -2.924628
+ 26.000000 130.850731 -1.605818 2.971887
+ 27.000000 -0.000000 -1.280928 2.614958
+ 28.000000 -0.000000 -1.420074 -3.042870
+ 29.000000 131.245519 -1.880799 2.865270
+ 30.000000 130.989445 -1.572470 2.960864
+ 31.000000 -0.000000 -1.290116 2.638086
+ 32.000000 127.788497 -0.949581 1.984184
+ 33.000000 -0.000000 -1.157371 1.301549
+ 34.000000 46.394453 -1.485623 2.414401
+ 35.000000 122.199269 -1.600880 1.673731
+ 36.000000 124.958095 -1.327079 1.845997
+ 37.000000 -0.000000 -1.836380 1.296387
+ 38.000000 -0.000000 -2.223072 1.805468
+ 39.000000 -0.000000 -2.274219 2.383922
+ 40.000000 128.921790 -2.456371 -3.025610
+ 41.000000 125.328005 -2.044977 -2.884314
+ 42.000000 -0.000000 -3.012814 2.990465
+ 43.000000 130.058627 -2.342880 -3.137588
+ 44.000000 -0.000000 -2.619330 -2.900628
+ 45.000000 131.784003 -1.528897 2.825369
+ 46.000000 126.932929 -1.979414 -3.089734
+ 47.000000 -0.000000 -1.016392 2.977223
+ 48.000000 -0.000000 -1.159133 2.473249
+ 49.000000 -0.000000 -1.607767 2.196542
+ 50.000000 132.626605 -2.266098 2.458738
+ 51.000000 133.596859 -1.278264 2.408541
+ 52.000000 -0.000000 -0.947888 2.735782
+ 53.000000 41.871923 -1.376922 2.435274
+ 54.000000 130.939249 -1.746861 2.943458
+ 55.000000 70.552652 -2.172646 3.126827
+ 56.000000 125.409325 -2.067458 -2.906529
+ 57.000000 8.360334 -1.555594 2.757736
+ 58.000000 47.273435 -1.580108 2.776616
+ 59.000000 11.044512 -1.056215 2.800800
+ 60.000000 -0.000000 -1.232641 2.595384
+ 61.000000 118.348440 -1.049935 2.638572
+ 62.000000 131.737939 -1.523107 2.848743
+ 63.000000 -0.000000 -1.227236 2.397271
+ 64.000000 122.720267 -2.024364 2.805436
+ 65.000000 82.647664 -2.189336 -3.118565
+ 66.000000 -0.000000 -1.197023 -3.077030
+ 67.000000 74.213777 -1.737268 -2.892846
+ 68.000000 114.370045 -1.484880 2.849848
+ 69.000000 135.941463 -1.088450 2.907810
+ 70.000000 -0.000000 -1.212658 2.849813
+ 71.000000 -0.000000 -1.307455 2.750214
+ 72.000000 94.744955 -1.815530 3.042789
+ 73.000000 131.160621 -1.987659 2.873129
+ 74.000000 128.444999 -2.629429 -3.002745
+ 75.000000 -0.000000 -2.885289 2.760290
+ 76.000000 -0.000000 -1.401661 2.498205
+ 77.000000 -0.000000 -1.195874 2.623903
+ 78.000000 -0.000000 -0.911638 2.363053
+ 79.000000 125.994299 -1.175922 1.848609
+ 80.000000 119.392377 -1.355867 2.190518
+ 81.000000 -0.000000 -1.199027 2.336253
+ 82.000000 128.499390 -1.318017 2.113790
+ 83.000000 129.424476 -1.501563 -3.140138
+ 84.000000 -0.000000 -1.650753 2.590856
+ 85.000000 -0.000000 -0.912059 2.340199
+ 86.000000 -0.000000 -1.710837 1.765805
+ 87.000000 -0.000000 -1.985996 1.982243
+ 88.000000 -0.000000 -2.772878 2.113946
+ 89.000000 -0.000000 -3.079709 2.553589
+ 90.000000 -0.000000 -2.610872 -2.884053
+ 91.000000 12.564401 -1.674190 2.666129
+ 92.000000 134.693442 -0.977544 2.492492
+ 93.000000 -0.000000 -1.252702 2.667236
+ 94.000000 125.980729 -1.908252 -2.958400
+ 95.000000 92.693330 -2.918460 2.889424
+ 96.000000 84.896254 -2.778682 2.675965
+ 97.000000 128.961966 -2.623658 -3.029816
+ 98.000000 -0.000000 -2.355724 2.279281
+ 99.000000 134.609504 -2.487881 2.777789
+ 100.000000 -0.000000 -2.215851 1.900325
\ No newline at end of file
diff --git a/tests/data/plm_label.out b/tests/data/plm_label.out
new file mode 100755
index 00000000..76bfc89f
--- /dev/null
+++ b/tests/data/plm_label.out
@@ -0,0 +1,506 @@
+#! FIELDS time dih-002-00 dih-002-01
+#! SET min_dih-002-00 -pi
+#! SET max_dih-002-00 pi
+#! SET min_dih-002-01 -pi
+#! SET max_dih-002-01 pi
+ 0.000000 -2.520308 2.474894
+ 0.200000 -2.449523 2.496500
+ 0.400000 -2.491978 2.472025
+ 0.600000 -2.531424 2.521245
+ 0.800000 -2.565063 2.420956
+ 1.000000 -2.500983 2.428175
+ 1.200000 -2.546925 2.572675
+ 1.400000 -2.433686 2.399923
+ 1.600000 -2.575176 2.495936
+ 1.800000 -2.528185 2.527008
+ 2.000000 -2.385461 2.564312
+ 2.200000 -2.573349 2.394163
+ 2.400000 -2.585179 2.473414
+ 2.600000 -2.634203 2.538219
+ 2.800000 -2.421998 2.509286
+ 3.000000 -2.472604 2.530176
+ 3.200000 -2.562050 2.592165
+ 3.400000 -2.500236 2.495446
+ 3.600000 -2.420599 2.625561
+ 3.800000 -2.512688 2.463152
+ 4.000000 -2.508848 2.547344
+ 4.200000 -2.582589 2.451976
+ 4.400000 -2.564629 2.524622
+ 4.600000 -2.602923 2.468120
+ 4.800000 -2.428597 2.508784
+ 5.000000 -2.666028 2.559341
+ 5.200000 -2.557802 2.463925
+ 5.400000 -2.681280 2.504329
+ 5.600000 -2.610295 2.527811
+ 5.800000 -2.546731 2.517777
+ 6.000000 -2.571485 2.581510
+ 6.200000 -2.441870 2.540361
+ 6.400000 -2.581844 2.403202
+ 6.600000 -2.526711 2.412685
+ 6.800000 -2.483409 2.398987
+ 7.000000 -2.592601 2.532503
+ 7.200000 -2.542966 2.417112
+ 7.400000 -2.449553 2.553440
+ 7.600000 -2.553756 2.481660
+ 7.800000 -2.475387 2.428549
+ 8.000000 -2.660535 2.534169
+ 8.200000 -2.635066 2.556401
+ 8.400000 -2.625156 2.426612
+ 8.600000 -2.539262 2.509977
+ 8.800000 -2.559617 2.519098
+ 9.000000 -2.535386 2.513589
+ 9.200000 -2.485318 2.495404
+ 9.400000 -2.504985 2.456185
+ 9.600000 -2.446633 2.472999
+ 9.800000 -2.434716 2.456373
+ 10.000000 -2.647595 2.536699
+ 10.200000 -2.503320 2.587777
+ 10.400000 -2.467042 2.485531
+ 10.600000 -2.507282 2.416697
+ 10.800000 -2.501470 2.408104
+ 11.000000 -2.521978 2.559808
+ 11.200000 -2.525561 2.529729
+ 11.400000 -2.481150 2.339976
+ 11.600000 -2.594731 2.506148
+ 11.800000 -2.400389 2.404995
+ 12.000000 -2.509268 2.388743
+ 12.200000 -2.522797 2.608622
+ 12.400000 -2.559740 2.479938
+ 12.600000 -2.478774 2.332319
+ 12.800000 -2.410041 2.412737
+ 13.000000 -2.461653 2.467181
+ 13.200000 -2.490323 2.597123
+ 13.400000 -2.552789 2.568560
+ 13.600000 -2.610134 2.514140
+ 13.800000 -2.541848 2.476964
+ 14.000000 -2.555477 2.452997
+ 14.200000 -2.511183 2.402717
+ 14.400000 -2.516437 2.366274
+ 14.600000 -2.495980 2.429470
+ 14.800000 -2.425002 2.458841
+ 15.000000 -2.608711 2.570876
+ 15.200000 -2.549363 2.594612
+ 15.400000 -2.547726 2.422881
+ 15.600000 -2.494459 2.531350
+ 15.800000 -2.535134 2.441636
+ 16.000000 -2.420130 2.601468
+ 16.200000 -2.455391 2.453033
+ 16.400000 -2.547039 2.501173
+ 16.600000 -2.607673 2.416975
+ 16.800000 -2.480366 2.628921
+ 17.000000 -2.592375 2.502779
+ 17.200000 -2.536104 2.485887
+ 17.400000 -2.478397 2.500442
+ 17.600000 -2.501509 2.512199
+ 17.800000 -2.649378 2.296575
+ 18.000000 -2.559290 2.586326
+ 18.200000 -2.455424 2.487157
+ 18.400000 -2.455211 2.335229
+ 18.600000 -2.497263 2.596066
+ 18.800000 -2.485064 2.466885
+ 19.000000 -2.592997 2.500235
+ 19.200000 -2.411961 2.562376
+ 19.400000 -2.520512 2.491013
+ 19.600000 -2.476196 2.489900
+ 19.800000 -2.534149 2.458614
+ 20.000000 -2.638546 2.379722
+ 20.200000 -2.509149 2.558098
+ 20.400000 -2.516003 2.480365
+ 20.600000 -2.555484 2.599989
+ 20.800000 -2.573269 2.389033
+ 21.000000 -2.424758 2.564351
+ 21.200000 -2.569541 2.444959
+ 21.400000 -2.480320 2.509005
+ 21.600000 -2.508307 2.579564
+ 21.800000 -2.389964 2.503420
+ 22.000000 -2.528830 2.460320
+ 22.200000 -2.574327 2.427278
+ 22.400000 -2.474220 2.527933
+ 22.600000 -2.528025 2.463967
+ 22.800000 -2.576501 2.426552
+ 23.000000 -2.518818 2.436160
+ 23.200000 -2.498152 2.409930
+ 23.400000 -2.480852 2.400423
+ 23.600000 -2.456037 2.503853
+ 23.800000 -2.530907 2.478148
+ 24.000000 -2.594146 2.466582
+ 24.200000 -2.432264 2.493363
+ 24.400000 -2.556607 2.520823
+ 24.600000 -2.475209 2.592347
+ 24.800000 -2.532569 2.458471
+ 25.000000 -2.425267 2.572570
+ 25.200000 -2.582064 2.557774
+ 25.400000 -2.516066 2.462337
+ 25.600000 -2.503853 2.430976
+ 25.800000 -2.389597 2.428435
+ 26.000000 -2.625643 2.451997
+ 26.200000 -2.499082 2.571591
+ 26.400000 -2.431691 2.489592
+ 26.600000 -2.519291 2.457722
+ 26.800000 -2.504365 2.434012
+ 27.000000 -2.422556 2.468164
+ 27.200000 -2.469674 2.498195
+ 27.400000 -2.581069 2.486232
+ 27.600000 -2.484068 2.454979
+ 27.800000 -2.433299 2.410685
+ 28.000000 -2.522977 2.529521
+ 28.200000 -2.555470 2.501008
+ 28.400000 -2.490057 2.395347
+ 28.600000 -2.611632 2.505484
+ 28.800000 -2.553370 2.492443
+ 29.000000 -2.490439 2.556057
+ 29.200000 -2.535852 2.331574
+ 29.400000 -2.445259 2.581298
+ 29.600000 -2.538160 2.400012
+ 29.800000 -2.458055 2.505109
+ 30.000000 -2.596744 2.522258
+ 30.200000 -2.511725 2.616679
+ 30.400000 -2.630018 2.372298
+ 30.600000 -2.433437 2.470730
+ 30.800000 -2.598019 2.450551
+ 31.000000 -2.461970 2.429757
+ 31.200000 -2.645899 2.489946
+ 31.400000 -2.346405 2.539413
+ 31.600000 -2.554013 2.407974
+ 31.800000 -2.651479 2.381904
+ 32.000000 -2.486540 2.489805
+ 32.200000 -2.475388 2.448274
+ 32.400000 -2.500964 2.445397
+ 32.600000 -2.555663 2.565859
+ 32.800000 -2.443348 2.551925
+ 33.000000 -2.473779 2.413775
+ 33.200000 -2.424325 2.493832
+ 33.400000 -2.444153 2.596041
+ 33.600000 -2.455922 2.498516
+ 33.800000 -2.422956 2.563856
+ 34.000000 -2.640257 2.561921
+ 34.200000 -2.417428 2.524110
+ 34.400000 -2.450842 2.489290
+ 34.600000 -2.562962 2.487250
+ 34.800000 -2.540896 2.548659
+ 35.000000 -2.534543 2.552481
+ 35.200000 -2.464884 2.536884
+ 35.400000 -2.565779 2.571570
+ 35.600000 -2.534380 2.564184
+ 35.800000 -2.526925 2.557474
+ 36.000000 -2.496542 2.436355
+ 36.200000 -2.514927 2.537571
+ 36.400000 -2.436736 2.473701
+ 36.600000 -2.597308 2.487848
+ 36.800000 -2.613262 2.512253
+ 37.000000 -2.534111 2.586701
+ 37.200000 -2.597160 2.553549
+ 37.400000 -2.525287 2.542035
+ 37.600000 -2.535779 2.517107
+ 37.800000 -2.718056 2.487201
+ 38.000000 -2.415518 2.435102
+ 38.200000 -2.468926 2.590852
+ 38.400000 -2.523516 2.491453
+ 38.600000 -2.702817 2.483910
+ 38.800000 -2.478362 2.483723
+ 39.000000 -2.583675 2.409544
+ 39.200000 -2.523072 2.471443
+ 39.400000 -2.598996 2.533630
+ 39.600000 -2.536077 2.534618
+ 39.800000 -2.409678 2.500217
+ 40.000000 -2.509982 2.445480
+ 40.200000 -2.596565 2.438768
+ 40.400000 -2.535066 2.563068
+ 40.600000 -2.522413 2.395102
+ 40.800000 -2.413118 2.497584
+ 41.000000 -2.595653 2.502336
+ 41.200000 -2.557740 2.459238
+ 41.400000 -2.567356 2.478502
+ 41.600000 -2.638882 2.425773
+ 41.800000 -2.627689 2.458100
+ 42.000000 -2.544826 2.491358
+ 42.200000 -2.459932 2.557795
+ 42.400000 -2.495761 2.489546
+ 42.600000 -2.519004 2.660732
+ 42.800000 -2.543840 2.573698
+ 43.000000 -2.486299 2.563034
+ 43.200000 -2.635020 2.545511
+ 43.400000 -2.404523 2.560314
+ 43.600000 -2.589502 2.425303
+ 43.800000 -2.454376 2.557856
+ 44.000000 -2.515345 2.428493
+ 44.200000 -2.537039 2.533154
+ 44.400000 -2.527925 2.474731
+ 44.600000 -2.493909 2.496143
+ 44.800000 -2.583586 2.529344
+ 45.000000 -2.432747 2.492897
+ 45.200000 -2.586376 2.536553
+ 45.400000 -2.501672 2.566489
+ 45.600000 -2.413702 2.566177
+ 45.800000 -2.426559 2.599131
+ 46.000000 -2.517639 2.493259
+ 46.200000 -2.520136 2.341433
+ 46.400000 -2.450186 2.549100
+ 46.600000 -2.491867 2.559114
+ 46.800000 -2.453301 2.474550
+ 47.000000 -2.542834 2.503613
+ 47.200000 -2.530757 2.523755
+ 47.400000 -2.546771 2.571044
+ 47.600000 -2.539602 2.573657
+ 47.800000 -2.623560 2.450708
+ 48.000000 -2.427407 2.459725
+ 48.200000 -2.605454 2.524807
+ 48.400000 -2.498223 2.377336
+ 48.600000 -2.578803 2.546993
+ 48.800000 -2.518917 2.458920
+ 49.000000 -2.502014 2.526250
+ 49.200000 -2.635369 2.506098
+ 49.400000 -2.463017 2.508241
+ 49.600000 -2.541665 2.379258
+ 49.800000 -2.359458 2.553145
+ 50.000000 -2.504090 2.489383
+ 50.200000 -2.495326 2.494034
+ 50.400000 -2.323972 2.480320
+ 50.600000 -2.568840 2.533464
+ 50.800000 -2.541589 2.623269
+ 51.000000 -2.637934 2.426697
+ 51.200000 -2.452441 2.432598
+ 51.400000 -2.532614 2.569881
+ 51.600000 -2.398438 2.487815
+ 51.800000 -2.547801 2.364225
+ 52.000000 -2.529925 2.574802
+ 52.200000 -2.624329 2.437697
+ 52.400000 -2.417882 2.550044
+ 52.600000 -2.538616 2.475868
+ 52.800000 -2.481304 2.343525
+ 53.000000 -2.547899 2.570958
+ 53.200000 -2.566637 2.547855
+ 53.400000 -2.479474 2.492309
+ 53.600000 -2.531158 2.412151
+ 53.800000 -2.424862 2.583135
+ 54.000000 -2.553797 2.353103
+ 54.200000 -2.578409 2.515417
+ 54.400000 -2.569049 2.468031
+ 54.600000 -2.621127 2.366239
+ 54.800000 -2.528328 2.401114
+ 55.000000 -2.503723 2.364202
+ 55.200000 -2.586800 2.488148
+ 55.400000 -2.408675 2.555750
+ 55.600000 -2.586134 2.383190
+ 55.800000 -2.496422 2.448538
+ 56.000000 -2.548260 2.405210
+ 56.200000 -2.450793 2.479748
+ 56.400000 -2.643029 2.463698
+ 56.600000 -2.501555 2.522806
+ 56.800000 -2.672773 2.504446
+ 57.000000 -2.471064 2.615882
+ 57.200000 -2.593835 2.571435
+ 57.400000 -2.435776 2.401579
+ 57.600000 -2.503217 2.417688
+ 57.800000 -2.451026 2.503838
+ 58.000000 -2.530115 2.522839
+ 58.200000 -2.495418 2.487806
+ 58.400000 -2.429824 2.447589
+ 58.600000 -2.651151 2.614617
+ 58.800000 -2.499382 2.468836
+ 59.000000 -2.521013 2.506656
+ 59.200000 -2.533780 2.601673
+ 59.400000 -2.553789 2.550515
+ 59.600000 -2.512565 2.443613
+ 59.800000 -2.443032 2.461863
+ 60.000000 -2.574945 2.605157
+ 60.200000 -2.459785 2.502020
+ 60.400000 -2.589749 2.472812
+ 60.600000 -2.503569 2.409473
+ 60.800000 -2.491523 2.541229
+ 61.000000 -2.508350 2.692643
+ 61.200000 -2.452118 2.482099
+ 61.400000 -2.451597 2.490957
+ 61.600000 -2.427460 2.366398
+ 61.800000 -2.491401 2.385885
+ 62.000000 -2.544026 2.513068
+ 62.200000 -2.452408 2.524479
+ 62.400000 -2.508645 2.481546
+ 62.600000 -2.504052 2.382806
+ 62.800000 -2.445728 2.428322
+ 63.000000 -2.414383 2.600672
+ 63.200000 -2.539432 2.405840
+ 63.400000 -2.538475 2.519588
+ 63.600000 -2.601527 2.480703
+ 63.800000 -2.527498 2.386972
+ 64.000000 -2.524543 2.518608
+ 64.200000 -2.559263 2.448758
+ 64.400000 -2.466613 2.483041
+ 64.600000 -2.579228 2.617576
+ 64.800000 -2.552529 2.406848
+ 65.000000 -2.541134 2.504060
+ 65.200000 -2.390999 2.363885
+ 65.400000 -2.486400 2.551708
+ 65.600000 -2.493604 2.380114
+ 65.800000 -2.566244 2.540605
+ 66.000000 -2.395659 2.553127
+ 66.200000 -2.483712 2.463379
+ 66.400000 -2.580097 2.511250
+ 66.600000 -2.472281 2.489005
+ 66.800000 -2.351386 2.516035
+ 67.000000 -2.527481 2.378646
+ 67.200000 -2.511783 2.425169
+ 67.400000 -2.449980 2.540157
+ 67.600000 -2.426645 2.539074
+ 67.800000 -2.608536 2.474057
+ 68.000000 -2.501379 2.413642
+ 68.200000 -2.595784 2.444199
+ 68.400000 -2.544809 2.509968
+ 68.600000 -2.540850 2.563933
+ 68.800000 -2.511254 2.579467
+ 69.000000 -2.668435 2.498607
+ 69.200000 -2.357782 2.482879
+ 69.400000 -2.327262 2.452030
+ 69.600000 -2.403577 2.425458
+ 69.800000 -2.514081 2.406967
+ 70.000000 -2.553401 2.481287
+ 70.200000 -2.553139 2.427406
+ 70.400000 -2.598814 2.466747
+ 70.600000 -2.566838 2.491719
+ 70.800000 -2.487315 2.525437
+ 71.000000 -2.554439 2.461700
+ 71.200000 -2.548031 2.693936
+ 71.400000 -2.580999 2.439385
+ 71.600000 -2.539676 2.382413
+ 71.800000 -2.398844 2.486998
+ 72.000000 -2.473562 2.416675
+ 72.200000 -2.514546 2.517968
+ 72.400000 -2.638830 2.496940
+ 72.600000 -2.507032 2.555991
+ 72.800000 -2.449345 2.415784
+ 73.000000 -2.550466 2.483894
+ 73.200000 -2.523314 2.474712
+ 73.400000 -2.378636 2.448424
+ 73.600000 -2.489525 2.467977
+ 73.800000 -2.527275 2.437445
+ 74.000000 -2.580020 2.512751
+ 74.200000 -2.585921 2.516939
+ 74.400000 -2.477065 2.407242
+ 74.600000 -2.572860 2.539971
+ 74.800000 -2.437181 2.409566
+ 75.000000 -2.494124 2.539577
+ 75.200000 -2.487601 2.575615
+ 75.400000 -2.558226 2.469158
+ 75.600000 -2.511583 2.465072
+ 75.800000 -2.475516 2.619267
+ 76.000000 -2.549147 2.545152
+ 76.200000 -2.557620 2.582978
+ 76.400000 -2.375164 2.548415
+ 76.600000 -2.492602 2.537967
+ 76.800000 -2.575757 2.549148
+ 77.000000 -2.548793 2.466091
+ 77.200000 -2.604439 2.408836
+ 77.400000 -2.524660 2.526850
+ 77.600000 -2.543410 2.533508
+ 77.800000 -2.471684 2.509951
+ 78.000000 -2.425044 2.469919
+ 78.200000 -2.467312 2.509085
+ 78.400000 -2.531606 2.295124
+ 78.600000 -2.498039 2.343988
+ 78.800000 -2.516459 2.464452
+ 79.000000 -2.466881 2.502722
+ 79.200000 -2.503962 2.510981
+ 79.400000 -2.570539 2.582055
+ 79.600000 -2.496869 2.448443
+ 79.800000 -2.524392 2.448737
+ 80.000000 -2.543102 2.477633
+ 80.200000 -2.498349 2.427797
+ 80.400000 -2.696493 2.414255
+ 80.600000 -2.588736 2.501554
+ 80.800000 -2.461325 2.423744
+ 81.000000 -2.545143 2.466897
+ 81.200000 -2.475346 2.548122
+ 81.400000 -2.669361 2.616087
+ 81.600000 -2.540229 2.611007
+ 81.800000 -2.542316 2.530424
+ 82.000000 -2.499939 2.515778
+ 82.200000 -2.562060 2.431026
+ 82.400000 -2.423095 2.488014
+ 82.600000 -2.543734 2.550079
+ 82.800000 -2.586785 2.579953
+ 83.000000 -2.513859 2.505708
+ 83.200000 -2.593621 2.524464
+ 83.400000 -2.492111 2.477765
+ 83.600000 -2.540240 2.485905
+ 83.800000 -2.427511 2.439224
+ 84.000000 -2.517701 2.599205
+ 84.200000 -2.581980 2.324935
+ 84.400000 -2.519853 2.489273
+ 84.600000 -2.451259 2.505853
+ 84.800000 -2.501446 2.416351
+ 85.000000 -2.416191 2.474459
+ 85.200000 -2.578123 2.592692
+ 85.400000 -2.632820 2.706938
+ 85.600000 -2.568389 2.483208
+ 85.800000 -2.510552 2.677651
+ 86.000000 -2.518367 2.456487
+ 86.200000 -2.521810 2.580019
+ 86.400000 -2.474891 2.502352
+ 86.600000 -2.454301 2.425272
+ 86.800000 -2.519223 2.598397
+ 87.000000 -2.499303 2.740362
+ 87.200000 -2.557038 2.526169
+ 87.400000 -2.475822 2.447415
+ 87.600000 -2.523135 2.509718
+ 87.800000 -2.506515 2.405144
+ 88.000000 -2.432554 2.587707
+ 88.200000 -2.450915 2.505536
+ 88.400000 -2.542099 2.570119
+ 88.600000 -2.568750 2.501847
+ 88.800000 -2.580448 2.548072
+ 89.000000 -2.479350 2.563310
+ 89.200000 -2.599166 2.497555
+ 89.400000 -2.518214 2.570899
+ 89.600000 -2.546900 2.412099
+ 89.800000 -2.412618 2.495835
+ 90.000000 -2.545820 2.513728
+ 90.200000 -2.511516 2.579490
+ 90.400000 -2.455431 2.365701
+ 90.600000 -2.497203 2.352631
+ 90.800000 -2.610554 2.619611
+ 91.000000 -2.486657 2.485089
+ 91.200000 -2.576567 2.455879
+ 91.400000 -2.500425 2.644621
+ 91.600000 -2.616473 2.495216
+ 91.800000 -2.436729 2.327637
+ 92.000000 -2.582224 2.462997
+ 92.200000 -2.563527 2.576130
+ 92.400000 -2.470337 2.415062
+ 92.600000 -2.382429 2.430159
+ 92.800000 -2.613328 2.486631
+ 93.000000 -2.560725 2.336265
+ 93.200000 -2.396587 2.463034
+ 93.400000 -2.585525 2.464688
+ 93.600000 -2.489675 2.480063
+ 93.800000 -2.547345 2.355188
+ 94.000000 -2.545950 2.395474
+ 94.200000 -2.541148 2.503845
+ 94.400000 -2.546098 2.697475
+ 94.600000 -2.477261 2.413785
+ 94.800000 -2.592167 2.466423
+ 95.000000 -2.614539 2.518582
+ 95.200000 -2.449296 2.399015
+ 95.400000 -2.506064 2.503861
+ 95.600000 -2.537428 2.614733
+ 95.800000 -2.535505 2.498534
+ 96.000000 -2.551632 2.467332
+ 96.200000 -2.464624 2.510703
+ 96.400000 -2.529434 2.416026
+ 96.600000 -2.419303 2.448537
+ 96.800000 -2.450119 2.433156
+ 97.000000 -2.402399 2.494451
+ 97.200000 -2.573791 2.392381
+ 97.400000 -2.521267 2.523790
+ 97.600000 -2.472893 2.529291
+ 97.800000 -2.542346 2.429490
+ 98.000000 -2.496596 2.497319
+ 98.200000 -2.563209 2.556837
+ 98.400000 -2.500214 2.552481
+ 98.600000 -2.500109 2.453054
+ 98.800000 -2.430181 2.422431
+ 99.000000 -2.485916 2.490850
+ 99.200000 -2.608064 2.511547
+ 99.400000 -2.565028 2.453932
+ 99.600000 -2.458753 2.370716
+ 99.800000 -2.676624 2.482563
+ 100.000000 -2.515375 2.500859
\ No newline at end of file
diff --git a/tests/mocked_ops.py b/tests/mocked_ops.py
index 6821cb56..686edb0c 100755
--- a/tests/mocked_ops.py
+++ b/tests/mocked_ops.py
@@ -23,7 +23,6 @@
 from rid.op.prep_label import CheckLabelInputs
 from rid.op.run_exploration import RunExplore
 from rid.op.run_label import RunLabel
-from rid.op.calc_mf import CalcMF
 from rid.op.prep_select import PrepSelect
 from rid.op.run_select import RunSelect
 from rid.op.run_train import TrainModel
@@ -37,14 +36,18 @@
     plumed_output_name,
     gmx_mdrun_log,
     gmx_xtc_name,
-    force_out,
+    cv_force_out,
+    mf_fig,
+    cluster_fig,
     cluster_selection_data_name,
     cluster_selection_index_name,
     sel_gro_name,
     model_devi_name,
     sel_ndx_name,
     cv_init_label,
-    model_devi_precision
+    model_devi_precision,
+    train_log,
+    train_fig
 )
 
 upload_packages.append(__file__)
@@ -98,15 +101,11 @@ def execute(
             self,
             ip : OPIO,
     ) -> OPIO:
-        with open(ip["forces"][0], "r") as f:
+        with open(ip["cv_forces"][0], "r") as f:
             f1 = f.read()
-        with open(ip["centers"][0], "r") as f:
-            f2 = f.read()
         
         with open(data_new, "w") as f:
             f.write(f1)
-            f.write("\n")
-            f.write(f2)
     
         op = OPIO({
             "data_new" : Path(data_new)
@@ -259,31 +258,18 @@ def execute(
             print("write log")
             with open(gmx_mdrun_log, "w") as f:
                 f.write("gmx md log file")
+            with open(cv_force_out,"w") as f:
+                f.write('1.000000e+00 2.000000e+00\n')
         
         op = OPIO({
                 "plm_out": ip["task_path"].joinpath(plumed_output_name),
-                "md_log": ip["task_path"].joinpath(gmx_mdrun_log)
+                "md_log": ip["task_path"].joinpath(gmx_mdrun_log),
+                "cv_forces": ip["task_path"].joinpath(cv_force_out),
+                "mf_fig": ip["task_path"].joinpath(mf_fig),
+                
             })
         return op
     
-class MockedCalcMF(CalcMF):
-    @OP.exec_sign_check
-    def execute(
-            self,
-            ip : OPIO,
-    ) -> OPIO:
-        task_path = Path(ip["task_name"])
-        task_path.mkdir(exist_ok=True, parents=True)
-        force = np.array([1,2])
-        with set_directory(task_path):
-            save_txt(force_out, force.reshape(1,-1))
-        op_out = OPIO(
-            {
-                "forces": task_path.joinpath(force_out)
-            }
-        )
-        return op_out
-    
 class MockedPrepSelect(PrepSelect):
     @OP.exec_sign_check
     def execute(
@@ -297,12 +283,15 @@ def execute(
         task_path = Path(ip["task_name"])
         task_path.mkdir(exist_ok=True, parents=True)
         with set_directory(task_path):
+            with open(cluster_fig, "w") as f:
+                f.write("this is the cluster fig")
             np.save(cluster_selection_index_name, cls_sel_idx)
             np.save(cluster_selection_data_name, selected_data)
         
         op_out = OPIO({
                 "cluster_threshold": cluster_threshold,
                 "numb_cluster": numb_cluster,
+                "cluster_fig": task_path.joinpath(cluster_fig),
                 "cluster_selection_index": task_path.joinpath(cluster_selection_index_name),
                 "cluster_selection_data": task_path.joinpath(cluster_selection_data_name)
             })
@@ -352,13 +341,25 @@ def execute(
             self,
             ip : OPIO,
     ) -> OPIO:
+        task_path = Path(ip["model_tag"])
+        task_path.mkdir(exist_ok=True, parents=True)
         assert(ip["data"].is_file())
-        tf_model = Path(tf_model_name.format(tag=ip["model_tag"]))
-        tf_model.write_text(f'This is init model {ip["model_tag"]}')
+        with set_directory(task_path):
+            tf_model = tf_model_name.format(tag=ip["model_tag"])
+            with open(tf_model,"w") as f:
+                f.write("this is trained model")
+            train_log_name = train_log.format(tag=ip["model_tag"])
+            with open(train_log_name,"w") as f:
+                f.write("this is log file")
+            train_fig_name = train_fig.format(tag=ip["model_tag"])
+            with open(train_fig_name,"w") as f:
+                f.write("this is train fig")
         
         op_out = OPIO(
             {
-                "model": tf_model
+                "model": task_path.joinpath(tf_model),
+                "train_log": task_path.joinpath(train_log_name),
+                "train_fig": task_path.joinpath(train_fig_name)
             }
         )
         return op_out
diff --git a/tests/op/test_calcmf.py b/tests/op/test_calcmf.py
deleted file mode 100755
index de07a881..00000000
--- a/tests/op/test_calcmf.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import os
-import numpy as np
-import unittest
-from pathlib import Path
-from dflow.python import (
-    OP,
-    OPIO,
-    OPIOSign,
-    Parameter
-    )
-from context import rid
-from rid.op.calc_mf import CalcMF
-from rid.utils import load_txt, save_txt, set_directory
-from pathlib import Path
-import shutil
-from rid.constants import (
-        force_out,
-        plumed_output_name
-    )
-
-class Test_Calcmf(unittest.TestCase):
-    def setUp(self):
-        self.task_name = "force"
-        self.datapath = "data"
-    
-    def tearDown(self):
-        ii = self.task_name
-        ii=Path(ii)
-        if ii.is_dir():
-            shutil.rmtree(ii)
-    
-    def test(self):
-        op = CalcMF()
-        data = Path(self.datapath)
-        op_in = OPIO(
-            {
-                "task_name": "force",
-                "plm_out": data/plumed_output_name,
-                "kappas": [500,500],
-                "at": data/"centers.out",
-                "tail": 0.9,
-                "angular_mask": [1, 1],
-            }
-        )
-        op_out = op.execute(op_in)
-        force1 = [float(i) for i in op_out["forces"].read_text().split(" ")]
-        force2 = [float(i) for i in (data/force_out).read_text().split(" ")]
-        self.assertListEqual(force1, force2)
\ No newline at end of file
diff --git a/tests/op/test_prep_data.py b/tests/op/test_prep_data.py
index 08232ec5..9d445873 100755
--- a/tests/op/test_prep_data.py
+++ b/tests/op/test_prep_data.py
@@ -16,8 +16,7 @@
         data_new,
         data_old,
         data_raw,
-        force_out,
-        center_out_name
+        cv_force_out
     )
 
 
@@ -36,8 +35,7 @@ def test(self):
         data = Path(self.datapath)
         op_in = OPIO(
             {
-                "forces": [data/force_out],
-                "centers": [data/center_out_name]
+                "cv_forces": [data/cv_force_out]
             }
         )
         op_out = op.execute(op_in)
diff --git a/tests/op/test_prep_explore.py b/tests/op/test_prep_explore.py
index bf261aca..9776b224 100644
--- a/tests/op/test_prep_explore.py
+++ b/tests/op/test_prep_explore.py
@@ -49,6 +49,7 @@ def test(self):
                 "exploration_config": gmx_config,
                 "cv_config": cv_config,
                 "task_name": "explored",
+                "block_tag": "iter-001"
             }
         )
         op_in2 = OPIO(
@@ -62,6 +63,7 @@ def test(self):
                 "exploration_config": gmx_config,
                 "cv_config": cv_config,
                 "task_name": "explored",
+                "block_tag":"iter-001"
             }
         )
         op_in3 = OPIO(
@@ -75,6 +77,7 @@ def test(self):
                 "exploration_config": gmx_config,
                 "cv_config": cv_config2,
                 "task_name": "explored",
+                "block_tag":"iter-001"
             }
         )
         op_out1 = op.execute(op_in1)
diff --git a/tests/op/test_prep_label.py b/tests/op/test_prep_label.py
index ff4637cd..8d770548 100644
--- a/tests/op/test_prep_label.py
+++ b/tests/op/test_prep_label.py
@@ -65,20 +65,21 @@ def test(self):
         conf_path = data/"conf.gro"
         top_path = data/"topol.top"
         center_path = data/"centers.out"
-        gmx_config = {"type":"gmx","nsteps": 50, "output_freq": 1, "temperature": 300, 
-                      "dt": 0.002, "output_mode": "both", "ntmpi": 1, "nt": 8, "max_warning": 0}
-        cv_config1 = {"mode": "torsion", "selected_resid": [1, 2], "cv_file": []}
-        cv_config2 = {"mode": "custom", "selected_resid": None, "cv_file": ["data/plumed.dat"]}
+        gmx_config1 = {"type":"gmx","nsteps": 50,"method":"restrained", "output_freq": 1, "temperature": 300, "kappas": [500,500],
+                    "dt": 0.002, "output_mode": "both", "ntmpi": 1, "nt": 8, "max_warning": 0}
+        gmx_config2 = {"type":"gmx","nsteps": 50,"method":"constrained", "output_freq": 1, "temperature": 300, "kappas": [500,500],
+                    "dt": 0.002, "output_mode": "both", "ntmpi": 1, "nt": 8, "max_warning": 0}
+        cv_config1 = {"mode": "torsion", "selected_resid": [1, 2], "angular_mask": [1,1],"cv_file": [""]}
+        cv_config2 = {"mode": "distance", "selected_atomid": [[1,2],[3,4]], "angular_mask": [0,0],"cv_file": [""]}
 
         op_in1 = OPIO(
             {
                 "topology": top_path,
                 "conf": conf_path,
-                "label_config": gmx_config,
+                "label_config": gmx_config1,
                 "cv_file": None,
                 "cv_config": cv_config1,
                 "task_name": self.taskname,
-                "kappas": [500,500],
                 "at": center_path
             }
         )
@@ -86,17 +87,14 @@ def test(self):
             {
                 "topology": top_path,
                 "conf": conf_path,
-                "label_config": gmx_config,
-                "cv_file": [data/"plumed.dat"],
+                "label_config": gmx_config2,
+                "cv_file": None,
                 "cv_config": cv_config2,
-                "task_name": self.taskname,
-                "kappas": [500,500],
-                "at": center_path
+                "task_name": self.taskname
             }
         )
     
         op_out1 = op.execute(op_in1)
-        op_out2 = op.execute(op_in2)
 
         with open(self.taskname+"/"+plumed_input_name, "r") as f:
             while True:
@@ -110,4 +108,20 @@ def test(self):
         c1 = np.loadtxt(center_path)[0]
         c2 = np.loadtxt(center_path)[1]
         self.assertEqual(a1, c1)
+        self.assertEqual(a2, c2)
+        
+        op_out2 = op.execute(op_in2)
+        
+        with open(self.taskname+"/"+plumed_input_name, "r") as f:
+            while True:
+                l1 = f.readline()
+                if l1 is not None:
+                    if l1[0] == 'd':
+                        break
+            l2 = f.readline()
+        a1 = l1.split("=")[-1].strip()
+        a2 = l2.split("=")[-1].strip()
+        c1 = "1,2"
+        c2 = "3,4"
+        self.assertEqual(a1, c1)
         self.assertEqual(a2, c2)
\ No newline at end of file
diff --git a/tests/op/test_run_label.py b/tests/op/test_run_label.py
index e6c3100f..b1bd3c21 100644
--- a/tests/op/test_run_label.py
+++ b/tests/op/test_run_label.py
@@ -28,6 +28,10 @@
 class Test_MockedRunLabel(unittest.TestCase):
     def setUp(self):
         self.taskname = "001"
+        self.datapath = "data"
+        os.mkdir(self.taskname)
+        plm_data = np.loadtxt(Path(self.datapath)/"plm_label.out")
+        np.savetxt(Path(self.taskname)/"plm.out", plm_data)
     
     def tearDown(self):
         ii = Path(self.taskname)
@@ -38,12 +42,16 @@ def tearDown(self):
     def test(self, mocked_run):
         mocked_run.return_value = 0, 0, 1
         op = RunLabel()
-        gmx_config = {"type":"gmx", "nsteps": 50, "output_freq": 1, "temperature": 300, 
-                               "dt": 0.002, "output_mode": "both", "ntmpi": 1, "nt": 8, "max_warning": 0}
+        gmx_config = {"type":"gmx","nsteps": 50,"method":"restrained", "output_freq": 1, "temperature": 300, "kappas": [500,500],
+                      "dt": 0.002, "output_mode": "both", "ntmpi": 1, "nt": 8, "max_warning": 0}
+        cv_config = {"mode": "torsion", "selected_resid": [1, 2],"angular_mask": [ 1, 1 ],"weights": [ 1, 1 ],"cv_file": []}
         op_in1 = OPIO(
             {
                 "task_path": Path(self.taskname),
                 "label_config": gmx_config,
+                "cv_config": cv_config,
+                "task_name": self.taskname,
+                "at": None,
                 "forcefield": None,
                 "index_file": None,
                 "dp_files": None,
diff --git a/tests/op/test_run_train.py b/tests/op/test_run_train.py
index 46d5d73c..9caf4452 100644
--- a/tests/op/test_run_train.py
+++ b/tests/op/test_run_train.py
@@ -10,6 +10,7 @@
 from rid.constants import data_raw
 from pathlib import Path
 from utils import DATA_RAW
+import shutil
 
 class Test_RunTrain(unittest.TestCase):
     def setUp(self):
@@ -18,16 +19,14 @@ def setUp(self):
 
     
     def tearDown(self):
-        os.remove("./model_000.pb")
-        os.remove("./model.ckpt.index")
-        os.remove("./model.ckpt.meta")
-        os.remove("./model.ckpt.data-00000-of-00001")
-        os.remove("./checkpoint")
         os.remove(Path(self.datapath)/data_raw)
+        ii = Path("000")
+        if ii.is_dir():
+            shutil.rmtree(ii)
     
     def test(self):
         op = TrainModel()
-        data = Path(self.datapath)
+        data = Path(os.path.abspath(self.datapath))
         train_config = {"neurons": [20, 20], "resnet": True, "batch_size": 2,
                         "epoches": 200, "init_lr": 0.0008, "decay_steps": 120,
                         "decay_rate": 0.96, "train_thread": 8, "drop_out_rate": 0.3, 
diff --git a/tests/test_data.py b/tests/test_data.py
index 9e4a26f3..0f0a05f1 100755
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -17,7 +17,7 @@
 
 from rid.constants import (
     data_raw,
-    force_out,
+    cv_force_out,
     center_out_name,
     data_old
 )
@@ -101,16 +101,14 @@
 @unittest.skipIf(skip_ut_with_dflow, skip_ut_with_dflow_reason)
 class TestMockedData(unittest.TestCase):
     def setUp(self):
-        self.forces = make_mocked_init_data(force_out,1)
-        self.centers = make_mocked_init_data(center_out_name,1)
+        self.cv_forces = make_mocked_init_data(cv_force_out,1)
         self.data_old = make_mocked_init_data(data_old,1)
         self.block_tag = "000"
         self.data_out = "data_generate"
         self.expected_out = data_raw
 
     def tearDown(self):
-        clear_dir(self.forces[0])
-        clear_dir(self.centers[0])
+        clear_dir(self.cv_forces[0])
         clear_dir(self.data_old[0])
         clear_dir(self.data_out)
             
@@ -128,8 +126,7 @@ def test_data(self):
                 "block_tag" : self.block_tag
             },
             artifacts = {
-            "forces": upload_artifact(self.forces[1]),
-            "centers": upload_artifact(self.centers[1]),
+            "cv_forces": upload_artifact(self.cv_forces[1]),
             "data_old": upload_artifact(self.data_old[1])
             },
         )
@@ -153,6 +150,4 @@ def test_data(self):
             l1 = f.readline()
             self.assertEqual(l1, "this is %s 0\n"%data_old)
             l2 = f.readline()
-            self.assertEqual(l2, "this is %s 0\n"%force_out)
-            l3 = f.readline()
-            self.assertEqual(l3, "this is %s 0"%center_out_name)
\ No newline at end of file
+            self.assertEqual(l2, "this is %s 0"%cv_force_out)
\ No newline at end of file
diff --git a/tests/test_exploration.py b/tests/test_exploration.py
index ca347487..a439e07d 100755
--- a/tests/test_exploration.py
+++ b/tests/test_exploration.py
@@ -125,13 +125,13 @@
 class TestMockedExploration(unittest.TestCase):
     def setUp(self):
         self.init_models = make_mocked_init_models(4)
-        self.init_confs = make_mocked_init_confs(3)
         trust_lvl_1 = 2.0
         trust_lvl_2 = 4.0
         exploration_config = {"type":"gmx"}
         cv_config = {"cv_dim": 3}
         walker_tags = []
         self.numb_walkers = 3
+        self.init_confs = make_mocked_init_confs(self.numb_walkers)
         for ii in range(self.numb_walkers):
             walker_tags.append(walker_tag_fmt.format(idx=ii))
         self.models = upload_artifact(self.init_models)
diff --git a/tests/test_init-block.py b/tests/test_init-block.py
index d9d5e241..73d10891 100644
--- a/tests/test_init-block.py
+++ b/tests/test_init-block.py
@@ -51,7 +51,6 @@
     MockedCheckLabelInputs,
     MockedPrepLabel,
     MockedRunLabel,
-    MockedCalcMF,
     MockedPrepSelect,
     MockedRunSelect,
     MockedCollectData,
@@ -82,7 +81,6 @@
     MockedCheckLabelInputs,
     MockedPrepLabel,
     MockedRunLabel,
-    MockedCalcMF,
     default_config,
     default_config)
 
@@ -132,7 +130,6 @@ def setUp(self):
         self.trust_lvl_1 = [trust_lvl_1]*self.numb_walkers
         self.trust_lvl_2 = [trust_lvl_2]*self.numb_walkers
         self.cluster_threshold = [2.0]*self.numb_walkers
-        self.kappas = [500.]*3
         self.angular_mask = [0.]*3
         self.weights = [1.]*3
         self.numb_cluster_upper = 8
@@ -175,7 +172,6 @@ def test_exploration(self):
                 "output_freq": self.output_freq,
                 "slice_mode": self.slice_mode,
                 "label_config": self.label_config,
-                "kappas": self.kappas,
                 "tail": self.tail,
                 "train_config": self.train_config
             },
@@ -216,7 +212,7 @@ def test_exploration(self):
         self.assertTrue(os.path.isfile(self.data_out+sub_path+gmx_xtc_name))
         self.assertTrue(os.path.isfile(self.data_out+sub_path+sel_ndx_name))
         self.assertTrue(os.path.isfile(self.data_out+"/"+data_raw))
-        self.assertTrue(os.path.isfile(self.data_out+"/"+tf_model_name.format(tag="000")))
+        self.assertTrue(os.path.isfile(self.data_out+sub_path+tf_model_name.format(tag="000")))
         with open(self.data_out+sub_path+gmx_conf_out,"r") as f:
             l1 = f.readline()
             self.assertEqual(l1, "This is init conf 0")
diff --git a/tests/test_iter-block.py b/tests/test_iter-block.py
index d9d433d1..9ff8692f 100644
--- a/tests/test_iter-block.py
+++ b/tests/test_iter-block.py
@@ -54,7 +54,6 @@
     MockedCheckLabelInputs,
     MockedPrepLabel,
     MockedRunLabel,
-    MockedCalcMF,
     MockedPrepSelect,
     MockedRunSelect,
     MockedCollectData,
@@ -85,7 +84,6 @@
     MockedCheckLabelInputs,
     MockedPrepLabel,
     MockedRunLabel,
-    MockedCalcMF,
     default_config,
     default_config)
 
@@ -137,7 +135,6 @@ def setUp(self):
         self.trust_lvl_1 = [trust_lvl_1]*self.numb_walkers
         self.trust_lvl_2 = [trust_lvl_2]*self.numb_walkers
         self.cluster_threshold = [2.0]*self.numb_walkers
-        self.kappas = [500.]*3
         self.angular_mask = [0.]*3
         self.weights = [1.]*3
         self.numb_cluster_upper = 8
@@ -185,7 +182,6 @@ def test_exploration(self):
                 "output_freq": self.output_freq,
                 "slice_mode": self.slice_mode,
                 "label_config": self.label_config,
-                "kappas": self.kappas,
                 "tail": self.tail,
                 "train_config": self.train_config,
                 "adjust_amplifier": self.adjust_amplifier,
@@ -229,7 +225,7 @@ def test_exploration(self):
         self.assertTrue(os.path.isfile(self.data_out+sub_path+gmx_xtc_name))
         self.assertTrue(os.path.isfile(self.data_out+sub_path+sel_ndx_name))
         self.assertTrue(os.path.isfile(self.data_out+"/"+data_raw))
-        self.assertTrue(os.path.isfile(self.data_out+"/"+tf_model_name.format(tag="000")))
+        self.assertTrue(os.path.isfile(self.data_out+sub_path+tf_model_name.format(tag="000")))
         with open(self.data_out+sub_path+gmx_conf_out,"r") as f:
             l1 = f.readline()
             self.assertEqual(l1, "This is init conf 0")
diff --git a/tests/test_label.py b/tests/test_label.py
index 1137de2f..43aef1c1 100755
--- a/tests/test_label.py
+++ b/tests/test_label.py
@@ -19,7 +19,7 @@
 from rid.constants import (
     walker_tag_fmt,
     gmx_mdrun_log,
-    force_out
+    cv_force_out
 )
 
 from rid.utils import normalize_resources
@@ -41,8 +41,7 @@
     make_mocked_init_confs,
     MockedCheckLabelInputs,
     MockedPrepLabel,
-    MockedRunLabel,
-    MockedCalcMF
+    MockedRunLabel
 )
 
 from dflow.python import upload_packages
@@ -127,12 +126,12 @@
 class TestMockedLabel(unittest.TestCase):
     def setUp(self):
         self.init_models = make_mocked_init_models(4)
-        self.init_confs = make_mocked_init_confs(3)
         self.center = make_mocked_init_data("center", 3)
         label_config = {"type":"gmx"}
         cv_config = {"cv_dim": 3}
         walker_tags = []
         self.numb_walkers = 3
+        self.init_confs = make_mocked_init_confs(self.numb_walkers)
         for ii in range(self.numb_walkers):
             walker_tags.append(walker_tag_fmt.format(idx=ii))
         self.models = upload_artifact(self.init_models)
@@ -160,7 +159,6 @@ def test_label(self):
             MockedCheckLabelInputs,
             MockedPrepLabel,
             MockedRunLabel,
-            MockedCalcMF,
             prep_config = default_config,
             run_config = default_config
         )
@@ -170,8 +168,6 @@ def test_label(self):
             parameters = {
                 "label_config" : self.label_config,
                 "cv_config" : self.cv_config,
-                "kappas": self.kappas,
-                "angular_mask": self.angular_mask,
                 "tail": self.tail,
                 "conf_tags": self.conf_tags,
                 "block_tag" : self.block_tag
@@ -202,11 +198,11 @@ def test_label(self):
 
         if not os.path.exists(self.data_out):
             os.mkdir(self.data_out)
-        download_artifact(step.outputs.artifacts["forces"],path=self.data_out)
+        download_artifact(step.outputs.artifacts["cv_forces"],path=self.data_out)
         download_artifact(step.outputs.artifacts["md_log"],path=self.data_out)
         sub_path = "/000_000/"
         self.assertTrue(os.path.isfile(self.data_out+sub_path+gmx_mdrun_log))
-        self.assertTrue(os.path.isfile(self.data_out+sub_path+force_out))
-        with open(self.data_out+sub_path+force_out,"r") as f:
+        self.assertTrue(os.path.isfile(self.data_out+sub_path+cv_force_out))
+        with open(self.data_out+sub_path+cv_force_out,"r") as f:
             l1 = f.readline()
             self.assertEqual(l1, '1.000000e+00 2.000000e+00\n')
\ No newline at end of file