@@ -1588,10 +1588,82 @@ def check_macro_cache(self, variable_name: str, period: str) -> bool:
15881588
15891589 return True
15901590
1591+ def get_input_variables (self , include_computed_variables : bool = True ) -> List [str ]:
1592+ """Return variable names stored as inputs on this simulation.
1593+
1594+ Args:
1595+ include_computed_variables: When ``True``, return the legacy
1596+ runtime list of variables with stored values. When ``False``,
1597+ return only structurally input variables that were populated
1598+ through ``set_input`` on the current branch.
1599+
1600+ Returns:
1601+ List[str]: Stored input variable names.
1602+ """
1603+ if include_computed_variables :
1604+ return list (self .input_variables )
1605+
1606+ return [
1607+ variable_name
1608+ for variable_name in self .tax_benefit_system .variables
1609+ if len (
1610+ self ._get_exportable_input_periods (
1611+ variable_name ,
1612+ include_computed_variables = False ,
1613+ )
1614+ )
1615+ > 0
1616+ ]
1617+
1618+ @property
1619+ def true_input_variables (self ) -> List [str ]:
1620+ """Stored variables that are safe to reload as source inputs."""
1621+ return self .get_input_variables (include_computed_variables = False )
1622+
1623+ def _is_exportable_input_variable (self , variable_name : str ) -> bool :
1624+ variable = self .tax_benefit_system .get_variable (variable_name )
1625+ return variable is not None and variable .is_input_variable ()
1626+
1627+ def _get_exportable_input_periods (
1628+ self ,
1629+ variable_name : str ,
1630+ include_computed_variables : bool ,
1631+ ) -> List [Period ]:
1632+ if include_computed_variables :
1633+ return self .get_holder (variable_name ).get_known_periods ()
1634+
1635+ if not self ._is_exportable_input_variable (variable_name ):
1636+ return []
1637+
1638+ user_input_periods = {
1639+ period
1640+ for input_variable_name , branch_name , period in getattr (
1641+ self , "_user_input_keys" , set ()
1642+ )
1643+ if input_variable_name == variable_name and branch_name == self .branch_name
1644+ }
1645+ if not user_input_periods :
1646+ return []
1647+ variable = self .tax_benefit_system .get_variable (variable_name )
1648+ holder = self .get_holder (variable_name )
1649+ if variable .definition_period == ETERNITY :
1650+ return holder .get_known_periods ()
1651+ known_periods = set (holder .get_known_periods ())
1652+ return sorted (user_input_periods & known_periods , key = str )
1653+
15911654 def to_input_dataframe (
15921655 self ,
1656+ include_computed_variables : bool = False ,
15931657 ) -> pd .DataFrame :
1594- """Exports a DataFrame which can be loaded back to a new Simulation to reproduce the same results.
1658+ """Exports a DataFrame that can be loaded back into a new Simulation.
1659+
1660+ By default, only structurally input variables populated through
1661+ ``set_input`` are exported. This avoids serializing pseudo-inputs and
1662+ stale calculated values that would override formulas when reloaded.
1663+
1664+ Args:
1665+ include_computed_variables: If ``True``, export every variable with
1666+ a known period, matching the historical unsafe behavior.
15951667
15961668 Returns:
15971669 pd.DataFrame: The DataFrame containing the input values.
@@ -1601,7 +1673,9 @@ def to_input_dataframe(
16011673
16021674 for variable in self .tax_benefit_system .variables :
16031675 variable_meta = self .tax_benefit_system .variables [variable ]
1604- for period in self .get_holder (variable ).get_known_periods ():
1676+ for period in self ._get_exportable_input_periods (
1677+ variable , include_computed_variables
1678+ ):
16051679 # Test if period matches entity definition period
16061680 if variable_meta .definition_period != period .unit :
16071681 continue
@@ -1611,8 +1685,16 @@ def to_input_dataframe(
16111685
16121686 return df
16131687
1614- def to_input_dict (self ) -> dict :
1615- """Exports a dictionary which can be loaded back to a new Simulation to reproduce the same results.
1688+ def to_input_dict (self , include_computed_variables : bool = False ) -> dict :
1689+ """Exports a dictionary that can be loaded back into a new Simulation.
1690+
1691+ By default, only structurally input variables populated through
1692+ ``set_input`` are exported. This avoids serializing pseudo-inputs and
1693+ stale calculated values that would override formulas when reloaded.
1694+
1695+ Args:
1696+ include_computed_variables: If ``True``, export every variable with
1697+ a known period, matching the historical unsafe behavior.
16161698
16171699 Returns:
16181700 dict: The dictionary containing the input values.
@@ -1621,7 +1703,9 @@ def to_input_dict(self) -> dict:
16211703
16221704 for variable in self .tax_benefit_system .variables :
16231705 data [variable ] = {}
1624- for period in self .get_holder (variable ).get_known_periods ():
1706+ for period in self ._get_exportable_input_periods (
1707+ variable , include_computed_variables
1708+ ):
16251709 values = self .calculate (variable , period , map_to = "person" )
16261710 if values is not None :
16271711 data [variable ][str (period )] = values .tolist ()
0 commit comments