diff --git a/src/modelchecker/multiobjective/helper/SparseMultiObjectiveHelper.cpp b/src/modelchecker/multiobjective/helper/SparseMultiObjectiveHelper.cpp
index 62f48ce42..f2c04ea1d 100644
--- a/src/modelchecker/multiobjective/helper/SparseMultiObjectiveHelper.cpp
+++ b/src/modelchecker/multiobjective/helper/SparseMultiObjectiveHelper.cpp
@@ -243,43 +243,12 @@ namespace storm {
             
             template <class SparseModelType, typename RationalNumberType>
             void SparseMultiObjectiveHelper<SparseModelType, RationalNumberType>::updateUnderApproximation(std::vector<RefinementStep> const& refinementSteps, std::shared_ptr<storm::storage::geometry::Polytope<RationalNumberType>>& underApproximation) {
-                std::vector<Point> paretoPointsVec;
-                paretoPointsVec.reserve(refinementSteps.size());
+                std::vector<Point> paretoPoints;
+                paretoPoints.reserve(refinementSteps.size());
                 for(auto const& step : refinementSteps) {
-                    paretoPointsVec.push_back(step.getPoint());
+                    paretoPoints.push_back(step.getPoint());
                 }
-                
-                STORM_LOG_WARN("REMOVE ADDING ADDITIONAL VERTICES AS SOON AS HYPRO WORKS FOR DEGENERATED POLYTOPES");
-                if(paretoPointsVec.front().size()==2) {
-                    Point p1 = {-10000, -9999};
-                    Point p2 = {-9999, -10000};
-                    paretoPointsVec.push_back(p1);
-                    paretoPointsVec.push_back(p2);
-                } else {
-                    Point p1 = {-10000, -9999, -9999};
-                    Point p2 = {-9999, -10000, -9999};
-                    Point p3 = {-9999, -9999, -10000};
-                    paretoPointsVec.push_back(p1);
-                    paretoPointsVec.push_back(p2);
-                    paretoPointsVec.push_back(p3);
-                }
-                
-                boost::optional<Point> upperBounds;
-                if(!paretoPointsVec.empty()){
-                    //Get the pointwise maximum of the pareto points
-                    upperBounds = paretoPointsVec.front();
-                    for(auto paretoPointIt = paretoPointsVec.begin()+1; paretoPointIt != paretoPointsVec.end(); ++paretoPointIt){
-                        auto upperBoundIt = upperBounds->begin();
-                        for(auto const& paretoPointCoordinate : *paretoPointIt){
-                            if(paretoPointCoordinate>*upperBoundIt){
-                                *upperBoundIt = paretoPointCoordinate;
-                            }
-                            ++upperBoundIt;
-                        }
-                    }
-                }
-                
-                underApproximation = storm::storage::geometry::Polytope<RationalNumberType>::create(paretoPointsVec)->downwardClosure(upperBounds);
+                underApproximation = storm::storage::geometry::Polytope<RationalNumberType>::createDownwardClosure(paretoPoints);
                 STORM_LOG_DEBUG("Updated UnderApproximation to " << underApproximation->toString(true));
             }
             
diff --git a/src/storage/geometry/HyproPolytope.cpp b/src/storage/geometry/HyproPolytope.cpp
index 79c696a64..0670edd37 100644
--- a/src/storage/geometry/HyproPolytope.cpp
+++ b/src/storage/geometry/HyproPolytope.cpp
@@ -154,47 +154,6 @@ namespace storm {
                 return std::make_shared<HyproPolytope<ValueType>>(internPolytope.linearTransformation(std::move(hyproMatrix), storm::adapters::toHypro(vector)));
             }
             
-            template <typename ValueType>
-            std::shared_ptr<Polytope<ValueType>> HyproPolytope<ValueType>::downwardClosure(boost::optional<Point> const& upperBounds) const {
-                if(this->isUniversal() || this->isEmpty()) {
-                    // In these cases, the polytope does not change, i.e., we return a copy of this
-                    return std::make_shared<HyproPolytope<ValueType>>(*this);
-                }
-                // Only keep the halfspaces where all entries of the normal vector are non-negative
-                std::vector<hypro::Halfspace<ValueType>> halfspaces;
-                for(auto& h : internPolytope.constraints()){
-                    if((h.normal().array() >= storm::utility::zero<ValueType>()).all()){
-                        halfspaces.push_back(h);
-                    }
-                }
-                // Add Halfspaces to bound the polytope in each (positive) direction
-                for(uint_fast64_t dim = 0; dim < internPolytope.dimension(); ++dim){
-                    hypro::vector_t<ValueType> direction = hypro::vector_t<ValueType>::Zero(internPolytope.dimension());
-                    direction(dim) = storm::utility::one<ValueType>();
-                    if(upperBounds){
-                        ValueType upperBound = (*upperBounds)[dim];
-                        halfspaces.emplace_back(std::move(direction), std::move(upperBound));
-                    } else {
-                        // Compute max{x_dim | x \in P }
-                        hypro::EvaluationResult<ValueType> evalRes = internPolytope.evaluate(direction);
-                        switch (evalRes.errorCode) {
-                            case hypro::SOLUTION::FEAS:
-                                halfspaces.emplace_back(std::move(direction), std::move(evalRes.supportValue));
-                                break;
-                            case hypro::SOLUTION::INFTY:
-                                // if this polytope is not bounded in the current direction, no halfspace needs to be added
-                                break;
-                            default:
-                                // there should never be an infeasible solution as we already excluded the empty polytope.
-                                STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected eror code for Polytope evaluation");
-                                break;
-                        }
-                    }
-                }
-                return std::make_shared<HyproPolytope<ValueType>>(HyproPolytopeType(std::move(halfspaces)));
-            }
-            
-            
             template <typename ValueType>
             std::pair<typename HyproPolytope<ValueType>::Point, bool> HyproPolytope<ValueType>::optimize(Point const& direction) const {
                 hypro::EvaluationResult<ValueType> evalRes = internPolytope.evaluate(storm::adapters::toHypro(direction));
diff --git a/src/storage/geometry/HyproPolytope.h b/src/storage/geometry/HyproPolytope.h
index 9de16fae8..78eb56ccd 100644
--- a/src/storage/geometry/HyproPolytope.h
+++ b/src/storage/geometry/HyproPolytope.h
@@ -106,17 +106,6 @@ namespace storm {
                  */
                 virtual std::shared_ptr<Polytope<ValueType>> linearTransformation(std::vector<Point> const& matrix, Point const& vector) const override;
                 
-                /*!
-                 * Returns the downward closure of this, i.e., the set { x | ex. y \in P : x<=y} where P is this Polytope.
-                 * Put differently, the resulting polytope corresponds to this polytope, where
-                 * 1. a vector y with y_i=max{x_i | x \in P} is computed and for each i, a halfspace with offset y_i and
-                 *    normal vector n (where n_i = 1 and the remaining entries are 0) is inserted.
-                 * 2. all halfspaces where the normal vector has at least one negative entry are removed
-                 *
-                 * @param upperBounds If given, this vector is considered for y (hence, max{x_i | x i \in P does not need to be computed)
-                 */
-                virtual std::shared_ptr<Polytope<ValueType>> downwardClosure(boost::optional<Point> const& upperBounds = boost::none) const override;
-                
                 /*!
                  * Finds an optimal point inside this polytope w.r.t. the given direction, i.e.,
                  * a point that maximizes dotPorduct(point, direction).
diff --git a/src/storage/geometry/Polytope.cpp b/src/storage/geometry/Polytope.cpp
index f5fc8ee6d..92f7b08c8 100644
--- a/src/storage/geometry/Polytope.cpp
+++ b/src/storage/geometry/Polytope.cpp
@@ -52,6 +52,43 @@ namespace storm {
                 return nullptr;
             }
             
+            template <typename ValueType>
+            std::shared_ptr<Polytope<ValueType>> Polytope<ValueType>::createDownwardClosure(std::vector<Point> const& points) {
+                if(points.empty()) {
+                    // In this case, the downwardclosure is empty
+                    return createEmptyPolytope();
+                }
+                uint_fast64_t const dimensions = points.front().size();
+                std::vector<Halfspace<ValueType>> halfspaces;
+                // We build the convex hull of the given points.
+                // However, auxiliary points (that will always be in the downward closure) are added.
+                // Then, the halfspaces of the resulting polytope are a superset of the halfspaces of the downward closure.
+                std::vector<Point> auxiliaryPoints = points;
+                auxiliaryPoints.reserve(auxiliaryPoints.size()*(1+dimensions));
+                for(auto const& point : points) {
+                    for(uint_fast64_t dim=0; dim<dimensions; ++dim) {
+                        auxiliaryPoints.push_back(point);
+                        auxiliaryPoints.back()[dim] -= storm::utility::one<ValueType>();
+                    }
+                }
+                std::vector<Halfspace<ValueType>> auxiliaryHalfspaces = create(auxiliaryPoints)->getHalfspaces();
+                // The downward closure is obtained by selecting the halfspaces for which the normal vector is non-negative (coordinate wise).
+                // Note that due to the auxiliary points, the polytope is never degenerated and thus there is always one unique halfspace-representation which is necessary:
+                // Consider, e.g., the convex hull of two points (1,0,0) and (0,1,1).
+                // There are multiple halfspace-representations for this set. In particular, there is one where all but one normalVectors have negative entries.
+                // However, the downward closure of this set can only be represented with 5 halfspaces.
+                for(auto& h : auxiliaryHalfspaces){
+                    bool allGreaterZero = true;
+                    for(auto const& value : h.normalVector()) {
+                        allGreaterZero &= (value >= storm::utility::zero<ValueType>());
+                    }
+                    if(allGreaterZero){
+                        halfspaces.push_back(std::move(h));
+                    }
+                }
+                return create(halfspaces);
+            }
+            
             template <typename ValueType>
             Polytope<ValueType>::Polytope() {
                 // Intentionally left empty
@@ -129,9 +166,8 @@ namespace storm {
             }
             
             template <typename ValueType>
-            std::shared_ptr<Polytope<ValueType>> Polytope<ValueType>::downwardClosure(boost::optional<Point> const& upperBounds) const {
-                STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Functionality not implemented.");
-                return nullptr;
+            std::shared_ptr<Polytope<ValueType>> Polytope<ValueType>::downwardClosure() const {
+                return createDownwardClosure(this->getVertices());
             }
             
             template <typename ValueType>
diff --git a/src/storage/geometry/Polytope.h b/src/storage/geometry/Polytope.h
index de08566e6..2950d2c94 100644
--- a/src/storage/geometry/Polytope.h
+++ b/src/storage/geometry/Polytope.h
@@ -41,6 +41,12 @@ namespace storm {
                  */
                 static std::shared_ptr<Polytope<ValueType>> createEmptyPolytope();
                 
+                /*!
+                 * Creates the downward closure of the given points (i.e., the set { x | ex. y \in conv(points) : x<=y }
+                 * If the vector of points is empty, the resulting polytope be empty.
+                 */
+                static std::shared_ptr<Polytope<ValueType>> createDownwardClosure(std::vector<Point> const& points);
+                
                 /*!
                  * Returns the vertices of this polytope.
                  */
@@ -98,14 +104,8 @@ namespace storm {
                 
                 /*!
                  * Returns the downward closure of this, i.e., the set { x | ex. y \in P : x<=y} where P is this Polytope.
-                 * Put differently, the resulting polytope corresponds to this polytope, where
-                 * 1. a vector y with y_i=max{x_i | x \in P} is computed and for each i, a halfspace with offset y_i and
-                 *    normal vector n (where n_i = 1 and the remaining entries are 0) is inserted.
-                 * 2. all halfspaces where the normal vector has at least one negative entry are removed
-                 *
-                 * @param upperBounds If given, this vector is considered for y (hence, max{x_i | x i \in P does not need to be computed)
                  */
-                virtual std::shared_ptr<Polytope<ValueType>> downwardClosure(boost::optional<Point> const& upperBounds = boost::none) const;
+                virtual std::shared_ptr<Polytope<ValueType>> downwardClosure() const;
                 
                 /*!
                  * Finds an optimal point inside this polytope w.r.t. the given direction, i.e.,