diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e43c01 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +/__pycache__ diff --git a/.vscode/launch.json b/.vscode/launch.json index 411a02c..8c52285 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,60 +1,162 @@ { "version": "0.2.0", "configurations": [ - { - "name": "Test_00", // Name of the current test - "type": "debugpy", - "request": "launch", - "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) - "console": "integratedTerminal", - "args": ["--input-reactor", - "${workspaceFolder}/Reactors/R0.json", // Path to the reactor's JSON definition - "--gamma", - "0.9", // Discount factor employed in the MDP - "--random-seed", - "42"] // Random seed for the experiment - }, + { + "name": "Test_00", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/R0.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, - { - "name": "Test_01", // Name of the current test - "type": "debugpy", - "request": "launch", - "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) - "console": "integratedTerminal", - "args": ["--input-reactor", - "${workspaceFolder}/Reactors/R1.json", // Path to the reactor's JSON definition - "--gamma", - "0.9", // Discount factor employed in the MDP - "--random-seed", - "42"] // Random seed for the experiment - }, + { + "name": "Test_01", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/R1.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, - { - "name": "Test_02", // Name of the current test - "type": "debugpy", - "request": "launch", - "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) - "console": "integratedTerminal", - "args": ["--input-reactor", - "${workspaceFolder}/Reactors/R2.json", // Path to the reactor's JSON definition - "--gamma", - "0.9", // Discount factor employed in the MDP - "--random-seed", - "42"] // Random seed for the experiment - }, + { + "name": "Test_02", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/R2.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, - { - "name": "Test_03", // Name of the current test - "type": "debugpy", - "request": "launch", - "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) - "console": "integratedTerminal", - "args": ["--input-reactor", - "${workspaceFolder}/Reactors/R3.json", // Path to the reactor's JSON definition - "--gamma", - "0.9", // Discount factor employed in the MDP - "--random-seed", - "42"] // Random seed for the experiment - } + { + "name": "Test_03", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/R3.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, + + { + "name": "Custom_Test_1", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/Reactor_1.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, + + { + "name": "Custom_Test_2", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/Reactor_2.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, + + { + "name": "Custom_Test_3", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/Reactor_3.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, + + { + "name": "Custom_Test_4", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/Reactor_4.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, + { + "name": "Custom_Test_5", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/Reactor_5.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + }, + { + "name": "Custom_Test_6", // Name of the current test + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", // Main Python file (do not modify) + "console": "integratedTerminal", + "args": [ + "--input-reactor", + "${workspaceFolder}/Reactors/Reactor_6.json", // Path to the reactor's JSON definition + "--gamma", + "0.9", // Discount factor employed in the MDP + "--random-seed", + "42" + ] // Random seed for the experiment + } ] } \ No newline at end of file diff --git a/ControlModule.py b/ControlModule.py index ebe085c..2fe1e76 100644 --- a/ControlModule.py +++ b/ControlModule.py @@ -8,43 +8,122 @@ def __init__(self): """Dummy constructor to use the Python Class as a namespace""" pass - @staticmethod # Hace que la función no necesite un argumento - def generate_P(probs) -> np.ndarray: + @staticmethod + def generate_P( + probs: np.ndarray, n_states: np.int32 = 100, n_actions: np.int32 = 3 + ) -> np.ndarray: """Function that generates the probabilities (transition) matrix""" - ### TO BE COMPLETED BY THE STUDENTS ### - # 3x3 o 3x3x3 o 100x100x3 - matriz_P = np.zeros((3, 10, 10), dtype=np.float64) + # Initialization of the Probability matrix + matrix_P = np.zeros((3, n_states, n_states), dtype=np.float64) + + probs_decrease = probs[0] probs_maintain = probs[1] - print(probs_maintain) - - for i in range(10): - for j in range(10): - if i == j: - matriz_P[1][i][j] = probs_maintain[1] - elif i + 1 == j: - matriz_P[1][i][j] = probs_maintain[2] - elif i-1 == j: - matriz_P[1][i][j] = probs_maintain[0] - + probs_increase = probs[2] + + # ---------------- DECREASE ---------------- + for initial_state in range(n_states): + for final_state in range(n_states): + if initial_state == final_state: + matrix_P[0][initial_state][final_state] = probs_decrease[2] + elif initial_state - 1 == final_state: + matrix_P[0][initial_state][final_state] = probs_decrease[1] + elif initial_state - 2 == final_state: + matrix_P[0][initial_state][final_state] = probs_decrease[0] + + # ---------------- MAINTAIN ---------------- + for initial_state in range(n_states): + for final_state in range(n_states): + if initial_state == final_state: + matrix_P[1][initial_state][final_state] = probs_maintain[1] + elif initial_state + 1 == final_state: + matrix_P[1][initial_state][final_state] = probs_maintain[2] + elif initial_state - 1 == final_state: + matrix_P[1][initial_state][final_state] = probs_maintain[0] + + # ---------------- INCREASE ---------------- + for initial_state in range(n_states): + for final_state in range(n_states): + if initial_state == final_state: + matrix_P[2][initial_state][final_state] = probs_increase[0] + elif initial_state + 1 == final_state: + matrix_P[2][initial_state][final_state] = probs_increase[1] + elif initial_state + 2 == final_state: + matrix_P[2][initial_state][final_state] = probs_increase[2] + # ---------------- EDGE CORRECTION ---------------- + # We count how many non-zero elements are to normalize the whole line + # Returns the index of the position where the prob is not null + for action in range(n_actions): + # Transposing the matrix + for initial_state in range(n_states): + non_zero_elts = np.where(matrix_P[action, initial_state, :] != 0)[0] + # We divide all elements by the sum of the total probabilities + if len(non_zero_elts) > 0: + alpha_val = 0 + for idx in non_zero_elts: + alpha_val += matrix_P[action][initial_state][idx] - print("Probabilidades:\n", matriz_P.shape) - print(matriz_P) - return matriz_P - ... + matrix_P[action][initial_state] *= 1 / alpha_val + # Adding an average probability if all elts are null + if len(non_zero_elts) == 0: + for end_state in range(n_states): + matrix_P[action][initial_state][end_state] = 1 / n_states + + return matrix_P @staticmethod - def generate_R() -> np.ndarray: + def generate_R(demand_t: np.float64, n_states: np.int32 = 100) -> np.ndarray: """Function that generates the rewards (costs) matrix""" - ### TO BE COMPLETED BY THE STUDENTS ### - ... + # Initialization of the Rewards matrix + matrix_R = np.zeros((3, n_states, n_states), dtype=np.float64) + # Method explained at the memory of the project + # ---------------- DECREASE ---------------- + for estado_inicial in range(n_states): + for estado_final in range(n_states): + delta_t = demand_t - (estado_final / n_states) + coste = abs(delta_t) + + if demand_t > (estado_inicial / n_states): + coste *= 2 + + matrix_R[0][estado_inicial][estado_final] = -coste + + # ---------------- MAINTAIN ---------------- + for estado_inicial in range(n_states): + for estado_final in range(n_states): + delta_t = demand_t - (estado_final / n_states) + coste = abs(delta_t) + + matrix_R[1][estado_inicial][estado_final] = -coste + + # ---------------- INCREASE ---------------- + for estado_inicial in range(n_states): + for estado_final in range(n_states): + delta_t = demand_t - (estado_final / n_states) + coste = abs(delta_t) + + if demand_t < (estado_inicial / n_states): + coste *= 2 + + matrix_R[2][estado_inicial][estado_final] = -coste + + return matrix_R @staticmethod - def control_iteration() -> np.int32: + def control_iteration(P, R, estado_actual, gamma, max_iter=1000) -> np.int32: """Function that computes one control-iteration""" - ### TO BE COMPLETED BY THE STUDENTS ### - ... + mdp = mdptoolbox.mdp.ValueIteration( + transitions=P, + reward=R, + discount=gamma, + max_iter=max_iter, + ) + + # Running the algorithm to converge to a single best action + mdp.run() + + return np.int32(mdp.policy[estado_actual]) @staticmethod def control_loop( @@ -55,8 +134,48 @@ def control_loop( gamma: np.float64, ) -> np.ndarray: """Function that computes all the required iterations (control-loop) to satisfy the power demand""" - ### TO BE COMPLETED BY THE STUDENTS ### + # Array of zeros of size 'demand' + response = np.zeros_like(a=demand, dtype=np.float64) + current_state = np.int32(0) + + # Matrix with the positions each action can apply + action_deltas = [ + np.array([-2, -1, 0], dtype=np.int32), + np.array([-1, 0, 1], dtype=np.int32), + np.array([0, 1, 2], dtype=np.int32), + ] + + P = ControlModule.generate_P(probs, n_states, n_actions) + + for t in range(demand.shape[0]): + + # Generation of the matrix R + R = ControlModule.generate_R(demand[t], n_states) + + # Calculation of the best action + action = ControlModule.control_iteration( + P=P, + R=R, + estado_actual=current_state, + gamma=gamma, + ) + + # Normalizing probs array + sum = np.sum(probs[action]) + if sum == 0: + for i in range(len(probs[action])): + probs[action][i] = 1 / sum + else: + probs[action] /= sum + + # Execution of the action (taking account uncertainty) + state_increment = np.random.choice(a=action_deltas[action], p=probs[action]) + current_state = current_state + state_increment + + # The current state must be within the borders + current_state = max(0, min(n_states - 1, current_state)) + + # We add the next state to the array to return it + response[t] = current_state / n_states - ### DUMMY BEHAVIOUR TO PREVENT CRASHING (MUST BE DELETED AFTER THE FULL IMPLEMENTATION) ### - return np.zeros_like(a=demand, dtype=np.float64) - ### ### + return response diff --git a/Metrics.py b/Metrics.py index 2515656..e31c215 100644 --- a/Metrics.py +++ b/Metrics.py @@ -3,32 +3,23 @@ def MAE(y_true: np.ndarray, y_pred: np.ndarray) -> np.float64: """ Implementation of the Mean Absolute Error (MAE) """ - ### TO BE COMPLETED BY THE STUDENTS ### - - ### DUMMY BEHAVIOUR TO PREVENT CRASHING (MUST BE DELETED AFTER THE FULL IMPLEMENTATION) ### - return 0.0 - ### ### + return np.float64((np.sum(abs(y_pred - y_true)))/len(y_pred)) + # return 0.0 DUMMY def MSE(y_true: np.ndarray, y_pred: np.ndarray) -> np.float64: """ Implementation of the Mean Squared Error (MSE) """ - ### TO BE COMPLETED BY THE STUDENTS ### - - ### DUMMY BEHAVIOUR TO PREVENT CRASHING (MUST BE DELETED AFTER THE FULL IMPLEMENTATION) ### - return 0.0 - ### ### + return np.float64(np.sum(abs((y_pred - y_true)**2))/len(y_pred)) + # return 0.0 DUMMY def R2(y_true: np.ndarray, y_pred: np.ndarray) -> np.float64: """ Implementation of the R2 metric """ - ### TO BE COMPLETED BY THE STUDENTS ### - - ### DUMMY BEHAVIOUR TO PREVENT CRASHING (MUST BE DELETED AFTER THE FULL IMPLEMENTATION) ### - return 0.0 - ### ### + return np.float64(1-(np.sum((y_true - y_pred)**2)/np.sum((y_true - np.mean(y_true))**2))) + # return 0.0 DUMMY def Corr(y_true: np.ndarray, y_pred: np.ndarray) -> np.float64: - """ Implementation of the Pearson's Correlation Coefficient """ - ### TO BE COMPLETED BY THE STUDENTS ### - - ### DUMMY BEHAVIOUR TO PREVENT CRASHING (MUST BE DELETED AFTER THE FULL IMPLEMENTATION) ### - return 0.0 - ### ### + """Implementation of the Pearson's Correlation Coefficient""" + + if np.std(y_true) == 0 or np.std(y_pred) == 0: + return np.float64(0.0) + + return np.float64(np.corrcoef(y_true, y_pred)[0, 1]) \ No newline at end of file diff --git a/Reactor.py b/Reactor.py index 2fcfb3a..874505a 100644 --- a/Reactor.py +++ b/Reactor.py @@ -1,50 +1,65 @@ # Import required dependencies import numpy as np + class Reactor: - def __init__(self, - model: str, - effective_section: np.float64, - neutron_flux: np.float64, - core_volume: np.float64, - fision_energy: np.float64, - probabilities: dict): - """ Constructor of the Reactor class """ - self.model = model + def __init__( + self, + model: str, + effective_section: np.float64, + neutron_flux: np.float64, + core_volume: np.float64, + fision_energy: np.float64, + probabilities: dict, + ): + """Constructor of the Reactor class""" + self.model = model self.effective_section = effective_section - self.neutron_flux = neutron_flux - self.core_volume = core_volume - self.fision_energy = fision_energy - self.probabilities = probabilities - self.max_power = self.compute_max_power() - self.k = self.compute_k() + self.neutron_flux = neutron_flux + self.core_volume = core_volume + self.fision_energy = fision_energy + self.probabilities = probabilities + self.max_power = self.compute_max_power() + self.k = self.compute_k() def __str__(self) -> str: - """ Overloading of the native __str__ function to print the class instances """ - _str = f"Model: {self.model}\n" + """Overloading of the native __str__ function to print the class instances""" + _str = f"Model: {self.model}\n" _str += f"Effective section: {self.effective_section} cm^-1\n" _str += f"Neutron flux: {self.neutron_flux} neutrons / (cm^2 · s)\n" _str += f"Core volume: {self.core_volume} cm^3\n" _str += f"Fision energy: {self.fision_energy} J\n" - _str += f"Probabilities: {self.probabilities}" + _str += f"Probabilities: {self.probabilities}\n" return _str def compute_max_power(self) -> np.float64: - """ Computes the maximum power of a reactor based on its physical features """ - ### TO BE COMPLETED BY THE STUDENTS ### - ... - + """Computes the maximum power of a reactor based on its physical features""" + # Returns the max power of the reactor (Equation 1) + return np.float64( + self.effective_section + * self.neutron_flux + * self.core_volume + * self.fision_energy + ) + def compute_k(self) -> np.float64: - """ Computes the value of the k-constant """ - ### TO BE COMPLETED BY THE STUDENTS ### - ... - + """Computes the value of the k-constant""" + self.P_max = self.compute_max_power() + # Applies the formula for 'k' and returns it + return -np.log(10**-6 / self.P_max) + def compute_power(self, control_bars_insertion: np.float64) -> np.float64: - """ Computes the power delivered (%) by the reactor based on the % of control-bars inserted """ - ### TO BE COMPLETED BY THE STUDENTS ### - ... - + """Computes the power delivered (%) by the reactor based on the % of control-bars inserted""" + k = self.compute_k() + # Applies the function to compute the power and returns it (Equation 2) + return np.float64(self.P_max * (np.e ** (-k * control_bars_insertion))) + def compute_control_bars_insertion(self, power: np.float64) -> np.float64: - """ Computes the % of controls-bars inserted based on the % of power delivered by the reactor """ - ### TO BE COMPLETED BY THE STUDENTS ### - ... + """Computes the % of controls-bars inserted based on the % of power delivered by the reactor""" + min_power = 10**-6 / self.max_power + # np.clip() forces 'power' to stay within the limits [min_power, 1.0] + power = np.clip(a=power, a_min=min_power, a_max=1.0) + + # Returns the percentage of bars to be inserted (B, inverse Equation 2) + insertion = -np.log(power) / self.k + return np.float64(np.clip(a=insertion, a_min=0.0, a_max=1.0)) diff --git a/Reactors/Reactor_1.json b/Reactors/Reactor_1.json new file mode 100644 index 0000000..810dbea --- /dev/null +++ b/Reactors/Reactor_1.json @@ -0,0 +1,24 @@ +{ + "model": "UP-REACTOR", + "effective_section": 17.6, + "neutron_flux": 5e6, + "core_volume": 9.42e6, + "fision_energy": 3.2e-12, + "probabilities": { + "decrease": [ + 0.34, + 0.33, + 0.33 + ], + "maintain": [ + 0.05, + 0.6, + 0.35 + ], + "increase": [ + 0.05, + 0.9, + 0.05 + ] + } +} \ No newline at end of file diff --git a/Reactors/Reactor_2.json b/Reactors/Reactor_2.json new file mode 100644 index 0000000..3c62d63 --- /dev/null +++ b/Reactors/Reactor_2.json @@ -0,0 +1,24 @@ +{ + "model": "BAD-REACTOR", + "effective_section": 5, + "neutron_flux": 5e13, + "core_volume": 9.42e6, + "fision_energy": 3.2e-11, + "probabilities": { + "decrease": [ + 0, + 0.33, + 0 + ], + "maintain": [ + 0.05, + 0.6, + 0.35 + ], + "increase": [ + 0.05, + 0.9, + 0.05 + ] + } +} \ No newline at end of file diff --git a/Reactors/Reactor_3.json b/Reactors/Reactor_3.json new file mode 100644 index 0000000..ad91be1 --- /dev/null +++ b/Reactors/Reactor_3.json @@ -0,0 +1,24 @@ +{ + "model": "AVERAGE-REACTOR", + "effective_section": 5, + "neutron_flux": 5e13, + "core_volume": 9.42e6, + "fision_energy": 3.2e-11, + "probabilities": { + "decrease": [ + 1, + 1, + 1 + ], + "maintain": [ + 1, + 1, + 1 + ], + "increase": [ + 1, + 1, + 1 + ] + } +} \ No newline at end of file diff --git a/Reactors/Reactor_4.json b/Reactors/Reactor_4.json new file mode 100644 index 0000000..a23f36d --- /dev/null +++ b/Reactors/Reactor_4.json @@ -0,0 +1,24 @@ +{ + "model": "ALWAYS-MAINTAIN", + "effective_section": 5, + "neutron_flux": 5e13, + "core_volume": 9.42e6, + "fision_energy": 3.2e-11, + "probabilities": { + "decrease": [ + 0.1, + 0.1, + 0.8 + ], + "maintain": [ + 0.05, + 0.9, + 0.05 + ], + "increase": [ + 0.7, + 0.15, + 0.1 + ] + } +} \ No newline at end of file diff --git a/Reactors/Reactor_5.json b/Reactors/Reactor_5.json new file mode 100644 index 0000000..62d9711 --- /dev/null +++ b/Reactors/Reactor_5.json @@ -0,0 +1,24 @@ +{ + "model": "IDEAL-REACTOR", + "effective_section": 5, + "neutron_flux": 5e13, + "core_volume": 9.42e6, + "fision_energy": 3.2e-11, + "probabilities": { + "decrease": [ + 0, + 1, + 0 + ], + "maintain": [ + 0, + 1, + 0 + ], + "increase": [ + 0, + 1, + 0 + ] + } +} \ No newline at end of file diff --git a/Reactors/Reactor_6.json b/Reactors/Reactor_6.json new file mode 100644 index 0000000..5aa6ce8 --- /dev/null +++ b/Reactors/Reactor_6.json @@ -0,0 +1,24 @@ +{ + "model": "USUALLY-DOWN", + "effective_section": 1000, + "neutron_flux": 5e23, + "core_volume": 9.42e16, + "fision_energy": 3.2e-5, + "probabilities": { + "decrease": [ + 0.4, + 0.6, + 0 + ], + "maintain": [ + 0.4, + 0.6, + 0 + ], + "increase": [ + 0.6, + 0.4, + 0 + ] + } +} \ No newline at end of file diff --git a/__pycache__/ControlModule.cpython-312.pyc b/__pycache__/ControlModule.cpython-312.pyc index 5d3bcb1..cfc526d 100644 Binary files a/__pycache__/ControlModule.cpython-312.pyc and b/__pycache__/ControlModule.cpython-312.pyc differ diff --git a/__pycache__/ControlModule.cpython-313.pyc b/__pycache__/ControlModule.cpython-313.pyc index 8788832..43629b4 100644 Binary files a/__pycache__/ControlModule.cpython-313.pyc and b/__pycache__/ControlModule.cpython-313.pyc differ diff --git a/__pycache__/Reactor.cpython-312.pyc b/__pycache__/Reactor.cpython-312.pyc index 5544cfa..6b6c8b8 100644 Binary files a/__pycache__/Reactor.cpython-312.pyc and b/__pycache__/Reactor.cpython-312.pyc differ diff --git a/main.py b/main.py index 94ed703..d3eb90b 100644 --- a/main.py +++ b/main.py @@ -8,39 +8,49 @@ from Metrics import * from Plotter import * -def get_args() -> tuple[Reactor, np.float64, np.float64]: # 3 salidas (?) + +def get_args() -> tuple[Reactor, np.float64, np.float64]: # Define the parser object parser = argparse.ArgumentParser() # Define the expected arguments to parse and their data types - parser.add_argument("--input-reactor", "-i", type=str, help="Path of the reactor's JSON file") - parser.add_argument("--gamma", "-g", type=float, help="Discount factor used in the MDP") - parser.add_argument("--random-seed", "-r", type=int, help="Pseudo-random number generator seed") + parser.add_argument( + "--input-reactor", "-i", type=str, help="Path of the reactor's JSON file" + ) + parser.add_argument( + "--gamma", "-g", type=float, help="Discount factor used in the MDP" + ) + parser.add_argument( + "--random-seed", "-r", type=int, help="Pseudo-random number generator seed" + ) # Parse the arguments args = parser.parse_args() - + # Some verbose to check the correct parsing of the input arguments print(f"Loading reactor from file: {args.input_reactor}") print(f"Using gamma (discount factor): {args.gamma}") print(f"Using {args.random_seed} as random seed") # Build the Reactor object by reading the reactor's JSON file - with open(args.input_reactor, 'r', encoding='utf-8') as file: + with open(args.input_reactor, "r", encoding="utf-8") as file: json_data = json.load(fp=file) - reactor = Reactor(model=json_data['model'], - effective_section=float(json_data['effective_section']), - neutron_flux=float(json_data['neutron_flux']), - core_volume=float(json_data['core_volume']), - fision_energy=float(json_data['fision_energy']), - probabilities=dict(json_data['probabilities'])) - + reactor = Reactor( + model=json_data["model"], + effective_section=float(json_data["effective_section"]), + neutron_flux=float(json_data["neutron_flux"]), + core_volume=float(json_data["core_volume"]), + fision_energy=float(json_data["fision_energy"]), + probabilities=dict(json_data["probabilities"]), + ) + # Some verbose of the reactor loaded - print(reactor) # Overloaded in the __str__ method of Reactor's class - + print(reactor) + # Return the Reactor object, gamma and the random seed return reactor, args.gamma, args.random_seed + def main() -> None: # Parse the main arguments reactor, gamma, random_seed = get_args() @@ -49,30 +59,34 @@ def main() -> None: np.random.seed(random_seed) # Get the probabilities from the reactor's dynamics - probs = np.array([reactor.probabilities['decrease'], - reactor.probabilities['maintain'], - reactor.probabilities['increase']], dtype=np.float64) - - matriz_P = ControlModule.generate_P(probs) + probs = np.array( + [ + reactor.probabilities["decrease"], + reactor.probabilities["maintain"], + reactor.probabilities["increase"], + ], + dtype=np.float64, + ) # Make a radar-plot with the reactor probabilities plot_reactor_as_radar(probs=probs) - + # Generate a random power demand demand = generate_demand(n_samples=512) - # Define the number of MDP's states, actions and the discount factor (gamma) - n_states = 100 + # Define the number of MDP's states and actions + n_states = 100 n_actions = 3 - # Get the response time-series (answer to the demand time-series) - response = ControlModule.control_loop(demand=demand, - probs=probs, - n_states=n_states, - n_actions=n_actions, - gamma=gamma) - + response = ControlModule.control_loop( + demand=demand, + probs=probs, + n_states=n_states, + n_actions=n_actions, + gamma=gamma, + ) + # Plot the original power demand plot_demand(demand=demand) @@ -86,9 +100,9 @@ def main() -> None: plot_correlation(demand=demand, response=response) # Print the four regression metrics for the current demand-response data - _MAE = MAE(y_true=demand, y_pred=response) - _MSE = MSE(y_true=demand, y_pred=response) - _R2 = R2(y_true=demand, y_pred=response) + _MAE = MAE(y_true=demand, y_pred=response) + _MSE = MSE(y_true=demand, y_pred=response) + _R2 = R2(y_true=demand, y_pred=response) _Corr = Corr(y_true=demand, y_pred=response) print(f"MAE={_MAE:.6f}") print(f"MSE={_MSE:.6f}") @@ -101,5 +115,6 @@ def main() -> None: # Plot the R2 and the Corr in a bar-plot plot_r2_and_pearson(R2=_R2, Pearson=_Corr) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/mdp-reactor-nuclear.zip b/mdp-reactor-nuclear.zip new file mode 100644 index 0000000..0a0e399 Binary files /dev/null and b/mdp-reactor-nuclear.zip differ