diff --git a/src/bayesvalidrox/surrogate_models/meta_model_engine.py b/src/bayesvalidrox/surrogate_models/meta_model_engine.py
index 08d91ecf77ff20aaaf88384604d75e22b31f8cc7..3985da8a3d4ff124428c9f8710b4d029ca03716c 100644
--- a/src/bayesvalidrox/surrogate_models/meta_model_engine.py
+++ b/src/bayesvalidrox/surrogate_models/meta_model_engine.py
@@ -486,7 +486,7 @@ class MetaModelEngine():
         return -1 * score   # -1 is for minimization instead of maximization
 
     # -------------------------------------------------------------------------
-    def util_BayesianActiveDesign(self, X_can, sigma2Dict, var='DKL'):
+    def util_BayesianActiveDesign(self, y_hat, std, sigma2Dict, var='DKL'):
         """
         Computes scores based on Bayesian active design criterion (var).
 
@@ -511,98 +511,35 @@ class MetaModelEngine():
 
         """
 
-        # Evaluate the PCE metamodels at that location ???
-        Y_mean_can, Y_std_can = self.MetaModel.eval_metamodel(
-            samples=np.array([X_can])
-            )
-
         # Get the data
         obs_data = self.observations
         n_obs = self.Model.n_obs
-        # TODO: Analytical DKL
+        mc_size = 10000
+
         # Sample a distribution for a normal dist
         # with Y_mean_can as the mean and Y_std_can as std.
-
-        # priorMean, priorSigma2, Obs = np.empty((0)),np.empty((0)),np.empty((0))
-
-        # for key in list(Y_mean_can):
-        #     # concatenate the measurement error
-        #     Obs = np.hstack((Obs,ObservationData[key]))
-
-        #     # concatenate the mean and variance of prior predictive
-        #     means, stds = Y_mean_can[key][0], Y_std_can[key][0]
-        #     priorMean = np.hstack((priorSigma2,means))
-        #     priorSigma2 = np.hstack((priorSigma2,stds**2))
-
-        # # Covariance Matrix of prior
-        # covPrior = np.zeros((priorSigma2.shape[0], priorSigma2.shape[0]), float)
-        # np.fill_diagonal(covPrior, priorSigma2)
-
-        # # Covariance Matrix of Likelihood
-        # covLikelihood = np.zeros((sigma2Dict.shape[0], sigma2Dict.shape[0]), float)
-        # np.fill_diagonal(covLikelihood, sigma2Dict)
-
-        # # Calculate moments of the posterior (Analytical derivation)
-        # n = priorSigma2.shape[0]
-        # covPost = np.dot(np.dot(covPrior,np.linalg.inv(covPrior+(covLikelihood/n))),covLikelihood/n)
-
-        # meanPost = np.dot(np.dot(covPrior,np.linalg.inv(covPrior+(covLikelihood/n))) , Obs) + \
-        #             np.dot(np.dot(covPrior,np.linalg.inv(covPrior+(covLikelihood/n))),
-        #                     priorMean/n)
-        # # Compute DKL from prior to posterior
-        # term1 = np.trace(np.dot(np.linalg.inv(covPrior),covPost))
-        # deltaMean = priorMean-meanPost
-        # term2 = np.dot(np.dot(deltaMean,np.linalg.inv(covPrior)),deltaMean[:,None])
-        # term3 = np.log(np.linalg.det(covPrior)/np.linalg.det(covPost))
-        # DKL = 0.5 * (term1 + term2 - n + term3)[0]
-
-        # ---------- Inner MC simulation for computing Utility Value ----------
-        # Estimation of the integral via Monte Varlo integration
-        MCsize = 20000
-        ESS = 0
-
-        while ((ESS > MCsize) or (ESS < 1)):
-
-            # Sample a distribution for a normal dist
-            # with Y_mean_can as the mean and Y_std_can as std.
-            Y_MC, std_MC = {}, {}
-            logPriorLikelihoods = np.zeros((MCsize))
-            for key in list(Y_mean_can):
-                means, stds = Y_mean_can[key][0], Y_std_can[key][0]
-                # cov = np.zeros((means.shape[0], means.shape[0]), float)
-                # np.fill_diagonal(cov, stds**2)
-
-                Y_MC[key] = np.zeros((MCsize, n_obs))
-                logsamples = np.zeros((MCsize, n_obs))
-                for i in range(n_obs):
-                    NormalDensity = stats.norm(means[i], stds[i])
-                    Y_MC[key][:, i] = NormalDensity.rvs(MCsize)
-                    logsamples[:, i] = NormalDensity.logpdf(Y_MC[key][:, i])
-
-                logPriorLikelihoods = np.sum(logsamples, axis=1)
-                std_MC[key] = np.zeros((MCsize, means.shape[0]))
-
-            #  Likelihood computation (Comparison of data and simulation
-            #  results via PCE with candidate design)
-            likelihoods = self.__normpdf(Y_MC, std_MC, obs_data, sigma2Dict)
-
-            # Check the Effective Sample Size (1<ESS<MCsize)
-            ESS = 1 / np.sum(np.square(likelihoods/np.nansum(likelihoods)))
-
-            # Enlarge sample size if it doesn't fulfill the criteria
-            if ((ESS > MCsize) or (ESS < 1)):
-                MCsize *= 10
-                ESS = 0
+        Y_MC, std_MC = {}, {}
+        logPriorLikelihoods = np.zeros((mc_size))
+        for key in list(y_hat):
+            cov = np.diag(std[key]**2)
+            rv = stats.multivariate_normal(mean=y_hat[key], cov=cov)
+            Y_MC[key] = rv.rvs(size=mc_size)
+            logPriorLikelihoods += rv.logpdf(Y_MC[key])
+            std_MC[key] = np.zeros((mc_size, y_hat[key].shape[0]))
+
+        #  Likelihood computation (Comparison of data and simulation
+        #  results via PCE with candidate design)
+        likelihoods = self.__normpdf(Y_MC, std_MC, obs_data, sigma2Dict)
 
         # Rejection Step
         # Random numbers between 0 and 1
-        unif = np.random.rand(1, MCsize)[0]
+        unif = np.random.rand(1, mc_size)[0]
 
         # Reject the poorly performed prior
         accepted = (likelihoods/np.max(likelihoods)) >= unif
 
         # Prior-based estimation of BME
-        logBME = np.log(np.nanmean(likelihoods))
+        logBME = np.log(np.nanmean(likelihoods), dtype=np.float128)
 
         # Posterior-based expectation of likelihoods
         postLikelihoods = likelihoods[accepted]
@@ -626,7 +563,7 @@ class MetaModelEngine():
 
         # Marginal log likelihood
         elif var == 'BME':
-            U_J_d = logBME
+            U_J_d = np.nanmean(likelihoods)
 
         # Entropy-based information gain
         elif var == 'infEntropy':
@@ -656,7 +593,7 @@ class MetaModelEngine():
             # D_theta_bar = np.mean(-2 * Likelihoods)
             N_star_p = 0.5 * np.var(np.log(likelihoods[likelihoods != 0]))
             Likelihoods_theta_mean = self.__normpdf(
-                Y_mean_can, Y_std_can, obs_data, sigma2Dict
+                y_hat, std, obs_data, sigma2Dict
                 )
             DIC = -2 * np.log(Likelihoods_theta_mean) + 2 * N_star_p
 
@@ -821,8 +758,7 @@ class MetaModelEngine():
         """
 
         # To avoid changes ub original aPCE object
-        Model = self.Model
-        MetaModel = deepcopy(self.MetaModel)
+        MetaModel = self.MetaModel
         out_names = MetaModel.ModelObj.Output.names
         if X_can.ndim == 1:
             X_can = X_can.reshape(1, -1)
@@ -838,25 +774,29 @@ class MetaModelEngine():
 
         # Evaluate the PCE metamodels at that location ???
         Y_PC_can, Y_std_can = MetaModel.eval_metamodel(samples=X_can)
-
-        # Add all suggestion as new ExpDesign
+        PCE_Model_can = deepcopy(MetaModel)
+        # Add the candidate to the ExpDesign
         NewExpDesignX = np.vstack((oldExpDesignX, X_can))
 
         NewExpDesignY = {}
         for key in oldExpDesignY.keys():
-            try:
-                NewExpDesignY[key] = np.vstack((oldExpDesignY[key],
-                                                Y_PC_can[key]))
-            except:
-                NewExpDesignY[key] = oldExpDesignY[key]
+            NewExpDesignY[key] = np.vstack(
+                (oldExpDesignY[key], Y_PC_can[key])
+                )
 
-        MetaModel.ExpDesign.sampling_method = 'user'
-        MetaModel.ExpDesign.X = NewExpDesignX
-        MetaModel.ExpDesign.Y = NewExpDesignY
+        PCE_Model_can.ExpDesign.sampling_method = 'user'
+        PCE_Model_can.ExpDesign.X = NewExpDesignX
+        PCE_Model_can.ModelOutputDict = NewExpDesignY
+        PCE_Model_can.ExpDesign.Y = NewExpDesignY
 
         # Train the model for the observed data using x_can
-        MetaModel.input_obj.poly_coeffs_flag = False
-        PCE_Model_can = MetaModel.train_norm_design(Model, parallel=False)
+        PCE_Model_can.input_obj.poly_coeffs_flag = False
+        PCE_Model_can.train_norm_design(parallel=False)
+
+        # Set the ExpDesign to its original values
+        PCE_Model_can.ExpDesign.X = oldExpDesignX
+        PCE_Model_can.ModelOutputDict = oldExpDesignY
+        PCE_Model_can.ExpDesign.Y = oldExpDesignY
 
         if var.lower() == 'mi':
             # Mutual information based on Krause et al
@@ -1078,10 +1018,16 @@ class MetaModelEngine():
         elif method.lower() == 'bayesactdesign':
             NCandidate = candidates.shape[0]
             U_J_d = np.zeros((NCandidate))
+            # Evaluate all candidates
+            y_can, std_can = self.MetaModel.eval_metamodel(samples=candidates)
+            # loop through candidates
             for idx, X_can in tqdm(enumerate(candidates), ascii=True,
-                                   desc="OptBayesianDesign"):
-                U_J_d[idx] = self.util_BayesianActiveDesign(X_can, sigma2Dict,
-                                                            var)
+                                   desc="BAL Design"):
+                y_hat = {key: items[idx] for key, items in y_can.items()}
+                std = {key: items[idx] for key, items in std_can.items()}
+                U_J_d[idx] = self.util_BayesianActiveDesign(
+                    y_hat, std, sigma2Dict, var)
+
         elif method.lower() == 'bayesoptdesign':
             NCandidate = candidates.shape[0]
             U_J_d = np.zeros((NCandidate))
@@ -1399,7 +1345,7 @@ class MetaModelEngine():
                     candidates, n_cand_groups, axis=0
                     )
 
-                results = Parallel(n_jobs=-1, backend='threading')(
+                results = Parallel(n_jobs=-1, backend='multiprocessing')(
                         delayed(self.run_util_func)(
                             exploit_method, split_cand[i], i, sigma2, var, X_MC)
                         for i in range(n_cand_groups))
@@ -1427,20 +1373,19 @@ class MetaModelEngine():
                     U_J_d = np.mean(U_J_d.reshape(-1, n_candidates), axis=1)
 
                 # create surrogate model for U_J_d
-                from sklearn.preprocessing import MinMaxScaler
-                # Take care of inf entries
-                good_indices = [i for i, arr in enumerate(U_J_d)
-                                if np.isfinite(arr).all()]
-                scaler = MinMaxScaler()
-                X_S = scaler.fit_transform(candidates[good_indices])
-                gp = MetaModel.gaussian_process_emulator(
-                    X_S, U_J_d[good_indices], autoSelect=True
-                    )
-                U_J_d = gp.predict(scaler.transform(allCandidates))
+                # from sklearn.preprocessing import MinMaxScaler
+                # # Take care of inf entries
+                # good_indices = [i for i, arr in enumerate(U_J_d)
+                #                 if np.isfinite(arr).all()]
+                # scaler = MinMaxScaler()
+                # X_S = scaler.fit_transform(candidates[good_indices])
+                # gp = MetaModel.gaussian_process_emulator(
+                #     X_S, U_J_d[good_indices], autoSelect=False
+                #     )
+                # U_J_d = gp.predict(scaler.transform(allCandidates))
 
                 # Normalize U_J_d
                 norm_U_J_d = U_J_d / np.sum(U_J_d)
-                print("norm_U_J_d:\n", norm_U_J_d)
             else:
                 norm_U_J_d = np.zeros((len(scoreExploration)))
 
@@ -1742,10 +1687,13 @@ class MetaModelEngine():
             # Surrogate error if valid dataset is given.
             if rmse is not None:
                 tot_sigma2s += rmse[out]**2
+            else:
+                tot_sigma2s += np.mean(std_pce[out])**2
 
             likelihoods *= stats.multivariate_normal.pdf(
                 y_hat_pce[out], data, np.diag(tot_sigma2s),
                 allow_singular=True)
+
         self.Likelihoods = likelihoods
 
         return likelihoods