Source code for VeraGridEngine.Utils.NumericalMethods.common

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.  
# SPDX-License-Identifier: MPL-2.0
from dataclasses import dataclass
from typing import Callable, Tuple
import numpy as np
import numba as nb
from matplotlib import pyplot as plt
from VeraGridEngine.basic_structures import Vec, CscMat, IntVec, CxVec


[docs] def check_function_and_args(func: Callable, args: Tuple, n_used_for_solver: int) -> bool: """ Checks if the number of supplied arguments matches the function signature :param func: Function pointer :param args: tuple of arguments to be passed before the mandatory arguments used by the numerical method :param n_used_for_solver: Number of mandatory arguments used by the numerical method :return: ok? """ n_args = func.__code__.co_argcount return n_args == n_used_for_solver + len(args)
[docs] @nb.njit(cache=True) def max_abs(x: Vec) -> float: """ Compute max abs efficiently :param x: :return: """ max_val = 0.0 for x_val in x: x_abs = abs(x_val) if x_abs > max_val: max_val = x_abs return max_val
[docs] @nb.njit(cache=True) def norm(x: Vec) -> float: """ Compute max abs efficiently :param x: :return: """ x_sum = 0.0 for x_val in x: x_sum += x_val * x_val return np.sqrt(x_sum)
[docs] def compute_L(h, f, J) -> float: """ 1/2 Β· ||f + J @ h|| :param h: some vector :param f: f vector :param J: Jacobian of f :return: """ v = f + J @ h return 0.5 * (v @ v)
[docs] @dataclass(slots=True) class ConvexFunctionResult: """ Result of the convex function evaluated iterativelly for a given method """ f: Vec # function increment of the equalities J: CscMat # Jacobian matrix
[docs] def compute_f_error(self): """ Compute the error of the increments g :return: max(abs(G)) """ return max_abs(self.f)
# return np.max(np.abs(self.f))
[docs] @dataclass(slots=True) class ConvexMethodResult: """ Iterative convex method result """ x: Vec # x solution error: float # method error converged: bool # converged? iterations: int # number of iterations elapsed: float # time elapsed in seconds error_evolution: Vec # array of errors to plot
[docs] def plot_error(self) -> None: """ Plot the IPS error """ plt.figure() plt.plot(self.error_evolution, ) plt.xlabel("Iterations") plt.ylabel("Error") plt.yscale('log') plt.show()
[docs] def print_info(self): """ Print information about the ConvexMethodResult :return: """ print("Iterations:\t", self.iterations) print("Converged:\t", self.converged) print("Error:\t", self.error) print("Elapsed:\t", self.elapsed, 's')
[docs] @nb.njit(cache=True) def find_closest_number(arr: Vec, target: float) -> Tuple[int, float]: """ Find the closest number that exists in array :param arr: Array to be searched (must be sorted from min to max) :param target: Value to search for :return: index in the array, Closes adjusted or truncated value """ if len(arr) == 0: # nothing to do return -1, target prev: float = arr[0] if target <= prev: return 0, prev last: float = arr[-1] if target >= last: return len(arr) - 1, last for i in range(1, len(arr)): val: float = arr[i] if val <= prev: # test that the values strictly increase raise Exception("The array must be monotonically increasing") if prev < target <= val: # the value is within the interval d_prev = target - prev d_post = val - target if abs(d_prev - d_post) < 1e-10: return i, val else: if d_prev < d_post: return i - 1, prev else: return i, val prev = val # if we reached here, something went wrong... return 0, arr[0]
[docs] @nb.njit(cache=True) def make_lookup(size: int, indices: IntVec) -> IntVec: """ Create a lookup array :param size: Size of the thing (i.e. number of buses) :param indices: indices to map (i.e. pq indices) :return: lookup array, -1 at the indices that do not match with the "indices" input array """ lookup = np.full(size, -1, dtype=np.int32) lookup[indices] = np.arange(len(indices), dtype=np.int32) return lookup
[docs] @nb.njit(cache=True) def make_complex(r: Vec, i: Vec) -> CxVec: """ Fastest way to create complex arrays :param r: :param i: :return: """ assert len(r) == len(i) res = np.empty(len(r), dtype=np.complex128) for k in range(len(r)): res[k] = complex(r[k], i[k]) return res