@@ -26,6 +26,7 @@ class Simulator:
2626 equations : list
2727 equations_sorted : list
2828 equations_graph : nx .DiGraph
29+ free_variables : list
2930
3031 def __init__ (self ):
3132 pass
@@ -46,52 +47,65 @@ def prepare_simulation(self, equations, concrete_model):
4647 equations_dict = {eq .name : eq for eq in equations }
4748 calc_dependencies (equations_dict , concrete_model )
4849 # Perform topological sort of equations based on dependencies
49- self .equations_sorted , self .equations_graph = sort_equations (
50- equations_dict , return_graph = True
50+ self .equations_sorted , self .equations_graph , self . free_variables = (
51+ sort_equations ( equations_dict , return_graph = True )
5152 )
5253
53- def run (
54- self ,
55- relative_abatement = None ,
56- simulation_model = None ,
57- ):
54+ def run (self , simulation_model = None , ** free_variables_kwargs ):
5855 """
5956 Runs MIMOSA as simulation.
6057
6158 It first sets the "free" variables (relative_abatement), then runs the simulation
6259
6360 Args:
64- relative_abatement (array of n_timesteps x n_regions):
65- Relative abatement values for each region and time step.
66- If None, it defaults to a zero abatement scenario.
61+ simulation_model (SimulationObjectModel): The simulation model to run.
62+ If None, a new SimulationObjectModel will be created.
63+ free_variables_kwargs: set of keyword arguments to optionally set free variables to a value. Can be:
64+ * None (equivalent to 0)
65+ * A float value: every region and time step will get this value
66+ * A numpy array of shape (n_timesteps, n_regions): each region and time step will get the corresponding value
6767 """
6868
69+ if simulation_model is None :
70+ # Create a SimulationObjectModel object that will be used to run the simulation
71+ simulation_model = SimulationObjectModel (self .concrete_model )
72+
6973 n_timesteps = len (self .concrete_model .t )
7074 n_regions = len (self .concrete_model .regions )
7175
72- # TODO: make dynamic list of "free" variables
73- # For now, we only have relative_abatement as a free variable
76+ # Check if there are no variables provided in kwargs that are not in the free variables:
77+ for var in free_variables_kwargs :
78+ if var not in self .free_variables :
79+ raise ValueError (
80+ f"Variable '{ var } ' is not a free variable. "
81+ f"Available free variables: { self .free_variables } "
82+ )
83+
84+ for var in self .free_variables :
85+ # Check if the variable is provided in the kwargs, otherwise set to None
86+ value = free_variables_kwargs .get (var , None )
87+
88+ if value is None :
89+ # If no relative abatement is provided, set it to zero
90+ value = 0
7491
75- if relative_abatement is None :
76- # If no relative abatement is provided, set it to zero
77- relative_abatement = np .zeros ((n_timesteps , n_regions ))
78- else :
92+ # TODO: check if variable is region/time dependent
7993 # First check if it is given as a dictionary with (time, region) keys
80- if isinstance (relative_abatement , dict ):
94+ if isinstance (value , dict ):
8195 # Convert the dictionary to a numpy array
82- relative_abatement = self ._dict_values_to_numpy (relative_abatement )
96+ value = self ._dict_values_to_numpy (value )
97+ # If it is a single float value, convert it to a numpy array
98+ elif isinstance (value , (int , float )):
99+ value = np .full ((n_timesteps , n_regions ), value )
100+
83101 # Check that the dimensions are correct
84- assert relative_abatement .shape == (n_timesteps , n_regions ), (
85- f"relative_abatement must be of shape (n_timesteps, n_regions), "
86- f"but is { relative_abatement .shape } ."
102+ assert value .shape == (n_timesteps , n_regions ), (
103+ f"{ var } must be of shape (n_timesteps, n_regions), "
104+ f"but is { value .shape } ."
87105 )
88106
89- if simulation_model is None :
90- # Create a SimulationObjectModel object that will be used to run the simulation
91- simulation_model = SimulationObjectModel (self .concrete_model )
92-
93- # Set the relative abatement for all regions and time periods
94- simulation_model .relative_abatement .values = relative_abatement
107+ # Set the relative abatement for all regions and time periods
108+ getattr (simulation_model , var ).values = value
95109
96110 self ._simulate (simulation_model )
97111
@@ -118,7 +132,7 @@ def f(x, objective_only=True):
118132 # Set global relative abatement (x) to regional abatement in simulation
119133 relative_abatement = x .reshape ((n_t , 1 )).repeat (n_r , axis = 1 )
120134 # Do simulation
121- self .run (relative_abatement , sim_m )
135+ self .run (sim_m , relative_abatement = relative_abatement )
122136 # Return the value to minimise (we try to maximise the final NPV)
123137 if objective_only :
124138 return - sim_m .NPV [n_t - 1 ]
0 commit comments