Source code for climada.engine.uncertainty.base

"""
This file is part of CLIMADA.

Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.

CLIMADA is free software: you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free
Software Foundation, version 3.

CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.

---

Define Uncertainty class.
"""

__all__ = ['UncVar', 'Uncertainty']

import logging
import json

from datetime import datetime as dt
from itertools import zip_longest
from pathlib import Path

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from climada.util.value_representation import value_to_monetary_unit as u_vtm
from climada.util.value_representation import sig_dig as u_sig_dig
from climada import CONFIG

LOGGER = logging.getLogger(__name__)

# Metrics that are multi-dimensional
METRICS_2D = ['eai_exp', 'at_event']

DATA_DIR = CONFIG.engine.uncertainty.local_data.user_data.dir()

FIG_W, FIG_H = 5, 6 #default figize width/heigh column/work multiplicators


[docs]class UncVar(): """ Uncertainty variable An uncertainty variable requires a single or multi-parameter function. The parameters must follow a given distribution. Attributes ---------- distr_dict : dict Distribution of the uncertainty parameters. Keys are uncertainty parameters names and Values are probability density distribution from scipy.stats package https://docs.scipy.org/doc/scipy/reference/stats.html labels : list Names of the uncertainty parameters (keys of distr_dict) uncvar_func : function User defined python fucntion with the uncertainty parameters as kwargs and which returns a climada object. Examples -------- Categorical variable function: LitPop exposures with m,n exponents in [0,5] import scipy as sp def litpop_cat(m, n): exp = Litpop() exp.set_country('CHE', exponent=[m, n]) return exp distr_dict = { 'm': sp.stats.randint(low=0, high=5), 'n': sp.stats.randint(low=0, high=5) } unc_var_cat = UncVar(uncvar_func=litpop_cat, distr_dict=distr_dict) Continuous variable function: Impact function for TC import scipy as sp def imp_fun_tc(G, v_half, vmin, k, _id=1): imp_fun = ImpactFunc() imp_fun.haz_type = 'TC' imp_fun.id = _id imp_fun.intensity_unit = 'm/s' imp_fun.intensity = np.linspace(0, 150, num=100) imp_fun.mdd = np.repeat(1, len(imp_fun.intensity)) imp_fun.paa = np.array([sigmoid_function(v, G, v_half, vmin, k) for v in imp_fun.intensity]) imp_fun.check() impf_set = ImpactFuncSet() impf_set.append(imp_fun) return impf_set distr_dict = {"G": sp.stats.uniform(0.8, 1), "v_half": sp.stats.uniform(50, 100), "vmin": sp.stats.norm(loc=15, scale=30), "k": sp.stats.randint(low=1, high=9) } unc_var_cont = UncVar(uncvar_func=imp_fun_tc, distr_dict=distr_dict) """
[docs] def __init__(self, uncvar_func, distr_dict): """ Initialize UncVar Parameters ---------- uncvar_func : function Variable defined as a function of the uncertainty parameters distr_dict : dict Dictionary of the probability density distributions of the uncertainty parameters, with keys matching the keyword arguments (i.e. uncertainty parameters) of the uncvar_func function. The distribution must be of type scipy.stats https://docs.scipy.org/doc/scipy/reference/stats.html Returns ------- None. """ self.labels = list(distr_dict.keys()) self.distr_dict = distr_dict self.uncvar_func = uncvar_func
[docs] def plot(self, figsize=None): """ Plot the distributions of the parameters of the uncertainty variable. Parameters ---------- figsize: tuple(int or float, int or float), optional The figsize argument of matplotlib.pyplot.subplots() The default is derived from the total number of plots (nplots) as: nrows, ncols = int(np.ceil(nplots / 3)), min(nplots, 3) figsize = (ncols * FIG_W, nrows * FIG_H) Returns ------- axes: matplotlib.pyplot.figure, matplotlib.pyplot.axes The figure and axes handle of the plot. """ nplots = len(self.distr_dict) nrows, ncols = int(np.ceil(nplots / 3)), min(nplots, 3) if figsize is None: figsize = (ncols * FIG_W, nrows * FIG_H) _fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize) if nplots > 1: flat_axes = axes.flatten() else: flat_axes = np.array([axes]) for ax, name_distr in zip_longest(flat_axes, self.distr_dict.items(), fillvalue=None): if name_distr is None: ax.remove() continue (param_name, distr) = name_distr x = np.linspace(distr.ppf(0.001), distr.ppf(0.999), 100) ax.plot(x, distr.pdf(x), label=param_name) ax.legend() return axes
[docs] @staticmethod def var_to_uncvar(var): """ Returns an uncertainty variable with no distribution if var is not an UncVar. Else, returns var. Parameters ---------- var : climada.uncertainty.UncVar or any other CLIMADA object Returns ------- UncVar var if var is UncVar, else UncVar with var and no distribution. """ if isinstance(var, UncVar): return var return UncVar(uncvar_func=lambda: var, distr_dict={})
[docs]class Uncertainty(): """ Uncertainty analysis class This is the base class to perform uncertainty analysis on the outputs of a climada.engine.impact.Impact() or climada.engine.costbenefit.CostBenefit() object. Attributes ---------- unc_vars : dict(UncVar) Dictonnary of the required uncertainty variables. samples_df : pandas.DataFrame Values of the sampled uncertainty parameters. It has n_samples rows and one column per uncertainty parameter. sampling_method : str Name of the sampling method from SAlib. https://salib.readthedocs.io/en/latest/api.html# n_samples : int Effective number of samples (number of rows of samples_df) param_labels : list Name of all the uncertainty parameters distr_dict : dict Comon flattened dictionary of all the distr_dic list in unc_vars. It represents the distribution of all the uncertainty parameters. problem_sa : dict The description of the uncertainty variables and their distribution as used in SALib. https://salib.readthedocs.io/en/latest/basics.html. metrics : dict Dictionary of the value of the CLIMADA metrics for each sample (of the uncertainty parameters) defined in samples_df. Keys are metrics names, e.g. 'aai_agg'', 'freq_curve', 'eai_exp', 'at_event' for impact.calc and 'tot_climate_risk', 'benefit', 'cost_ben_ratio', 'imp_meas_present', 'imp_meas_future' for cost_benefit.calc. Values are pd.DataFrame of dict(pd.DataFrame), with one row for one sample. sensitivity: dict Sensitivity indices for each metric. Keys are metrics names, e.g. 'aai_agg'', 'freq_curve', 'eai_exp', 'at_event' for impact.calc and 'tot_climate_risk', 'benefit', 'cost_ben_ratio', 'imp_meas_present', 'imp_meas_future' for cost_benefit.calc. Values are the sensitivity indices dictionary as returned by SALib. """
[docs] def __init__(self, unc_vars, samples=None, metrics=None, sensitivity=None): """ Initialize Uncertainty Parameters ---------- unc_vars : dict keys are names and values are climade.engine.uncertainty.UncVar samples : pd.DataFrame, optional DataFrame of sampled parameter values. Column names must be parameter names (all labels) from all unc_vars. The default is pd.DataFrame(). metrics : dict(), optional dictionary of the CLIMADA metrics (outputs from Impact.calc() or CostBenefits.calcl()) for which the uncertainty distribution (and optionally the sensitivity) will be computed. For each sample (row of samples), each metric must have a definite value. Metrics are named directly after their defining attributes: Impact: ['aai_agg', 'freq_curve', 'eai_exp', 'at_event'] CostBenefits: ['tot_climate_risk', 'benefit', 'cost_ben_ratio', 'imp_meas_present', 'imp_meas_future'] Keys are metric names and values are pd.DataFrame with values for each parameter sample (one row per sample). The default is {}. sensitivity: dict(), optional dictionary of the sensitivity analysis for each uncertainty parameter. The default is {}. """ self.unc_vars = unc_vars if unc_vars else {} self.samples_df = samples if samples is not None else pd.DataFrame( columns = self.param_labels) self.sampling_method = None self.metrics = metrics if metrics is not None else {} self.sensitivity = sensitivity if sensitivity is not None else {} self.check()
[docs] def check(self): """ Check if the data variables are consistent Returns ------- check: boolean True if data is consistent. """ check = True check &= (self.param_labels == self.samples_df.columns.to_list()) if not check: raise ValueError("Parameter names from unc_vars do not " "correspond to parameters names of sample") for metric, df_distr in self.metrics.items(): if not df_distr.empty: check &= (len(df_distr) == self.n_samples) if not check: raise ValueError(f"Metric f{metric} has less values than" " the number of samples {self.n_samples}") if df_distr.isnull().values.any(): LOGGER.warning("At least one metric evaluated to Nan for " "one cominbation of uncertainty parameters containend " "in sample. Note that the sensitivity analysis will " "then return Nan. " "See https://github.com/SALib/SALib/issues/237") return check
@property def n_samples(self): """ The effective number of samples Returns ------- n_samples: int effective number of samples """ if isinstance(self.samples_df, pd.DataFrame): return self.samples_df.shape[0] return 0 @property def param_labels(self): """ Labels of all uncertainty parameters. Returns ------- param_labels: list of strings Labels of all uncertainty parameters. """ return list(self.distr_dict.keys()) @property def distr_dict(self): """ Dictionary of the distribution of all the parameters of all variables listed in self.unc_vars Returns ------- distr_dict : dict( sp.stats objects ) Dictionary of all distributions. """ distr_dict = dict() for unc_var in self.unc_vars.values(): distr_dict.update(unc_var.distr_dict) return distr_dict @property def problem_sa(self): """ The description of the uncertainty variables and their distribution as used in SALib. https://salib.readthedocs.io/en/latest/basics.html Returns ------- problem_sa : dict Salib problem dictionary. """ return { 'num_vars' : len(self.param_labels), 'names' : self.param_labels, 'bounds' : [[0, 1]]*len(self.param_labels) }
[docs] def make_sample(self, N, sampling_method='saltelli', sampling_kwargs=None): """ Make a sample for all parameters with their respective distributions using the chosen sampling_method from SALib. https://salib.readthedocs.io/en/latest/api.html This sets the attributes self.sampling method and self.samples_df. Parameters ---------- N : int Number of samples as defined in SALib.sample.saltelli.sample(). sampling_method: string The sampling method as defined in SALib. Possible choices: 'saltelli', 'fast_sampler', 'latin', 'morris', 'dgsm', 'ff' https://salib.readthedocs.io/en/latest/api.html The default is 'saltelli' sampling_kwargs: dict() Optional keyword arguments of the chosen SALib sampling method. Returns ------- df_samples: pd.DataFrame() Dataframe of the generated samples (one row = one sample, columns = uncertainty parameters) """ self.sampling_method = sampling_method uniform_base_sample = self._make_uniform_base_sample(N, sampling_method, sampling_kwargs) df_samples = pd.DataFrame(uniform_base_sample, columns=self.param_labels) for param in list(df_samples): df_samples[param] = df_samples[param].apply( self.distr_dict[param].ppf ) self.samples_df = df_samples LOGGER.info("Effective number of made samples: %d", self.n_samples) return df_samples
def _make_uniform_base_sample(self, N, sampling_method='saltelli', sampling_kwargs=None): """ Make a uniform distributed [0,1] sample for the defined uncertainty parameters (self.param_labels) with the chosen method from SALib (self.sampling_method) https://salib.readthedocs.io/en/latest/api.html Parameters ---------- N: Number of samples as defined for the SALib sample method. Note that the effective number of created samples might be larger (c.f. SALib) sampling_method: string The sampling method as defined in SALib. Possible choices: 'saltelli', 'fast_sampler', 'latin', 'morris', 'dgsm', 'ff' https://salib.readthedocs.io/en/latest/api.html The default is 'saltelli' sampling_kwargs: dict() Optional keyword arguments of the chosen SALib sampling method. Returns ------- sample_uniform : np.matrix Returns a NumPy matrix containing the sampled uncertainty parameters using the defined sampling method (self.sampling_method) """ if sampling_kwargs is None: sampling_kwargs = {} #Import the named submodule from the SALib sample module #From the workings of __import__ the use of 'from_list' is necessary #c.f. https://stackoverflow.com/questions/2724260/why-does-pythons-import-require-fromlist salib_sampling_method = getattr( __import__('SALib.sample', fromlist=[sampling_method] ), sampling_method ) sample_uniform = salib_sampling_method.sample( problem = self.problem_sa, N = N, **sampling_kwargs) return sample_uniform
[docs] def est_comp_time(self, time_one_run, pool=None): """ Estimate the computation time Parameters ---------- time_one_run : int/float Estimated computation time for one parameter set in seconds pool : pathos.pool, optional pool that would be used for parallel computation. The default is None. Returns ------- Estimated computation time in secs. """ time_one_run = u_sig_dig(time_one_run, n_sig_dig=3) if time_one_run > 5: LOGGER.warning("Computation time for one set of parameters is " "%.2fs. This is suspiciously long. Possible " "Potential reasons: unc_vars are loading data, centroids not" " assigned to exp before defining unc_var, ..." "\n If computation cannot be reduced, consider using" " a surrogate model https://www.uqlab.com/", time_one_run) ncpus = pool.ncpus if pool else 1 total_time = self.n_samples * time_one_run / ncpus return total_time
[docs] def calc_sensitivity(self, salib_method='sobol', method_kwargs=None): """ Compute the sensitivity indices using SALib. Prior to doing this sensitivity analysis, one must compute the distribution of the output metrics values (i.e. self.metrics is defined) for all the parameter samples (rows of self.samples_df). According to Wikipedia, sensitivity analysis is “the study of how the uncertainty in the output of a mathematical model or system (numerical or otherwise) can be apportioned to different sources of uncertainty in its inputs.” The sensitivity of each input is often represented by a numeric value, called the sensitivity index. Sensitivity indices come in several forms: First-order indices: measures the contribution to the output variance by a single model input alone. Second-order indices: measures the contribution to the output variance caused by the interaction of two model inputs. Total-order index: measures the contribution to the output variance caused by a model input, including both its first-order effects (the input varying alone) and all higher-order interactions. This sets the attribute self.sensitivity. Parameters ---------- salib_method : str sensitivity analysis method from SALib.analyse Possible choices: 'fast', 'rbd_fact', 'morris', 'sobol', 'delta', 'ff' The default is 'sobol'. Note that in Salib, sampling methods and sensitivity analysis methods should be used in specific pairs. https://salib.readthedocs.io/en/latest/api.html method_kwargs: dict(), optional Keyword arguments of the chosen SALib analyse method. The default is to use SALib's default arguments. Returns ------- sensitivity_dict : dict dictionary of the sensitivity indices. Keys are the metrics names, values the sensitivity indices dictionary as returned by SALib. """ check_salib(self.sampling_method, salib_method) #Import the named submodule from the SALib analyse module #From the workings of __import__ the use of 'from_list' is necessary #c.f. https://stackoverflow.com/questions/2724260/why-does-pythons-import-require-fromlist method = getattr( __import__('SALib.analyze', fromlist=[salib_method] ), salib_method ) #Certaint Salib method required model input (X) and output (Y), others #need only ouput (Y) salib_kwargs = method.analyze.__code__.co_varnames #obtain all kwargs of the salib method X = self.samples_df.to_numpy() if 'X' in salib_kwargs else None if method_kwargs is None: method_kwargs = {} sensitivity_dict = {} for name, df_metric in self.metrics.items(): sensitivity_dict[name] = {} for metric in df_metric: Y = df_metric[metric].to_numpy() if X is not None: sensitivity_index = method.analyze(self.problem_sa, X, Y, **method_kwargs) else: sensitivity_index = method.analyze(self.problem_sa, Y, **method_kwargs) sensitivity_dict[name].update({metric: sensitivity_index}) self.sensitivity = sensitivity_dict return sensitivity_dict
[docs] def plot_distribution(self, metric_list=None, figsize=None): """ Plot the distribution of values. Parameters ---------- metric_list: list, optional List of metrics to plot the distribution. The default is None. figsize: tuple(int or float, int or float), optional The figsize argument of matplotlib.pyplot.subplots() The default is derived from the total number of plots (nplots) as: nrows, ncols = int(np.ceil(nplots / 3)), min(nplots, 3) figsize = (ncols * FIG_W, nrows * FIG_H) Raises ------ ValueError If no metric distribution was computed the plot cannot be made. Returns ------- axes: matplotlib.pyplot.axes The axes handle of the plot. """ if not self.metrics: raise ValueError("No uncertainty data present for these metrics. "+ "Please run an uncertainty analysis first.") if metric_list is None: metric_list = list(self.metrics.keys()) df_values = pd.DataFrame() for metric in metric_list: if metric not in METRICS_2D: df_values = df_values.append(self.metrics[metric]) df_values_log = df_values.apply(np.log10).copy() df_values_log = df_values_log.replace([np.inf, -np.inf], np.nan) cols = df_values_log.columns nplots = len(cols) nrows, ncols = int(np.ceil(nplots / 3)), min(nplots, 3) if not figsize: figsize = (ncols * FIG_W, nrows * FIG_H) _fig, axes = plt.subplots(nrows = nrows, ncols = ncols, figsize = figsize, sharex = True, sharey = True) if nplots > 1: flat_axes = axes.flatten() else: flat_axes = np.array([axes]) for ax, col in zip_longest(flat_axes, cols, fillvalue=None): if col is None: ax.remove() continue data = df_values_log[col] if data.empty: ax.remove() continue data.hist(ax=ax, bins=100, density=True, histtype='step') avg = df_values[col].mean() std = df_values[col].std() ax.plot([np.log10(avg), np.log10(avg)], [0, 1], color='red', linestyle='dashed', label="avg=%.2f%s" %u_vtm(avg)) ax.plot([np.log10(avg) - np.log10(std) / 2, np.log10(avg) + np.log10(std) / 2], [0.3, 0.3], color='red', label="std=%.2f%s" %u_vtm(std)) ax.set_title(col) ax.set_xlabel('value [log10]') ax.set_ylabel('density of events') ax.legend() return axes
[docs] def plot_sample(self, figsize=None): """ Plot the sample distributions of the uncertainty parameters. Parameters --------- figsize: tuple(int or float, int or float), optional The figsize argument of matplotlib.pyplot.subplots() The default is derived from the total number of plots (nplots) as: nrows, ncols = int(np.ceil(nplots / 3)), min(nplots, 3) figsize = (ncols * FIG_W, nrows * FIG_H) Raises ------ ValueError If no sample was computed the plot cannot be made. Returns ------- axes: matplotlib.pyplot.axes The axis handle of the plot. """ if self.samples_df.empty: raise ValueError("No uncertainty sample present."+ "Please make a sample first.") nplots = len(self.param_labels) nrows, ncols = int(np.ceil(nplots / 3)), min(nplots, 3) if not figsize: figsize = (ncols * FIG_W, nrows * FIG_H) _fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize) for ax, label in zip_longest(axes.flatten(), self.param_labels, fillvalue=None): if label is None: ax.remove() continue self.samples_df[label].hist(ax=ax, bins=100) ax.set_title(label) ax.set_xlabel('value') ax.set_ylabel('Sample count') return axes
[docs] def plot_sensitivity(self, salib_si='S1', metric_list=None, figsize=None): """ Plot one of the first order sensitivity indices of the chosen metric(s). This requires that a senstivity analysis was already performed. E.g. For the sensitivity analysis method 'sobol', the choices are ['S1', 'ST'], for 'delta' the choices are ['delta', 'S1']. For more information see the SAlib documentation: https://salib.readthedocs.io/en/latest/basics.html Parameters ---------- salib_si: string, optional The first order (one value per metric output) sensitivity index to plot. This must be a key of the sensitivity dictionaries in self.sensitivity[metric] for each metric in metric_list. The default is S1. metric_list: list of strings, optional List of metrics to plot the sensitivity. If a metric is not found in self.sensitivity, it is ignored. The default is all metrics from Impact.calc or CostBenefit.calc: ['aai_agg', 'freq_curve', 'tot_climate_risk', 'benefit', 'cost_ben_ratio', 'imp_meas_present', 'imp_meas_future', 'tot_value'] figsize: tuple(int or float, int or float), optional The figsize argument of matplotlib.pyplot.subplots() The default is derived from the total number of plots (nplots) as: nrows, ncols = int(np.ceil(nplots / 3)), min(nplots, 3) figsize = (ncols * FIG_W, nrows * FIG_H) Raises ------ ValueError If no sensitivity is available the plot cannot be made. Returns ------- axes: matplotlib.pyplot.axes The axes handle of the plot. """ if not self.metrics: raise ValueError("No sensitivity present for this metrics. " "Please run a sensitivity analysis first.") if metric_list is None: metric_list = ['aai_agg', 'freq_curve', 'tot_climate_risk', 'tot_value', 'benefit', 'cost_ben_ratio', 'imp_meas_present', 'imp_meas_future', 'tot_value'] metric_list = list(set(metric_list) & set(self.metrics.keys())) nplots = len(metric_list) nrows, ncols = int(np.ceil(nplots / 3)), min(nplots, 3) if not figsize: figsize = (ncols * FIG_W, nrows * FIG_H) _fig, axes = plt.subplots(nrows = nrows, ncols = ncols, figsize = figsize, sharex = True, sharey = True) if nplots > 1: flat_axes = axes.flatten() else: flat_axes = np.array([axes]) for ax, metric in zip_longest(flat_axes, metric_list, fillvalue=None): if metric is None: ax.remove() continue si_dict = self.sensitivity[metric] S = {label: si[salib_si] for label, si in si_dict.items()} df_S = pd.DataFrame(S) if df_S.empty: ax.remove() continue try: S_conf = { label: si[salib_si + '_conf'] for label, si in si_dict.items() } except KeyError: S_conf = [] df_S_conf = pd.DataFrame(S_conf) if df_S_conf.empty: df_S.plot(ax=ax, kind='bar') else: df_S.plot(ax=ax, kind='bar', yerr=df_S_conf) ax.set_xticklabels(self.param_labels, rotation=0) ax.set_title(salib_si + ' - ' + metric) plt.tight_layout() return axes
[docs] def save_samples_df(self, filename=None): """ Save the samples_df dataframe to .csv Parameters ---------- filename : str or pathlib.Path, optional The filename with absolute or relative path. The default name is "samples_df + datetime.now() + .csv" and the default path is taken from climada.config Returns ------- save_path : pathlib.Path Path to the saved file """ if filename is None: filename = "samples_df" + dt.now().strftime("%Y-%m-%d-%H%M%S") filename = Path(DATA_DIR) / Path(filename) save_path = Path(filename) save_path = save_path.with_suffix('.csv') self.samples_df.to_csv(save_path, index=False) return save_path
[docs] def load_samples_df(self, filename): """ Load a samples_df from .csv file Parameters ---------- filename : str or pathlib.Path The filename with absolute or relative path. Returns ------- samples_df : pandas.DataFrame The loaded samples_df """ self.samples_df = pd.read_csv(Path(filename).with_suffix('.csv')) return self.samples_df
[docs] def save_metrics(self, filename=None): """ Save the metrics dictionary to .json Parameters filename : str or pathlib.Path, optional The filename with absolute or relative path. The default name is "metrics + datetime.now() + .json" and the default path is taken from climada.config Returns ------- save_path : pathlib.Path Path to the saved file """ if filename is None: filename = "metrics" + dt.now().strftime("%Y-%m-%d-%H%M%S") filename = Path(DATA_DIR) / Path(filename) save_path = Path(filename).with_suffix('.json') with open(save_path, 'w') as f: json.dump(self.metrics, f) return save_path
[docs] def load_metrics(self, filename, directory=None): """ Load a metrics from .json file Parameters ---------- filename : str or pathlib.Path The filename. directory : str or pathlib.Path, optional The directory path. The default is taken from climada.config Returns ------- metrics : dict The loaded metrics dictionary """ if directory is None: directory = DATA_DIR load_path = Path(filename).with_suffix('.json') with open(load_path, 'r') as f: self.metrics = json.load(f) return self.metrics
[docs] def save_sensitivity(self, filename=None): """ Save the sensitivity dictionary to .json Parameters filename : str or pathlib.Path, optional The filename with absolute or relative path. The default name is "sensitivity + datetime.now() + .json" and the default path is taken from climada.config Returns ------- save_path : pathlib.Path Path to the saved file """ if filename is None: filename = "sensitivity" + dt.now().strftime("%Y-%m-%d-%H%M%S") filename = Path(DATA_DIR) / Path(filename) save_path = Path(filename).with_suffix('.json') with open(save_path, 'w') as f: json.dump(self.sensitivity, f) return save_path
[docs] def load_sensitivity(self, filename): """ Load a sensitivity from .json file Parameters ---------- filename : str or pathlib.Path The filename directory : str or pathlib.Path, optional The directory path. The default is taken from climada.config Returns ------- sensitivity : dict The loaded sensitivity dictionary """ load_path = Path(filename).with_suffix('.json') with open(load_path, 'r') as f: self.sensitivity = json.load(f) return self.sensitivity
SALIB_COMPATIBILITY = { 'fast': ['fast_sampler'], 'rbd_fast': ['latin'] , 'morris': ['morris'], 'sobol' : ['saltelli'], 'delta' : ['latin'], 'dgsm' : ['fast_sampler', 'latin', 'morris', 'saltelli', 'latin', 'ff'], 'ff' : ['ff'], } def check_salib(sampling_method, sensitivity_method): """ Checks whether the chosen salib sampling method and sensitivity method respect the pairing recommendation by the salib package. Parameters ---------- sampling_method : str Name of the sampling method. sensitivity_method : str Name of the sensitivity analysis method. Returns ------- bool True if sampling and sensitivity methods respect the recommended pairing. """ if sampling_method not in SALIB_COMPATIBILITY[sensitivity_method]: LOGGER.warning("The chosen combination of sensitivity method (%s)" " and sampling method (%s) does not correspond to the" " recommendation of the salib pacakge." "\n https://salib.readthedocs.io/en/latest/api.html", sampling_method, sensitivity_method ) return False return True