Source code for aixcalibuha.sensitivity_analysis.pawn

"""
Adds the PAWNAnalyzer to the available
classes of sensitivity analysis.
"""

from SALib.sample import sobol
from SALib.sample import morris
from SALib.sample import fast_sampler as fast
from SALib.analyze import pawn as analyze_pawn
import numpy as np
from aixcalibuha.sensitivity_analysis import SenAnalyzer
from aixcalibuha import CalibrationClass


[docs]class PAWNAnalyzer(SenAnalyzer): """ PAWN method from SALib https://salib.readthedocs.io/en/latest/api.html#pawn-sensitivity-analysis Density-based method which computes the PAWN index at 'min', 'max', 'mean', 'median' and coefficient of variation 'cv'. Additional arguments: :keyword bool calc_second_order: Default True, used for the sampler of the sobol-method :keyword int s: Default 10, used for the pawn-method. :keyword str sampler: Which sampler should be used. Default sobol. Choose between 'sobol', 'morris' and 'fast'. :keyword int num_levels: Default num_samples, used for the sampler of the morris-method. :keyword optimal_trajectories: Used for the sampler of the morris-method. :keyword bool local_optimization: Default True, used for the sampler of the morris-method. :keyword int M: Default 4, used for the sampler of the fast-method. """ def __init__(self, sim_api, **kwargs): super().__init__( sim_api=sim_api, **kwargs) # Set additional kwargs self.calc_second_order = kwargs.pop("calc_second_order", True) self.s = kwargs.pop("s", 10) self.sampler = kwargs.pop("sampler", 'sobol') self.num_levels = kwargs.pop("num_levels", self.num_samples) self.optimal_trajectories = kwargs.pop("optimal_trajectories", None) self.local_optimization = kwargs.pop("local_optimization", True) self.M = kwargs.pop("M", 4) @property def analysis_variables(self): """The analysis variables of the PAWN method""" return ['minimum', 'mean', 'median', 'maximum', 'CV']
[docs] def analysis_function(self, x, y): """ Use the SALib.analyze.pawn method to analyze the simulation results. :param np.array x: placeholder for the `X` parameter of the morris method not used for sobol :param np.array y: The NumPy array containing the model outputs :return: returns the result of the SALib.analyze.pawn method (from the documentation: This implementation reports the PAWN index at the min, mean, median, and max across the slides/conditioning intervals as well as the coefficient of variation (``CV``). The median value is the typically reported value. As the ``CV`` is (standard deviation / mean), it indicates the level of variability across the slides, with values closer to zero indicating lower variation.) """ return analyze_pawn.analyze(self.problem, x, y, S=self.s)
[docs] def create_sampler_demand(self): """ Function to create the sampler parameters for the sampler method """ if self.sampler == 'sobol': return {'calc_second_order': self.calc_second_order} if self.sampler == 'morris': return {'num_levels': self.num_levels, 'optimal_trajectories': self.optimal_trajectories, 'local_optimization': self.local_optimization} if self.sampler == 'fast': return {'M': self.M} raise NotImplementedError(f'{self.sampler} is not implemented yet')
[docs] def generate_samples(self): """ Run the sampler for the selected sampler and return the results. :return: The list of samples generated as a NumPy array with one row per sample and each row containing one value for each variable name in `problem['names']`. :rtype: np.ndarray """ if self.sampler == 'sobol': return sobol.sample(self.problem, N=self.num_samples, **self.create_sampler_demand()) if self.sampler == 'morris': return morris.sample(self.problem, N=self.num_samples, **self.create_sampler_demand()) if self.sampler == 'fast': return fast.sample(self.problem, N=self.num_samples, **self.create_sampler_demand()) raise NotImplementedError(f'{self.sampler} is not implemented yet')
def _get_res_dict(self, result: dict, cal_class: CalibrationClass, analysis_variable: str): """ Convert the result object to a dict with the key being the variable name and the value being the result associated to self.analysis_variable. """ if result is None: names = cal_class.tuner_paras.get_names() return dict(zip(names, np.zeros(len(names)))) return dict(zip(result['names'], result[analysis_variable]))