diff --git a/xbanalysis/problems/fba_problem.py b/xbanalysis/problems/fba_problem.py
index ad2d65995398ed7110fc20d33e45ccea68049809..bebc68bf899fad87c14fbe219fd8a5611a7b93db 100644
--- a/xbanalysis/problems/fba_problem.py
+++ b/xbanalysis/problems/fba_problem.py
@@ -1,4 +1,5 @@
 import numpy as np
+import math
 from scipy import sparse
 
 from xbanalysis.solvers.glpk_linear_problem import GlpkLinearProblem
@@ -192,6 +193,45 @@ class FbaProblem:
         """
         self.lp.remove_obj_coefs(self.get_coefficients(objective))
 
+    def update_lp_flux_bounds(self, flux_bounds):
+        """update flux bounds for selected reactions in the linear problem.
+
+        lp problem might need to be instantiated
+        overwrite bounds of protected functions will be cleaned from already removed reactions
+
+        :param flux_bounds: lower and upper flux bounds for selected reactions
+        :type flux_bounds: dict: key: reaction id, value: list-like of two float values
+        """
+        if self.lp is None:
+            self.lp = self.create_core_fba_lp()
+        for rid, bound_upd in flux_bounds.items():
+            if rid in self.rids:
+                temp_bounds = self.flux_bounds[:, self.rid2idx[rid]].copy()
+                for i in [0, 1]:
+                    if math.isfinite(bound_upd[i]):
+                        temp_bounds[i] = bound_upd[i]
+                self.lp.set_col_bnd(self.rid2idx[rid], np.fmax(temp_bounds, 0))
+                if rid in self.rids_rev:
+                    self.lp.set_col_bnd(self.rid2idx_rev[rid], np.fmax(np.flip(-temp_bounds), 0))
+
+    def reset_lp_flux_bounds(self, rids):
+        """restore original flux bounds for selected reactions in linear problem.
+
+        overwrite bounds of protected functions will be cleaned from already removed reactions
+
+        :param rids: reaction ids for which to restore the model orignal flux bounds
+        :type rids: list-like of str
+        """
+        assert self.lp is not None
+
+        for rid in rids:
+            if rid in self.rids:
+                assert rid in self.rids
+                bounds = self.flux_bounds[:, self.rid2idx[rid]]
+                self.lp.set_col_bnd(self.rid2idx[rid], np.fmax(bounds, 0))
+                if rid in self.rids_rev:
+                    self.lp.set_col_bnd(self.rid2idx_rev[rid], np.fmax(np.flip(-bounds), 0))
+
     def fba_optimize(self, short_result=False):
         """FBA optimization of current problem
 
@@ -252,6 +292,62 @@ class FbaProblem:
         res['reduced_costs'] = self.combine_fwd_rev(res['reduced_costs'])
         return res
 
+    def cyclefree_fba_optimize(self):
+        """cycle free FBA (remove loops from FBA solution)
+
+        Reference:
+        Desouki et al., 2015, CycleFreeFlux: efficient removal of thermodynamically
+        infeasible loops from flux distributions
+
+        Method:
+        - run FBA and determine a flux distribution
+        - implement model objective function as equality constraint with FBA optimum
+        - configure a new objective function ('minimize' absolut fluxes)
+            - exchange reactions are not considered and fluxes are fixed at FBA level
+            - reactions with positive FBA fluxes get a coefficient of 1.0 and fluxes bound between zero and FBA level
+            - reactions with negative FBA fluxes get a coefficient of -1.0 and fluxes bound between FBA level and zero
+                - i.e. internal reactions are only allowed to shrink, but not change direction
+            - run FBA on new objective and retrieve new flux values for reactions.
+                - use new flux values with model objective value as optimum
+        :return: result of cycleFree FBA solution
+        :rtype: dict
+        """
+        # get optimum value for model objective function
+        res = self.fba_optimize(short_result=False)
+        if res['success'] is False:
+            return res
+        optimum = res['fun']
+
+        cff_objective = {}
+        cff_flux_bounds = {}
+        for rid, idx in self.rid2idx.items():
+            flux = res['x'][idx]
+            if len(self.xba_model.reactions[rid].products) == 0:
+                cff_flux_bounds[rid] = [flux, flux]
+            elif flux >= 0:
+                cff_flux_bounds[rid] = [0.0, flux]
+                cff_objective[rid] = 1.0
+            else:
+                cff_flux_bounds[rid] = [flux, 0.0]
+                cff_objective[rid] = -1.0
+
+        # implement model main objective as an equality constraint with optimal value
+        row_bnds = np.array([optimum, optimum])
+        row_idx0 = self.lp.add_constraint(self.get_coefficients(self.objective), row_bnds)
+
+        # configure cyclefree FBA objective
+        self.set_lp_objective(cff_objective, 'minimize')
+        self.update_lp_flux_bounds(cff_flux_bounds)
+        res = self.lp.solve(short_result=False)
+        self.reset_lp_flux_bounds(set(cff_flux_bounds))
+        self.remove_lp_objective(cff_objective)
+        self.lp.del_constraint(row_idx0)
+
+        res['fun'] = optimum
+        res['x'] = self.combine_fwd_rev(res['x'])
+        res['reduced_costs'] = self.combine_fwd_rev(res['reduced_costs'])
+        return res
+
     def fva_optimize(self, rids=None, frac_of_opt=1.0):
         """Flux variability analyzis on reactions within fraction of optimum