Project: s41467-024-54846-2 | Function: generate_config (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Generates Monte Carlo configurations for parameter uncertainty analysis. Creates multiple parameter sets by randomly sampling from available model options, implementing the 1000 Monte Carlo simulations mentioned in the paper.

**KEY TERMS:** {
  "nMC": "number of Monte Carlo simulations",
  "Par": "model parameters",
  "mod_list": "model configuration options",
  "config": "Monte Carlo configuration",
  "fixed": "fixed parameter options"
}

**CONTEXT CODE:** """
Copyright: IIASA (International Institute for Applied Systems Analysis), 2016-2021; CEA (Commissariat a L'Energie Atomique) & UVSQ (Universite de Versailles et Saint-Quentin), 2016
Contributor(s): Thomas Gasser (gasser@iiasa.ac.at), Yann Quilcaille

This software is a computer program whose purpose is to simulate the behavior of the Earth system, with a specific but not exclusive focus on anthropogenic climate change.

This software is governed by the CeCILL license under French law and abiding by the rules of distribution of free software.  You can use, modify and/ or redistribute the software under the terms of the CeCILL license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". 

As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability. 

In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software, that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured and,  more generally, to use and operate it in the same conditions as regards security. 

The fact that you are presently reading this means that you have had knowledge of the CeCILL license and that you accept its terms.
"""

##################################################
##################################################

import random
import warnings
import numpy as np
import xarray as xr

from scipy.stats import theilslopes

from core_fct.fct_misc import extend_timeseries, lognorm_distrib_param


##################################################
## 1. GENERATE MONTE CARLO PARAMETERS
##################################################

## generate all Monte Carlo configurations 
def generate_config(Par, nMC, ignored=['mean_', 'old_', 'off_'], fixed={}, return_details=False):
    '''
    Function to generate Monte Carlo configuration (= parameters) for OSCAR.

    Input:
    ------
    Par (xr.Dataset)        dataset containing primary parameters
    nMC (int)               number of MC elements

    Output:
    -------
    Par_mc (xr.Dataset)     dataset containing MC parameters
    mod_mc (xr.Dataset)     (optional) dataset containing the name of the chosen options for each configuration

    Options:
    --------
    ignored (list)          configurations containing any of the list's elements in their names will be ignored;
                            default = ['mean_', 'old_', 'off_']
    fixed (dict)            dict of options to be fixed (i.e. ignored by MC and taken as provided);
                            keys are configuration options (starting with 'mod_') and values are desired configuration;
                            default = {}
    return_details (bool)   whether details on each configuration should be returned
                            default = False
    '''

    raise NotImplementedError("Function masked for testing - use golden version for implementation")


## function to add noise and shift existing MC parameters
def adjust_config(Par_mc, shift={}, noise={}):
    '''
    Function to add shift (first) and/or noise (second) to the input Monte Carlo parameters.
    
    Input:
    ------
    Par_mc (xr.Dataset)     dataset containing MC parameters
    
    Output:
    -------
    Par_mc2 (xr.Dataset)    dataset containing adjusted MC parameters

    Options:
    --------
    shift (dict)            dict of options to shift all MC values of a parameter;
                            keys are parameter names and values are float;
                            default = {}
    noise (dict)            dict of options to add extra noise to a parameter;
                            keys are parameter names and values are 2-tuples with chosen distrib ('normal' or 'lognormal') and normalized std;
                            default = {}
    '''

    ## copy input dataset
    Par_mc2 = Par_mc.copy(deep=True)

    ## loop on parameters
    for par in Par_mc2:
        
        ## add config axis if missing
        if par in shift or par in noise:
            if 'config' not in Par_mc2[par].dims:
                Par_mc2[par] = Par_mc2[par] * xr.full_like(Par_mc[par] + Par_mc.config, 1)

        ## shifting
        if par in shift:
            Par_mc2[par] = Par_mc2[par] + shift[par]

        ## adding noise
        if par in noise:
            ## normal distrib
            if noise[par][0] == 'normal':
                Par_mc2[par] = Par_mc2[par] * np.random.normal(1., scale=noise[par][1], size=Par_mc2[par].shape)
            ## lognormal distrib
            elif noise[par][0] == 'lognormal':
                Par_mc2[par] = Par_mc2[par] * np.random.lognormal(*lognorm_distrib_param(1., noise[par][1]), size=Par_mc2[par].shape)

    ## return adjusted dataset
    return Par_mc2


##################################################
## 2. GENERATE MONTE CARLO FORCINGS
##################################################

## generate all Monte Carlo drivers
def generate_drivers(For, nMC, fixed={}):
    '''
    Function to geenrate Monte Carlo (historical) drivers for OSCAR.
    
    Input:
    ------
    For (xr.Dataset)        dataset containing secondary historical drivers
    nMC (int)               number of MC elements
    
    Output:
    -------
    For_mc (xr.Dataset)     dataset containing MC historical drivers
    Options:
    --------
    fixed (dict)            dict of data options to be fixed (i.e. ignored by MC and taken as provided);
                            keys are data options (starting with 'data_') and values are desired configuration;


Complete the function implementation replacing "raise NotImplementedError".