diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6489f0c99b49d863785fb93edcd578b0ec0a2e7d..d6a04a58251617ac02fb881f996947896b82539a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -32,7 +32,7 @@ install(TARGETS sisso++ DESTINATION ${CMAKE_CURRENT_LIST_DIR}/../bin/)
 if(USE_PYTHON)
     include(${CMAKE_CURRENT_LIST_DIR}/../cmake/TransferDocStrings.cmake)
     file(GLOB_RECURSE SISSOLIB_SOURCES *.cpp)
-    list(REMOVE_ITEM SISSOLIB_SOURCES main.cpp)
+    list(REMOVE_ITEM SISSOLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/main.cpp)
     list(REMOVE_ITEM SISSOLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/python/bindings_docstring_keyed.cpp)
     list(REMOVE_ITEM SISSOLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/python/bindings_docstring_keyed.hpp)
 
diff --git a/src/descriptor_identifier/Model/Model.cpp b/src/descriptor_identifier/Model/Model.cpp
index 744348a0da84c8fd501fa29d3378658bc0e6725d..0b21669aff7287042c7ef330c09a20750a7b4132 100644
--- a/src/descriptor_identifier/Model/Model.cpp
+++ b/src/descriptor_identifier/Model/Model.cpp
@@ -80,13 +80,14 @@ Model::Model(std::string train_file)
 
         int rung = std::stoi(split_str[0]);
         std::string unit_str = split_str[1];
-        std::string expr = split_str[2];
+        std::string postfix_expr = split_str[2];
+        std::string expr = split_str[3];
 
         std::vector<double> feat_val(_n_samp_train);
         std::vector<double> feat_test_val = {};
         std::copy_n(&_D_train[ff * _n_samp_train], _n_samp_train, feat_val.data());
 
-        model_node_ptr feat = std::make_shared<ModelNode>(ff, rung, expr, feat_val, feat_test_val, Unit(unit_str));
+        model_node_ptr feat = std::make_shared<ModelNode>(ff, rung, expr, postfix_expr, feat_val, feat_test_val, Unit(unit_str));
         _feats.push_back(feat);
     }
 
@@ -107,14 +108,16 @@ Model::Model(std::string train_file, std::string test_file)
 
         int rung = std::stoi(split_str[0]);
         std::string unit_str = split_str[1];
-        std::string expr = split_str[2];
+        std::string postfix_expr = split_str[2];
+        std::string expr = split_str[3];
+
         std::vector<double> feat_val(_n_samp_train);
         std::vector<double> feat_test_val(_n_samp_test);
 
         std::copy_n(&_D_train[ff * _n_samp_train], _n_samp_train, feat_val.data());
         std::copy_n(&_D_test[ff * _n_samp_test], _n_samp_test, feat_test_val.data());
 
-        _feats.push_back(std::make_shared<ModelNode>(ff, rung, expr, feat_val, feat_test_val, Unit(unit_str)));
+        _feats.push_back(std::make_shared<ModelNode>(ff, rung, expr, postfix_expr, feat_val, feat_test_val, Unit(unit_str)));
     }
 }
 
@@ -283,7 +286,7 @@ void Model::to_file(std::string filename, bool train, std::vector<int> test_inds
 
     out_file_stream << "# Feature Rung, Units, and Expressions" << std::endl;
     for(int ff = 0; ff < _feats.size(); ++ff)
-        out_file_stream << std::setw(6) << std::left << "# " + std::to_string(ff) + ", " << std::to_string(_feats[ff]->rung()) + ", " << std::setw(50) << _feats[ff]->unit().toString() + ", " << _feats[ff]->expr() << std::endl;
+        out_file_stream << std::setw(6) << std::left << "# " + std::to_string(ff) + ", " << std::to_string(_feats[ff]->rung()) + ", " << std::setw(50) << _feats[ff]->unit().toString() + ", " << _feats[ff]->postfix_expr() + "," << _feats[ff]->expr() << std::endl;
 
     out_file_stream << "# Number of Samples Per Task" << std::endl;
     if(train)
diff --git a/src/descriptor_identifier/SISSORegressor.cpp b/src/descriptor_identifier/SISSORegressor.cpp
index ae27fe0d5ab02526095682e0e98cef1f75d044be..8542e30a3eeafe72b2c8e766fe86adab79d7b0c1 100644
--- a/src/descriptor_identifier/SISSORegressor.cpp
+++ b/src/descriptor_identifier/SISSORegressor.cpp
@@ -110,7 +110,7 @@ void SISSORegressor::fit()
     for(int rr = 0; rr < _n_residual; ++rr)
     {
         node_value_arrs::clear_temp_test_reg();
-        model_node_ptr model_feat = std::make_shared<ModelNode>(_feat_space->phi_selected()[rr]->arr_ind(), _feat_space->phi_selected()[rr]->rung(), _feat_space->phi_selected()[rr]->expr(), _feat_space->phi_selected()[rr]->value(), _feat_space->phi_selected()[rr]->test_value(), _feat_space->phi_selected()[rr]->unit());
+        model_node_ptr model_feat = std::make_shared<ModelNode>(_feat_space->phi_selected()[rr]->arr_ind(), _feat_space->phi_selected()[rr]->rung(), _feat_space->phi_selected()[rr]->expr(), _feat_space->phi_selected()[rr]->postfix_expr(), _feat_space->phi_selected()[rr]->value(), _feat_space->phi_selected()[rr]->test_value(), _feat_space->phi_selected()[rr]->unit());
         models.push_back(Model(_prop, _prop_test, {model_feat}, _task_sizes_train, _task_sizes_test));
         models.back().copy_error(&residual[rr * _n_samp]);
         if(_mpi_comm->rank() == 0)
@@ -210,7 +210,7 @@ void SISSORegressor::l0_norm(std::vector<double>& prop, int n_dim)
         for(int ii = 0; ii < n_dim; ++ii)
         {
             int index = all_inds_min[inds[rr] * n_dim + ii];
-            min_nodes[ii] = std::make_shared<ModelNode>(_feat_space->phi_selected()[index]->arr_ind(), _feat_space->phi_selected()[index]->rung(), _feat_space->phi_selected()[index]->expr(), _feat_space->phi_selected()[index]->value(), _feat_space->phi_selected()[index]->test_value(), _feat_space->phi_selected()[index]->unit());
+            min_nodes[ii] = std::make_shared<ModelNode>(_feat_space->phi_selected()[index]->arr_ind(), _feat_space->phi_selected()[index]->rung(), _feat_space->phi_selected()[index]->expr(), _feat_space->phi_selected()[index]->postfix_expr(), _feat_space->phi_selected()[index]->value(), _feat_space->phi_selected()[index]->test_value(), _feat_space->phi_selected()[index]->unit());
         }
         models.push_back(Model(_prop, _prop_test, min_nodes, _task_sizes_train, _task_sizes_test));
     }
diff --git a/src/feature_creation/feature_space/FeatureSpace.cpp b/src/feature_creation/feature_space/FeatureSpace.cpp
index 79a06815d88ac19509ae68b6b493e541fc3e4695..b49a7ef03b579201228bf0894206d78dc779ee24 100644
--- a/src/feature_creation/feature_space/FeatureSpace.cpp
+++ b/src/feature_creation/feature_space/FeatureSpace.cpp
@@ -66,7 +66,7 @@ void FeatureSpace::initialize_fs(std::vector<double> prop)
     {
         std::ofstream out_file_stream = std::ofstream();
         out_file_stream.open(_feature_space_file);
-        out_file_stream << std::setw(14) <<std::left << "# FEAT_ID" << std::setw(24) << std::left << "Score" << "Feature Expression" << std::endl;
+        out_file_stream << std::setw(14) <<std::left << "# FEAT_ID" << std::setw(24) << std::left << "Score" << "Feature Postfix Expression (RPN)" << std::endl;
         out_file_stream.close();
     }
     _project = project_funcs::project_r;
@@ -535,7 +535,6 @@ void FeatureSpace::sis(std::vector<double>& prop)
         if(is_valid)
         {
             scores_sel[cur_feat_local] = _scores[inds[ii]];
-            // phi_sel.push_back(std::make_shared<FeatureNode>(cur_feat + cur_feat_local, _phi[inds[ii]]->expr(), _phi[inds[ii]]->value(), _phi[inds[ii]]->test_value(), _phi[inds[ii]]->unit(), true));
             phi_sel.push_back(_phi[inds[ii]]);
             phi_sel.back()->set_selected(true);
             phi_sel.back()->set_d_mat_ind(cur_feat + cur_feat_local);
@@ -642,7 +641,7 @@ void FeatureSpace::sis(std::vector<double>& prop)
                 inds = util_funcs::argsort(sent_scores);
                 for(int ii = 0; ii < _n_sis_select; ++ii)
                 {
-                    out_file_stream << std::setw(14) <<std::left << cur_feat << std::setw(24) << std::setprecision(18) << std::left << -1 * sent_scores[inds[ii]] << sent_phi[inds[ii]]->expr() << std::endl;
+                    out_file_stream << std::setw(14) <<std::left << cur_feat << std::setw(24) << std::setprecision(18) << std::left << -1 * sent_scores[inds[ii]] << sent_phi[inds[ii]]->postfix_expr() << std::endl;
                     _phi_selected.push_back(sent_phi[inds[ii]]);
                     _phi_selected.back()->set_selected(true);
                     _phi_selected.back()->set_d_mat_ind(cur_feat);
@@ -665,7 +664,7 @@ void FeatureSpace::sis(std::vector<double>& prop)
                 {
                     if(valid_score_against_current(cur_feat_local, sent_phi[inds[ii]]->value().data(), sent_scores[inds[ii]], scores_sel, scores_comp))
                     {
-                        out_file_stream << std::setw(14) <<std::left << cur_feat << std::setw(24) << std::setprecision(18) << std::left << -1 * sent_scores[inds[ii]] << sent_phi[inds[ii]]->expr() << std::endl;
+                        out_file_stream << std::setw(14) <<std::left << cur_feat << std::setw(24) << std::setprecision(18) << std::left << -1 * sent_scores[inds[ii]] << sent_phi[inds[ii]]->postfix_expr() << std::endl;
 
                         _phi_selected.push_back(sent_phi[inds[ii]]);
 
@@ -711,7 +710,7 @@ void FeatureSpace::sis(std::vector<double>& prop)
         inds = util_funcs::argsort(scores_sel);
         for(auto& ind : inds)
         {
-            out_file_stream << std::setw(14) <<std::left << cur_feat << std::setw(24) << std::setprecision(18) << std::left << -1 * scores_sel[ind] << phi_sel[ind]->expr() << std::endl;
+            out_file_stream << std::setw(14) <<std::left << cur_feat << std::setw(24) << std::setprecision(18) << std::left << -1 * scores_sel[ind] << phi_sel[ind]->postfix_expr() << std::endl;
             _phi_selected.push_back(phi_sel[ind]);
             _phi_selected.back()->set_d_mat_ind(cur_feat);
             _phi_selected.back()->set_value();
diff --git a/src/feature_creation/node/FeatureNode.cpp b/src/feature_creation/node/FeatureNode.cpp
index 9b0031d4dc42a0609f98521a8e7c2d293d2236ff..1145a4f2eddc63436ce0690c7da1eb8d5084ff89 100644
--- a/src/feature_creation/node/FeatureNode.cpp
+++ b/src/feature_creation/node/FeatureNode.cpp
@@ -40,4 +40,19 @@ void FeatureNode::update_div_mult_leaves(std::map<std::string, double>& div_mult
     expected_abs_tot += std::abs(fact);
 }
 
+std::map<int, int> FeatureNode::primary_feature_decomp()
+{
+    std::map<int, int> pf_decomp;
+    pf_decomp[_arr_ind] = 1;
+    return pf_decomp;
+}
+
+void FeatureNode::update_primary_feature_decomp(std::map<int, int>& pf_decomp)
+{
+    if(pf_decomp.count(_arr_ind) > 0)
+        pf_decomp[_arr_ind] += 1;
+    else
+        pf_decomp[_arr_ind] = 1;
+}
+
 // BOOST_CLASS_EXPORT(FeatureNode)
diff --git a/src/feature_creation/node/FeatureNode.hpp b/src/feature_creation/node/FeatureNode.hpp
index c1d05d921cbfdae69da460bf82b7846f28a60ea7..abf9086b0b7dfd539240bd20b8f752224082dc57 100644
--- a/src/feature_creation/node/FeatureNode.hpp
+++ b/src/feature_creation/node/FeatureNode.hpp
@@ -214,6 +214,35 @@ public:
      */
     inline int rung(int cur_rung = 0){return cur_rung;}
 
+    /**
+     * @brief Get the primary feature decomposition of a feature
+     * @return A map representing the primary feature comprising a feature
+     */
+    std::map<int, int> primary_feature_decomp();
+
+    /**
+     * @brief Update the primary feature decomposition of a feature
+     *
+     * @param pf_decomp The primary feature decomposition of the feature calling this function.
+     */
+    void update_primary_feature_decomp(std::map<int, int>& pf_decomp);
+
+    /**
+     * @brief Converts a feature into a postfix expression (reverse polish notation)
+     *
+     * @details Recursively creates a postfix representation of the string
+     *
+     * @param cur_expr The current expression
+     * @return The current postfix expression of the feature
+     */
+    inline void update_postfix(std::string& cur_expr){cur_expr = get_postfix_term() + "|" + cur_expr;};
+
+    /**
+     * @brief Get the three character representation of the operator
+     * @return the three character representation of the operator
+     */
+    inline std::string get_postfix_term(){return std::to_string(_feat_ind);}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/ModelNode.cpp b/src/feature_creation/node/ModelNode.cpp
index 13b962916bd0b8fb6636b487f38f9de9f238928d..b6f4d2142ad1b5d43ac9030006e13a6d4bc9cb76 100644
--- a/src/feature_creation/node/ModelNode.cpp
+++ b/src/feature_creation/node/ModelNode.cpp
@@ -3,8 +3,9 @@
 ModelNode::ModelNode()
 {}
 
-ModelNode::ModelNode(int feat_ind, int rung, std::string expr, std::vector<double> value, std::vector<double> test_value, Unit unit) :
+ModelNode::ModelNode(int feat_ind, int rung, std::string expr, std::string post_fix_expr, std::vector<double> value, std::vector<double> test_value, Unit unit) :
     FeatureNode(feat_ind, expr, value, test_value, unit, false),
+    _expr_postfix(post_fix_expr),
     _rung(rung)
  {}
 
@@ -30,3 +31,45 @@ void ModelNode::update_div_mult_leaves(std::map<std::string, double>& div_mult_l
 
     expected_abs_tot += std::abs(fact);
 }
+
+std::map<int, int> ModelNode::primary_feature_decomp()
+{
+    std::map<int, int> pf_decomp;
+    std::vector<std::string> split_postfix = str_utils::split_string_trim(_expr_postfix, "|");
+    for(auto& part : split_postfix)
+    {
+        try
+        {
+            if(pf_decomp.count(std::stoi(part)))
+                ++pf_decomp[std::stoi(part)];
+            else
+                pf_decomp[std::stoi(part)] = 1;
+        }
+        catch(const std::invalid_argument e)
+        {
+            // Do Nothing
+        }
+    }
+
+    return pf_decomp;
+}
+
+void ModelNode::update_primary_feature_decomp(std::map<int, int>& pf_decomp)
+{
+    pf_decomp.clear();
+    std::vector<std::string> split_postfix = str_utils::split_string_trim(_expr_postfix, "|");
+    for(auto& part : split_postfix)
+    {
+        try
+        {
+            if(pf_decomp.count(std::stoi(part)))
+                ++pf_decomp[std::stoi(part)];
+            else
+                pf_decomp[std::stoi(part)] = 1;
+        }
+        catch(const std::invalid_argument e)
+        {
+            // Do Nothing
+        }
+    }
+}
diff --git a/src/feature_creation/node/ModelNode.hpp b/src/feature_creation/node/ModelNode.hpp
index 2609c874ff8fcb598049f8478c0a55671e8adfb2..eabb2f7be417ca47552f8180de940f48fcada786 100644
--- a/src/feature_creation/node/ModelNode.hpp
+++ b/src/feature_creation/node/ModelNode.hpp
@@ -10,6 +10,7 @@
 #define MODEL_NODE
 
 #include <feature_creation/node/FeatureNode.hpp>
+#include <utils/string_utils.hpp>
 
 // DocString: cls_model_node
 /**
@@ -33,7 +34,7 @@ class ModelNode: public FeatureNode
 
 protected:
     int _rung;
-
+    std::string _expr_postfix;
 public:
     /**
      * @brief Base Constructor
@@ -51,7 +52,7 @@ public:
      * @param value Value of the feature for each test sample
      * @param unit Unit of the feature
      */
-    ModelNode(int feat_ind, int rung, std::string expr, std::vector<double> value, std::vector<double> test_value, Unit unit);
+    ModelNode(int feat_ind, int rung, std::string expr, std::string expr_postfix, std::vector<double> value, std::vector<double> test_value, Unit unit);
 
     /**
      * @brief Copy Constructor
@@ -132,7 +133,6 @@ public:
      */
     inline double* test_value_ptr(int offset = -1){return _test_value.data();}
 
-
     // DocString: model_node_rung
     /**
      * @brief return the rung of the feature
@@ -141,6 +141,25 @@ public:
      */
     inline int rung(int cur_rung = 0){return _rung;}
 
+    /**
+     * @brief Update the primary feature decomposition of a feature
+     *
+     * @param pf_decomp The primary feature decomposition of the feature calling this function.
+     */
+    void update_primary_feature_decomp(std::map<int, int>& pf_decomp);
+
+    /**
+     * @brief Get the primary feature decomposition of a feature
+     * @return A map representing the primary feature comprising a feature
+     */
+    std::map<int, int> primary_feature_decomp();
+
+    /**
+     * @brief Get the three character representation of the operator
+     * @return the three character representation of the operator
+     */
+    inline std::string get_postfix_term(){return _expr_postfix;}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/Node.hpp b/src/feature_creation/node/Node.hpp
index c15b32e953279f6870efa914e68b5f347629a3c3..b258b5e728116203fe58e4dc43325a16f6a79555 100644
--- a/src/feature_creation/node/Node.hpp
+++ b/src/feature_creation/node/Node.hpp
@@ -28,6 +28,7 @@
 #include <boost/serialization/unique_ptr.hpp>
 
 #ifdef PY_BINDINGS
+    namespace py = boost::python;
     namespace np = boost::python::numpy;
 #endif
 
@@ -262,6 +263,42 @@ public:
      */
     virtual int rung(int cur_rung = 0) = 0;
 
+    /**
+     * @brief Get the primary feature decomposition of a feature
+     * @return A map representing the primary feature comprising a feature
+     */
+    virtual std::map<int, int> primary_feature_decomp() = 0;
+
+    /**
+     * @brief Update the primary feature decomposition of a feature
+     *
+     * @param pf_decomp The primary feature decomposition of the feature calling this function.
+     */
+    virtual void update_primary_feature_decomp(std::map<int, int>& pf_decomp) = 0;
+
+    /**
+     * @brief Converts a feature into a postfix expression (reverse polish notation)
+     *
+     * @details Recursively creates a postfix representation of the string
+     *
+     * @param cur_expr The current expression
+     * @return The current postfix expression of the feature
+     */
+    virtual void update_postfix(std::string& cur_expr) = 0;
+
+    // DocString: node_postfix_expr
+    /**
+     * @brief Get the postfix expression for the feature
+     * @return The postfix string for the expression
+     */
+    inline std::string postfix_expr(){std::string cur_expr = ""; update_postfix(cur_expr); return cur_expr.substr(0, cur_expr.size() - 1);}
+
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    virtual std::string get_postfix_term() = 0;
+
     /**
      * @brief update the dictionary used to check if an Add/Sub/AbsDiff node is valid
      *
@@ -279,6 +316,8 @@ public:
      * @param expected_abs_tot The expected absolute sum of all values in div_mult_leaves
      */
     virtual void update_div_mult_leaves(std::map<std::string, double>& div_mult_leaves, double fact, double& expected_abs_tot) = 0;
+
+
     #ifdef PY_BINDINGS
 
         // DocString: node_value_py
@@ -294,6 +333,13 @@ public:
          * @return The test data as a numpy array
          */
         inline np::ndarray test_value_py(){return python_conv_utils::to_ndarray<double>(test_value());}
+
+        // DocString: node_primary_feature_decomp
+        /**
+         * @brief Get the primary feature decomposition of a feature
+         * @return A python dict representing the primary feature comprising a feature
+         */
+        inline py::dict primary_feature_decomp_py(){return python_conv_utils::to_dict<int, int>(primary_feature_decomp());}
     #endif
 };
 
diff --git a/src/feature_creation/node/operator_nodes/OperatorNode.hpp b/src/feature_creation/node/operator_nodes/OperatorNode.hpp
index 070e24fe68fbec7d6285d4603aeba24fb526a64b..2fca627650f79516a2136b31f7885fe07723b27f 100644
--- a/src/feature_creation/node/operator_nodes/OperatorNode.hpp
+++ b/src/feature_creation/node/operator_nodes/OperatorNode.hpp
@@ -217,6 +217,49 @@ public:
      */
     virtual NODE_TYPE type() = 0;
 
+     /**
+     * @brief Get the primary feature decomposition of a feature
+     * @return A map representing the primary feature comprising a feature
+     */
+    std::map<int, int> primary_feature_decomp()
+    {
+        std::map<int, int> pf_decomp;
+        update_primary_feature_decomp(pf_decomp);
+        return pf_decomp;
+    }
+
+    /**
+     * @brief Update the primary feature decomposition of a feature
+     *
+     * @param pf_decomp The primary feature decomposition of the feature calling this function.
+     */
+    void update_primary_feature_decomp(std::map<int, int>& pf_decomp)
+    {
+        for(auto& feat : _feats)
+            feat->update_primary_feature_decomp(pf_decomp);
+    }
+
+    /**
+     * @brief Converts a feature into a postfix expression (reverse polish notation)
+     *
+     * @details Recursively creates a postfix representation of the string
+     *
+     * @param cur_expr The current expression
+     * @return The current postfix expression of the feature
+     */
+    inline void update_postfix(std::string& cur_expr)
+    {
+        cur_expr = get_postfix_term() + "|" + cur_expr;
+        for(int nn = N - 1; nn >= 0; --nn)
+            _feats[nn]->update_postfix(cur_expr);
+    };
+
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    virtual std::string get_postfix_term() = 0;
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/absolute_difference.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/absolute_difference.hpp
index faa4f5630c7265b5d8e4745661c7827e10e7ddd8..1f4de2a1afb21b881035ac9743c822c2e381cee1 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/absolute_difference.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/absolute_difference.hpp
@@ -113,6 +113,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::ABS_DIFF;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "abd";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/absolute_value.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/absolute_value.hpp
index 26c1107837982dc5592f5dd080f6b09197bcd80c..e8e9734f42c839ec14ed69a1d6357ef0b8deea29 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/absolute_value.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/absolute_value.hpp
@@ -111,6 +111,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::ABS;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "abs";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add.hpp
index 38a63e82f8e64daa4bc2246ef181e647a8bee966..5bbb668cb35d78eb50b5452d1b96c4facf3813b7 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/add.hpp
@@ -112,6 +112,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::ADD;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "add";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos.hpp
index b168913569714b3367febff99058e3adf6a3b344..90475353897d76816ff36b56112398a6a4173850 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cos.hpp
@@ -111,6 +111,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::COS;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "cos";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cube.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cube.hpp
index 4730e17985c55aa792af04a4c7b435d1e9042c73..6166868d127d7f0cde93c5757581e61a4c753c16 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cube.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cube.hpp
@@ -111,6 +111,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::CB;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "cb";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cube_root.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cube_root.hpp
index 5054cdd39c4dad4e290c60d1171af66ef845228d..41acf2cb8847301d5a42de770ac488a282fe3f2e 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cube_root.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/cube_root.hpp
@@ -111,6 +111,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::CBRT;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "cbrt";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/divide.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/divide.hpp
index 3d5dc138b8540474cc55dc16c7b188a29185f8b5..b7ba2d1eaf5f9472e396a894907d8ab291b4f146 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/divide.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/divide.hpp
@@ -112,6 +112,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::DIV;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "div";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exponential.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exponential.hpp
index b03ab0b143e77a99c1b4453efdb2ac3e80550fdd..8355ebc4390b4e20580eac45687bbf8ee0fd48af 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exponential.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/exponential.hpp
@@ -111,6 +111,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::EXP;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "exp";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inverse.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inverse.hpp
index 80735aa383fddd89f0a6504d3193caba391b18f5..60109dc4f9f49218d427953e10b51f191a2364a1 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inverse.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/inverse.hpp
@@ -99,6 +99,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::INV;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "inv";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log.hpp
index e59fd4dfcc8a91a6984f1780596b6b6f4fa4bb54..a3c4542d07412f584dc4ad44da7197b5333fd026 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/log.hpp
@@ -111,6 +111,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::LOG;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "log";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/multiply.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/multiply.hpp
index e58b0ce4a74e93efc27d7c3294b782d6a4ebd0e9..ba528c4302d4c175a3034f52f56f07a073ee411f 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/multiply.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/multiply.hpp
@@ -113,6 +113,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::MULT;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "mult";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/negative_exponential.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/negative_exponential.hpp
index 517f396fd2bae4c1fbaffdf6cc72b2766b925ef7..66c17bf1d771b6d12ae9f3c5ca4774928aa39734 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/negative_exponential.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/negative_exponential.hpp
@@ -112,6 +112,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::NEG_EXP;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "nexp";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin.hpp
index e06d55b66f539f34a3a2244584df22f1f2c88652..69f5147d0263c264a63dfd75b97baff0f99a0534 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sin.hpp
@@ -112,6 +112,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::SIN;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "sin";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sixth_power.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sixth_power.hpp
index e2e6ad8ca2736282247a4735359968da8aa17316..dda10de5ad8fe519c08c73d0915105f1bd39e3c4 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sixth_power.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/sixth_power.hpp
@@ -112,6 +112,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::SIX_POW;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "sp";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/square.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/square.hpp
index cf9fa6df0eaa4fd780e2ea051dfe2ee4dada52aa..b4044035ac9ad0c6e3129e3e625b2688373c90e6 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/square.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/square.hpp
@@ -111,6 +111,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::SQ;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "sq";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/square_root.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/square_root.hpp
index c1bcb8808ac6f03a38c11ec2d53102dbc0cd6127..cce30e4e932686fd0496dc8adc6c5b7c17f56492 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/square_root.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/square_root.hpp
@@ -112,6 +112,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::SQRT;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "sqrt";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/subtract.hpp b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/subtract.hpp
index 64b5e59be984d6677b54c88a93776f3f0ca3e779..246391a4cf314d12249937280efca8c818ddea31 100644
--- a/src/feature_creation/node/operator_nodes/allowed_operator_nodes/subtract.hpp
+++ b/src/feature_creation/node/operator_nodes/allowed_operator_nodes/subtract.hpp
@@ -113,6 +113,12 @@ public:
      */
     inline NODE_TYPE type(){return NODE_TYPE::SUB;}
 
+    /**
+     * @brief Get the string character representation of the node for the postfix expression
+     * @return the string representation of the node for the postfix expression
+     */
+    inline std::string get_postfix_term(){return "sub";}
+
     /**
      * @brief update the dictionary used to check if an Add/Sub node is valid
      *
diff --git a/src/feature_creation/node/utils.cpp b/src/feature_creation/node/utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ce5260798b82bc3d477183ad15e8b4e3bc7bd99
--- /dev/null
+++ b/src/feature_creation/node/utils.cpp
@@ -0,0 +1,112 @@
+#include <feature_creation/node/utils.hpp>
+
+node_ptr str2node::postfix2node(std::string postfix_expr, const std::vector<node_ptr>& phi_0, int feat_ind)
+{
+    std::vector<node_ptr> stack;
+    std::vector<std::string> postfix_split = str_utils::split_string_trim(postfix_expr, "|");
+    feat_ind += postfix_split.size() - 1;
+    for(int ff = 0; ff < postfix_split.size(); ++ff)
+    {
+        std::string term = postfix_split[ff];
+        try
+        {
+            stack.push_back(phi_0[std::stoi(term)]);
+            --feat_ind;
+        }
+        catch(const std::invalid_argument e)
+        {
+            if(term == "add")
+            {
+                stack[stack.size() - 2] = std::make_shared<AddNode>(stack[stack.size() - 2], stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+                stack.pop_back();
+            }
+            else if(term == "sub")
+            {
+                stack[stack.size() - 2] = std::make_shared<SubNode>(stack[stack.size() - 2], stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+                stack.pop_back();
+            }
+            else if(term == "abd")
+            {
+                stack[stack.size() - 2] = std::make_shared<AbsDiffNode>(stack[stack.size() - 2], stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+                stack.pop_back();
+            }
+            else if(term == "mult")
+            {
+                stack[stack.size() - 2] = std::make_shared<MultNode>(stack[stack.size() - 2], stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+                stack.pop_back();
+            }
+            else if(term == "div")
+            {
+                stack[stack.size() - 2] = std::make_shared<DivNode>(stack[stack.size() - 2], stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+                stack.pop_back();
+            }
+            else if(term == "abs")
+                stack[stack.size() - 1] = std::make_shared<AbsNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "inv")
+                stack[stack.size() - 1] = std::make_shared<InvNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "exp")
+                stack[stack.size() - 1] = std::make_shared<ExpNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "nexp")
+                stack[stack.size() - 1] = std::make_shared<NegExpNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "log")
+                stack[stack.size() - 1] = std::make_shared<LogNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "sin")
+                stack[stack.size() - 1] = std::make_shared<SinNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "cos")
+                stack[stack.size() - 1] = std::make_shared<CosNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "sq")
+                stack[stack.size() - 1] = std::make_shared<SqNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "sqrt")
+                stack[stack.size() - 1] = std::make_shared<SqrtNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "cb")
+                stack[stack.size() - 1] = std::make_shared<CbNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "cbrt")
+                stack[stack.size() - 1] = std::make_shared<CbrtNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else if(term == "sp")
+                stack[stack.size() - 1] = std::make_shared<SixPowNode>(stack[stack.size() - 1], feat_ind, 1e-50, 1e50);
+            else
+                throw std::logic_error("Term in postfix expression does not represent a node");
+            --feat_ind;
+        }
+    }
+    if(stack.size() != 1)
+        throw std::logic_error("Went through postfix expression and still more than one node in the list. This must be an invalid expression: " + postfix_expr + ".");
+    return stack[0];
+}
+
+std::vector<node_ptr> str2node::phi_selected_from_file(std::string filename, std::vector<node_ptr> phi_0)
+{
+    node_value_arrs::resize_values_arr(0, phi_0.size(), true);
+    node_value_arrs::initialize_d_matrix_arr();
+
+    std::ifstream file_stream;
+    file_stream.open(filename, std::ios::in);
+
+    std::string line;
+    std::vector<std::string> split_line;
+
+    std::vector<node_ptr> phi_selected;
+    int feat_ind = phi_0.size();
+    int feat_sel = 0;
+    std::getline(file_stream, line);
+
+    while(std::getline(file_stream, line))
+    {
+        if(line[0] == '#')
+            continue;
+
+        node_value_arrs::resize_d_matrix_arr(1);
+        boost::algorithm::split(split_line, line, boost::algorithm::is_any_of("\t "), boost::token_compress_on);
+
+        node_ptr new_feat = postfix2node(split_line[2], phi_0, feat_ind);
+
+        new_feat->set_selected(true);
+        new_feat->set_d_mat_ind(feat_sel);
+        new_feat->set_value();
+        phi_selected.push_back(std::make_shared<ModelNode>(feat_ind, new_feat->rung(), new_feat->expr(), new_feat->postfix_expr(), new_feat->value(), new_feat->test_value(), new_feat->unit()));
+        ++feat_ind;
+        ++feat_sel;
+    }
+    file_stream.close();
+    return phi_selected;
+}
diff --git a/src/feature_creation/node/utils.hpp b/src/feature_creation/node/utils.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8d7d4a39a0a0efa23834b87c4564e46aefc8f90
--- /dev/null
+++ b/src/feature_creation/node/utils.hpp
@@ -0,0 +1,43 @@
+/** @file feature_creation/node/utils.hpp
+ *  @brief utility functions to build node_ptrs from strings
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef NODE_UTILS
+#define NODE_UTILS
+
+#include <fstream>
+#include <feature_creation/node/FeatureNode.hpp>
+#include <feature_creation/node/ModelNode.hpp>
+#include <feature_creation/node/operator_nodes/allowed_ops.hpp>
+#include <feature_creation/node/value_storage/nodes_value_containers.hpp>
+
+#include <utils/string_utils.hpp>
+
+namespace str2node
+{
+    /**
+     * @brief Convert a postfix expression into a node_ptr
+     * @details Creates a stack to iteratively generate the feature represented by the expression
+     *
+     * @param postfix_expr The postfix expression of the feature node
+     * @param phi_0 The initial feature set
+     * @param feat_ind The desired feature index
+     * @return The feature node described by the postfix expression
+     */
+    node_ptr postfix2node(std::string postfix_expr, const std::vector<node_ptr>& phi_0, int feat_ind);
+
+    /**
+     * @brief Convert a feature_space/selected_features.txt into a phi_selected;
+     * @details Read in the file to get the postfix expressions and regenerate the selected features using phi_0
+     *
+     * @param filename The name of the feature_space/selected_features.txt file
+     * @param phi_0 The initial feature space
+     *
+     * @return The selected feature set from the file
+     */
+    std::vector<node_ptr> phi_selected_from_file(std::string filename, std::vector<node_ptr> phi_0);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/python/bindings_docstring_keyed.cpp b/src/python/bindings_docstring_keyed.cpp
index aa507ff7713eca566857c5041710fc744ca46753..c4f609b3949c0d5cbc461b9da11b58288402203a 100644
--- a/src/python/bindings_docstring_keyed.cpp
+++ b/src/python/bindings_docstring_keyed.cpp
@@ -30,6 +30,8 @@ void sisso::register_all()
     sisso::feature_creation::node::registerSqNode();
     sisso::feature_creation::node::registerSqrtNode();
     sisso::feature_creation::node::registerSixPowNode();
+
+    def("phi_selected_from_file", &str2node::phi_selected_from_file_py);
 }
 
 void sisso::feature_creation::registerFeatureSpace()
@@ -96,6 +98,8 @@ void sisso::feature_creation::node::registerNode()
         .add_property("d_mat_ind", &Node::d_mat_ind, &Node::set_d_mat_ind, "@DocString_node_set_d_mat_ind@")
         .add_property("value", &Node::value_py, "@DocString_node_value_py@")
         .add_property("test_value", &Node::test_value_py, "@DocString_node_test_value_py@")
+        .add_property("primary_feat_decomp", &Node::primary_feature_decomp_py, "@DocString_node_primary_feature_decomp@")
+        .add_property("postfix_expr", &Node::postfix_expr, "@DocString_node_postfix_expr@")
         .def("expr", pure_virtual(&Node::expr), "@DocString_node_expr@")
         .def("unit", pure_virtual(&Node::unit), "@DocString_node_unit@")
         .def("set_value", pure_virtual(&Node::set_value), "@DocString_node_set_value@")
@@ -131,7 +135,7 @@ void sisso::feature_creation::node::registerModelNode()
     std::string (ModelNode::*expr_const)() const = &ModelNode::expr;
 
     using namespace boost::python;
-    class_<ModelNode, bases<FeatureNode>>("ModelNode", init<int, int, std::string, std::vector<double>, std::vector<double>, Unit>())
+    class_<ModelNode, bases<FeatureNode>>("ModelNode", init<int, int, std::string, std::string, std::vector<double>, std::vector<double>, Unit>())
         .def("is_nan", &ModelNode::is_nan, "@DocString_model_node_is_nan@")
         .def("is_const", &ModelNode::is_const, "@DocString_model_node_is_const@")
         .def("set_value", &ModelNode::set_value, "@DocString_model_node_set_value@")
diff --git a/src/python/bindings_docstring_keyed.hpp b/src/python/bindings_docstring_keyed.hpp
index a68dc5e9319a390d77922bf2c662ec6517c7417b..e11568b8946ff41e22497b10a8af101e441dbd37 100644
--- a/src/python/bindings_docstring_keyed.hpp
+++ b/src/python/bindings_docstring_keyed.hpp
@@ -9,6 +9,7 @@
 
 #include <descriptor_identifier/SISSORegressor.hpp>
 #include <feature_creation/feature_space/FeatureSpace.hpp>
+#include <python/feature_creation/node_utils.hpp>
 
 namespace py = boost::python;
 namespace np = boost::python::numpy;
@@ -45,6 +46,10 @@ namespace sisso
                 inline bool is_const(){return this->get_override("is_const")();}
                 inline NODE_TYPE type(){return this->get_override("type")();}
                 inline int rung(int cur_rung = 0){return this->get_override("rung")();}
+                std::map<int, int> primary_feature_decomp(){return this->get_override("primary_feature_decomp")();}
+                void update_primary_feature_decomp(std::map<int, int>& pf_decomp){this->get_override("update_primary_feature_decomp")();}
+                void update_postfix(std::string& cur_expr){this->get_override("update_postfix")();}
+                std::string get_postfix_term(){return this->get_override("get_postfix_term")();}
                 inline void update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot){this->get_override("update_add_sub_leaves");}
                 inline void update_div_mult_leaves(std::map<std::string, double>& div_mult_leaves, double fact, double& expected_abs_tot){this->get_override("update_div_mult_leaves");}
             };
@@ -60,6 +65,7 @@ namespace sisso
                 inline int rung(int cur_rung = 0){return this->get_override("rung")();}
                 inline std::string expr(){return this->get_override("expr")();}
                 inline Unit unit(){return this->get_override("unit")();}
+                std::string get_postfix_term(){return this->get_override("get_postfix_term")();}
                 inline void update_add_sub_leaves(std::map<std::string, int>& add_sub_leaves, int pl_mn, int& expected_abs_tot){this->get_override("update_add_sub_leaves")();}
                 inline void update_div_mult_leaves(std::map<std::string, double>& div_mult_leaves, double fact, double& expected_abs_tot){this->get_override("update_div_mult_leaves")();}
             };
diff --git a/src/python/conversion_utils.hpp b/src/python/conversion_utils.hpp
index 2549ec8ce49be49ff7dfb56d4ddab1031e8a9837..cd82cf4398049b4bb43c5af2e9e138db08abea9c 100644
--- a/src/python/conversion_utils.hpp
+++ b/src/python/conversion_utils.hpp
@@ -102,6 +102,16 @@ namespace python_conv_utils
         std::copy_n(vec.data(), vec.size(), reinterpret_cast<T*>(arr.get_data()));
         return arr;
     }
+
+    template<typename key, typename val>
+    py::dict to_dict(std::map<key, val> map)
+    {
+        py::dict dct;
+        for(auto& iter : map)
+            dct[iter.first] = iter.second;
+
+        return dct;
+    }
 }
 
 #endif
\ No newline at end of file
diff --git a/src/python/feature_creation/FeatureSpace.cpp b/src/python/feature_creation/FeatureSpace.cpp
index 589bb14ab2360a60dd932228408abd2df007d459..73cbcd2ae398ed23e9d6a290afe4eea4ff0b24eb 100644
--- a/src/python/feature_creation/FeatureSpace.cpp
+++ b/src/python/feature_creation/FeatureSpace.cpp
@@ -76,6 +76,6 @@ py::list FeatureSpace::phi_selected_py()
 {
     py::list feat_lst;
     for(auto& feat : _phi_selected)
-        feat_lst.append<ModelNode>(ModelNode(feat->d_mat_ind(), feat->rung(), feat->expr(), feat->value(), feat->test_value(), feat->unit()));
+        feat_lst.append<ModelNode>(ModelNode(feat->d_mat_ind(), feat->rung(), feat->expr(), feat->postfix_expr(), feat->value(), feat->test_value(), feat->unit()));
     return feat_lst;
 }
diff --git a/src/python/feature_creation/node_utils.cpp b/src/python/feature_creation/node_utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ec2b1c637692da574787486dd0be86491cd6727c
--- /dev/null
+++ b/src/python/feature_creation/node_utils.cpp
@@ -0,0 +1,11 @@
+#include <python/feature_creation/node_utils.hpp>
+
+py::list str2node::phi_selected_from_file_py(std::string filename, py::list phi_0)
+{
+    std::vector<node_ptr> phi_selected = phi_selected_from_file(filename, python_conv_utils::shared_ptr_vec_from_list<Node, FeatureNode>(phi_0));
+
+    py::list feat_lst;
+    for(auto& feat : phi_selected)
+        feat_lst.append<ModelNode>(ModelNode(feat->d_mat_ind(), feat->rung(), feat->expr(), feat->postfix_expr(), feat->value(), feat->test_value(), feat->unit()));
+    return feat_lst;
+}
\ No newline at end of file
diff --git a/src/python/feature_creation/node_utils.hpp b/src/python/feature_creation/node_utils.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d3d7e9a5fa63721642587f70fa0b0279a4e1563
--- /dev/null
+++ b/src/python/feature_creation/node_utils.hpp
@@ -0,0 +1,28 @@
+/** @file python/feature_creation/node_utils.hpp
+ *  @brief python binding functions for node utilities
+ *
+ *  @author Thomas A. R. Purcell (tpurcell)
+ *  @bug No known bugs.
+ */
+#ifndef NODE_UTILS_PY
+#define NODE_UTILS_PY
+
+#include <feature_creation/node/utils.hpp>
+
+namespace py = boost::python;
+
+namespace str2node
+{
+    /**
+     * @brief Convert a feature_space/selected_features.txt into a phi_selected;
+     * @details Read in the file to get the postfix expressions and regenerate the selected features using phi_0
+     *
+     * @param filename The name of the feature_space/selected_features.txt file
+     * @param phi_0 The initial feature space
+     *
+     * @return The selected feature set from the file as a python file
+     */
+    py::list phi_selected_from_file_py(std::string filename, py::list phi_0);
+}
+
+#endif
\ No newline at end of file
diff --git a/test/sisso.json b/test/sisso.json
index 1011b74c95a1081d81df63ff5cec61742336affc..51cc8128c51a676c62d22cef7dbd14a0ac3d05f6 100644
--- a/test/sisso.json
+++ b/test/sisso.json
@@ -7,7 +7,7 @@
     "max_abs_feat_val": 1e5,
     "data_file": "data.csv",
     "property_key": "energy_diff",
-    "leave_out_frac": 0.05,
+    "leave_out_frac": 0.0,
     "n_rung_generate": 0,
     "n_rung_store": 1,
     "leave_out_inds": [],