text:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Apr 11 09:26:42 2020

@author: djemi
"""

print('Hello Sam Sam')

# import libraries
import matplotlib.pyplot as plt
import pandas as pd
import json
import numpy as np

# import libraries for Natural Language Processing and more
import nltk
from nltk.stem import WordNetLemmatizer
from sklearn.decomposition import TruncatedSVD
from wordcloud import WordCloud
import bs4 as bs
import urllib.request
import re
from datetime import datetime

# importing the html form the url
url_list = pd.read_excel('../Sam_Url_OK_KO_connexion.xlsx')

# starting cleaning htlm pages and adding data in to json file
word_lemmatizer = WordNetLemmatizer()

patents = {'patent', 'patents', 'company', 'application','product',
                     'products', 'access', 'package', 'brend','companies'}
patents_words = set(w.rstrip() for w in patents)

#print(time_it(start_process_nlp, n=1, start=1,end=2))

def start_process_nlp(article_paragraphs = '', corpus = '', number = 0):
    try:
        data = []
        errors = []
        for ligne in range(len(url_list)):
            try:
                url = url_list.get('OK_Connexion')[ligne]

                if ligne <= 400 and url is not np.nan:
                    print (ligne, url)

                    raw_html = urllib.request.urlopen(url)
                    raw_html = raw_html.read()
                    article_html = bs.BeautifulSoup(raw_html, 'lxml')
                    article_paragraphs = article_html.find_all('body')
                    corpus = first_clean_text_html_return_corpus(article_paragraphs)

                    if len(corpus) == 0:
                        print('No text or problem to find text in the url :',url)
                        add_error(errors,ligne,url,'requete_html_vide_text')
                        # add list of error url can be use after for analysing
                    else:
                        make_clean_text(corpus)
                        add_to_data(data, corpus, url, ligne)
                        visualize(corpus)

                else:
                    break
            except urllib.error.URLError as urlErr:
                print('URLError ',url)
                add_error(errors,ligne,url, str(urlErr))
                pass
            except urllib.error.HTTPError as httpErr:
                print('HTTPError ',url)
                add_error(errors,ligne,url, str(httpErr))
                pass
    except :
        pass
    finally :
        add_data_to_json(data)
        add_errors_to_json(errors)

def first_clean_text_html_return_corpus(article_paragraphs):
    article_text = ''
    for para in article_paragraphs:
        article_text += para.text
    corpus = nltk.sent_tokenize(article_text)
    return corpus

def make_clean_text(corpus):
    for i in range(len(corpus )):
        corpus [i] = corpus [i].lower()
        corpus [i] = re.sub(r'\W',' ',corpus [i])
        corpus [i] = re.sub(r'\s+',' ',corpus [i])
        #corpus [i] = my_tokenizer(corpus [i])

def my_tokenizer(s):
    s = s.lower()
    tokens_new = nltk.tokenize.word_tokenize(s)
    tokens_new = [token for token in tokens_new if len(token) > 2]
    '''
        Definition of :
            lemmatize -> a lemma is the canonical or base form for a set of word  and is
    '''
    tokens_new = [word_lemmatizer.lemmatize(token) for token in tokens_new]

    #tokens_new = [token for token in tokens_new if token not in patents_words]
    tokens_new = [token for token in tokens_new if not any(c.isdigit() for c in token)]
    return tokens_new

def add_to_data(data, corpus, url, number):
    one_patern = {}
    one_patern['number'] = number
    one_patern['date_time_extraction'] = str(datetime.now())
    one_patern['url'] = url
    one_patern['all_text'] = corpus
    data.append(one_patern)

def add_data_to_json(data):
    with open("data_corpus.json", "w") as write_file:
            json.dump(data, write_file)

def add_error(errors, number, url, type_error):
    error = {}
    error['number'] = number
    error['url'] = url
    error['date_time'] = str(datetime.now())
    error['type_error'] = type_error
    errors.append(error)

def add_errors_to_json(errors):
    with open("data_errors.json", "w") as write_file:
            json.dump(errors, write_file)

def visualize(corpus):
    words=''

    for msg in corpus:
        msg = msg.lower()
        words += msg + ''

    wordcloud = WordCloud(width=800, height=600,
                          background_color='white',
                          colormap='Dark2',
                          max_font_size=200,
                          random_state=42).generate(words)
    plt.imshow(wordcloud)
    plt.axis('off')
    plt.show()

'''
def time_it(fn, rep=1, *args, ***kwargs):
    start = time.perf_counter()
    for i in range(rep):
        fn(*args,***kwargs)
    end = time.perf_counter()
    return (end-start)/rep
'''
# start runing
start_process_nlp()

max_stars_repo_name: djemalzecevic/nounours

max_issues_repo_name: djemalzecevic/nounours

max_forks_repo_name: djemalzecevic/nounours


text:
#!/usr/bin/env python
u"""
calc_leakage_ratio.py

Calculate first-order estimate of leakage by comparing
kernel to the harmonic representation of the region of interest
"""
import os
import sys
import numpy as np
from gravity_toolkit.ncdf_read import ncdf_read
from gravity_toolkit.ncdf_write import ncdf_write
from gravity_toolkit.ncdf_read_stokes import ncdf_read_stokes
from gravity_toolkit.read_love_numbers import read_love_numbers
from gravity_toolkit.harmonic_summation import harmonic_summation
from gravity_toolkit.plm_mohlenkamp import plm_mohlenkamp

#------------------------------------------------------------------------------
#-- Calculate Leakage ratio
#------------------------------------------------------------------------------
def calc_leakage(parameters):
	#-- read grid parameters
	DDEG_RASTER = float(parameters['DDEG_RASTER'])
	#-- read harmonic parameters
	LMAX = int(parameters['LMAX'])
	MMAX = int(parameters['MMAX'])
	LMIN = int(parameters['LMIN'])
	#-- smoothing radius
	RAD = int(parameters['RAD'])
	#-- Set up output labels
	OCN = '_OCN' if parameters['MASCON_OCEAN'] in ['Y','y'] else ''
	#-- read love numbers from file in parameter file
	love_file = os.path.expanduser(parameters['LOVE_FILE'])
	hl,kl,ll = read_love_numbers(love_file,REFERENCE='CF')

	#-- load mascon configuration of interest
	mascon_nums = np.array(parameters['MSCN_NUMS'].split(','),dtype=int)
	mascon_name = parameters['MSCN_NAME']
	lbl = '{0}_{1}'.format(mascon_name,parameters['MSCN_NUMS'].replace(',','+'))

	#-- check if difference file exists
	diff_file = os.path.join(os.path.expanduser(parameters['DIRECTORY']),\
		'MASCON_{0}_YLMS_{1:.2f}DEG_SKERNEL{2}_L{3:d}_r{4:d}km_DIFFERENCE.nc'\
		.format(lbl,DDEG_RASTER,OCN,LMAX,RAD))

	# #-- If file exists, read it. If not, calculate it and save it to file for next time and/or reference.
	# if os.path.exists(diff_file):
	# 	print('Reading difference file...')
	# 	diff_data = ncdf_read(diff_file,DATE=False,ATTRIBUTES=False,TITLE=False)
	# 	diff = diff_data['data']
	# 	lons = diff_data['lon']
	# 	lats = diff_data['lat']
	# else:
		# print('Calculating difference file...')
	#-- read kernel
	kern_file = os.path.join(os.path.expanduser(parameters['DIRECTORY']),\
		'MASCON_{0}_YLMS_{1:.2f}DEG_SKERNEL{2}_L{3:d}_r{4:d}km.nc'\
		.format(lbl,DDEG_RASTER,OCN,LMAX,RAD))
	kern_data = ncdf_read(kern_file,DATE=False)

	#-- extra kernel fields and make grid
	lons = kern_data['lon']
	lats = kern_data['lat']
	kern = kern_data['data']
	glon,glat = np.meshgrid(lons,lats)

	#-- create Legendre polynomials
	th = (90.0 - lats)*np.pi/180.0
	plm = plm_mohlenkamp(LMAX, np.cos(th))

	#-- convert harmonics to mass unit before spatial conversion
	#-- Average Density of the Earth [g/cm^3]
	rho_e = 5.517
	#-- Average Radius of the Earth [cm]
	rad_e = 6.371e8
	l = np.arange(0,LMAX+1)
	dfactor = rho_e*rad_e*(2.0*l+1.0)/(1.0 +kl[l])/3.0
	for m in range(0,MMAX+1):#-- MMAX+1 to include MMAX
		for l in range(m,LMAX+1):#-- LMAX+1 to include LMAX
			plm[l,m,:] = plm[l,m,:]*dfactor[l]

	#-- read mascon harmonics
	Ylms = {}
	harmonic_dir = os.path.join(os.path.dirname(os.path.expanduser(parameters['DIRECTORY'])),'harmonics')
	for i in mascon_nums:
		har_file = os.path.join(harmonic_dir,'mascon_{0:d}_Ylms_L{1:d}_{2:.2f}deg.nc'.format(i,LMAX,DDEG_RASTER))
		Ylms[i] = ncdf_read_stokes(har_file,DATE=False)

	#-- convert the harmonics to spatial domain and sum up mascons
	harm_sum = np.zeros(glon.shape)
	for i in mascon_nums:
		harm_sum += harmonic_summation(Ylms[i]['clm'],Ylms[i]['slm'],lons,lats,LMAX=LMAX,MMAX=MMAX,PLM=plm).transpose()

	#-- take difference between kernel and harmonic representation
	diff = kern - harm_sum

	#-- save difference to file
	ncdf_write(diff,lons,lats,0,FILENAME=diff_file,DATE=False,UNITS='unitless',LONGNAME='Sensitivity_Kernel_DIFFERENCE')

	#-- calculate Mean Percent Error (https://en.wikipedia.org/wiki/Mean_percentage_error)
	#- first calculate a map of percent errors
	percent_error = np.abs(diff/harm_sum) * 100
	#-- calculate area-weighted mean
	th_grid = (90.0 - glat)*np.pi/180.0
	area_scale = np.sin(th_grid)
	mpe = np.abs(np.sum(area_scale*percent_error)/np.sum(area_scale))
	print("Mean Percentage Error: {0:.1f}".format(mpe))

	#-- calculate mean ratio
	ratio = kern/harm_sum * 100
	mean_ratio = np.sum(area_scale*ratio)/np.sum(area_scale)
	print("Mean Ratio (%): {0:.1f}".format(mean_ratio))

	#-- ratio of sum(sigma*A)/sum(sigma)
	mass_ratio = np.sum(harm_sum*kern*area_scale)/np.sum(harm_sum*area_scale) * 100
	print("Mass Ratio (%): {0:.2f}".format(mass_ratio))

#------------------------------------------------------------------------------
#-- main function
#------------------------------------------------------------------------------
def main():
	if len(sys.argv) == 1:
		sys.exit('No paramter file given')
	else:
		#-- read input files
		input_files = sys.argv[1:]
		parameters = {}
		for infile in input_files:
			#-- for each paramter file, extract parameters
			fid = open(infile, 'r')
			for fileline in fid:
				part = fileline.split()
				parameters[part[0]] = part[1]
			fid.close()
			#-- feed parameters to function to calculate leakage
			calc_leakage(parameters)

#------------------------------------------------------------------------------
#-- run main program
#------------------------------------------------------------------------------
if __name__ == '__main__':
	main()

ext: py

reason: import numpy


text:
# zadanie 6
# Utwórz nową macierz 2x3 różnych wartości a następnie
# wylicz cosinus dla każdej z jej wartości i zapisz do zmiennej “b”.
import numpy as np

katy = np.array([[0, 30, 45], [90, 75, 25]])
b = np.empty((2, 3))
b = np.cos(katy*np.pi/180)
print('katy:')
print(katy)
print('b:')
print(b)
for i in range(0, 2, 1):
    for j in range(0, 3, 1):
        print('cos('+str(katy[i, j])+') =', b[i, j])


text:
def desectpath(path):
    basepath = ('/').join(path.split('/')[:-1])
    filename = path.split('/')[-1]
    ext = filename.split('.')[-1]
    fname = ('.').join(filename.split('.')[:-1])
    return basepath, fname, ext

# from https://stackoverflow.com/a/9878781:

def int_str(val, keyspace = "suTaWp6Z7P2zFYn5IMQUfqr4wAgeldRDVyvkS801HGjNJCbBKotE9x3hmiXcOL"):
    """ Turn a positive integer into a string.
    Keyspace can be anything you like - this was just shuffled letters and numbers, but...
    each character must occur only once. """
    assert len(set(keyspace)) == len(keyspace)
    assert val >= 0
    out = ""
    while val > 0:
        val, digit = divmod(val, len(keyspace))
        out += keyspace[digit]
    return out[::-1]

def str_int(val, keyspace = "suTaWp6Z7P2zFYn5IMQUfqr4wAgeldRDVyvkS801HGjNJCbBKotE9x3hmiXcOL"):
    """ Turn a string into a positive integer. """
    assert len(set(keyspace)) == len(keyspace)
    out = 0
    for c in val:
        out = out * len(keyspace) + keyspace.index(c)
    return out

def chaffify(val, chaff_val = 98127634):
    """ Add chaff to the given positive integer. """
    return val * chaff_val

def dechaffify(chaffy_val, chaff_val = 98127634):
    """ Dechaffs the given chaffed value.
    chaff_val must be the same as given to chaffify().
    If the value does not seem to be correctly chaffed, raises a ValueError. """
    val, chaff = divmod(chaffy_val, chaff_val)
    if chaff != 0:
        raise ValueError("Invalid chaff in value")
    return val

def id_to_key(number, keyspace="suTaWp6Z7P2zFYn5IMQUfqr4wAgeldRDVyvkS801HGjNJCbBKotE9x3hmiXcOL"):
    return int_str(chaffify(number),keyspace)

def key_to_id(key, keyspace="suTaWp6Z7P2zFYn5IMQUfqr4wAgeldRDVyvkS801HGjNJCbBKotE9x3hmiXcOL"):
    return dechaffify(str_int(key,keyspace))

import random, string

def randomkey():
    return ''.join(random.sample(string.ascii_letters+string.digits,len(string.ascii_letters+string.digits)))

from cv2 import imdecode
from numpy import asarray, uint8
import urllib

# cv2.imread cannot read from an url therefore this function
def url_to_img(url):
    req = urllib.urlopen(url)
    arr = asarray(bytearray(req.read()), dtype=uint8)
    return imdecode(arr,1)

reason: from numpy


text:
import torch
import numpy as np
import pytorch_lightning as pl
from torch.utils.data import DataLoader

# pylint: disable=no-member

class GPU_DataLoader_ST(object):
    def __init__(
        self,
        cmp,
        prt,
        asy,
        pIC50,
        prefixes,
        cmp_noise=0.0,
        prt_noise=0.0,
        batch_size=512,
        shuffle=False,
    ):

        self._n_examples = len(cmp)
        self.cmp_noise = cmp_noise
        self.prt_noise = prt_noise

        self.cmp = torch.tensor(cmp).float().cuda()
        self.prt = torch.tensor(prt).float().cuda()
        self.asy = torch.tensor(asy).float().cuda()
        self.pIC50 = torch.tensor(pIC50).float().cuda()
        self.prefixes = torch.tensor(prefixes).float().cuda()

        self.batch_index = 0
        self.batch_size = batch_size
        self.shuffle = shuffle
        if self.shuffle:
            self._update_permutation()
        else:
            self.permutation = torch.arange(self._n_examples).cuda()

    def __iter__(self):
        return self

    def _update_permutation(self):

        self.permutation = torch.randperm(self._n_examples).cuda()

    def _get_batch(self):
        example_indices = self.permutation.narrow(
            dim=0, start=self.batch_index, length=self.batch_size
        )
        cmp = torch.index_select(self.cmp, dim=0, index=example_indices)
        prt = torch.index_select(self.prt, dim=0, index=example_indices)
        asy = torch.index_select(self.asy, dim=0, index=example_indices)
        pIC50 = torch.index_select(self.pIC50, dim=0, index=example_indices)
        prefixes = torch.index_select(self.prefixes, dim=0, index=example_indices)

        cmp = torch.tanh(
            torch.atanh(cmp) + (torch.randn_like(cmp).cuda() * self.cmp_noise)
        )
        prt = prt + (torch.randn_like(prt).cuda() * self.prt_noise)

        return cmp, prt, asy, pIC50, prefixes

    def __next__(self):

        if self.batch_index + self.batch_size > self._n_examples:
            self.batch_index = 0
            if self.shuffle:
                self._update_permutation()
            raise StopIteration
        else:
            batch = self._get_batch()
            self.batch_index += self.batch_size
            return batch

class GPU_DataLoader_MT(object):
    def __init__(
        self,
        cmp,
        pIC50,
        prefixes,
        taskind,
        cmp_noise=0.0,
        batch_size=512,
        shuffle=False,
    ):

        self._n_examples = len(cmp)
        self.cmp_noise = cmp_noise

        self.cmp = torch.tensor(cmp).float().cuda()
        self.pIC50 = torch.tensor(pIC50).float().cuda()
        self.prefixes = torch.tensor(prefixes).float().cuda()
        self.taskind = torch.tensor(taskind).long().cuda()

        self.batch_index = 0
        self.batch_size = batch_size
        self.shuffle = shuffle
        if self.shuffle:
            self._update_permutation()
        else:
            self.permutation = torch.arange(self._n_examples).cuda()

    def __iter__(self):
        return self

    def _update_permutation(self):

        self.permutation = torch.randperm(self._n_examples).cuda()

    def _get_batch(self):
        example_indices = self.permutation.narrow(
            dim=0, start=self.batch_index, length=self.batch_size
        )
        cmp = torch.index_select(self.cmp, dim=0, index=example_indices)
        pIC50 = torch.index_select(self.pIC50, dim=0, index=example_indices)
        prefixes = torch.index_select(self.prefixes, dim=0, index=example_indices)
        taskind = torch.index_select(self.taskind, dim=0, index=example_indices)

        cmp = torch.tanh(
            torch.atanh(cmp) + (torch.randn_like(cmp).cuda() * self.cmp_noise)
        )

        return cmp, pIC50, prefixes, taskind

    def __next__(self):

        if self.batch_index + self.batch_size > self._n_examples:
            self.batch_index = 0
            if self.shuffle:
                self._update_permutation()
            raise StopIteration
        else:
            batch = self._get_batch()
            self.batch_index += self.batch_size
            return batch

class GPU_DataLoader_MT_withPRT(GPU_DataLoader_MT):
    def __init__(
        self,
        cmp,
        prt,
        pIC50,
        prefixes,
        task_ind,
        cmp_noise=0.0,
        prt_noise=0.0,
        batch_size=512,
        shuffle=False,
    ):

        super().__init__(cmp, pIC50, prefixes, task_ind, cmp_noise, batch_size, shuffle)
        self.prt_noise = prt_noise
        self.prt = torch.tensor(prt).float().cuda()

    def _get_batch(self):
        example_indices = self.permutation.narrow(
            dim=0, start=self.batch_index, length=self.batch_size
        )
        cmp = torch.index_select(self.cmp, dim=0, index=example_indices)
        prt = torch.index_select(self.prt, dim=0, index=example_indices)
        pIC50 = torch.index_select(self.pIC50, dim=0, index=example_indices)
        prefixes = torch.index_select(self.prefixes, dim=0, index=example_indices)
        taskind = torch.index_select(self.taskind, dim=0, index=example_indices)

        cmp = torch.tanh(
            torch.atanh(cmp) + (torch.randn_like(cmp).cuda() * self.cmp_noise)
        )
        prt = prt + (torch.randn_like(prt).cuda() * self.prt_noise)

        return cmp, prt, pIC50, prefixes, taskind

max_stars_repo_name: bayer-science-for-a-better-life/mtpcm

max_issues_repo_name: bayer-science-for-a-better-life/mtpcm

max_forks_repo_name: bayer-science-for-a-better-life/mtpcm


text:
"""
    The model's parameters module
    =============================

    This module defines the main classes containing the model configuration parameters.
    The parameters are typically specified as :class:`~.params.parameter.Parameter` objects.

    There are seven types of parameters arranged in classes:

    * :class:`ScaleParams` contains the model scale parameters. These parameters are used to scale and
      `nondimentionalize`_ the :class:`~.params.parameter.Parameter` of the other parameters classes according to
      their :attr:`~.params.parameter.Parameter.units` attribute.
    * :class:`AtmosphericParams` contains the atmospheric dynamical parameters.
    * :class:`AtmosphericTemperatureParams` containing the atmosphere's temperature and heat-exchange parameters.
    * :class:`OceanicParams` contains the oceanic dynamical parameters.
    * :class:`OceanicTemperatureParams` contains the ocean's temperature and heat-exchange parameters.
    * :class:`GroundParams` contains the ground dynamical parameters (e.g. orography).
    * :class:`GroundTemperatureParams` contains the ground's temperature and heat-exchange parameters.

    These parameters classes are regrouped into a global structure :class:`QgParams` which also contains

    * spectral modes definition of the model
    * physical constants
    * parameters derived from the ones provided by the user
    * helper functions to initialize and parameterize the model

    This global parameters structure is used by the other modules to construct the model's ordinary differential
    equations.

    Warning
    -------

    If a model's parameter is set to `None`, it is assumed to be disabled.

    ---------------------

    Description of the classes
    --------------------------

    .. _nondimentionalize: https://en.wikipedia.org/wiki/Nondimensionalization
"""

import numpy as np
import pickle
import warnings
from abc import ABC

from qgs.params.parameter import Parameter
from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis
from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis

class Params(ABC):
    """Base class for a model's parameters container.

    Parameters
    ----------
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.
    """

    _name = ""

    def __init__(self, dic=None):

        self.set_params(dic)

    def set_params(self, dic):
        """Set the specified parameters values.

        Parameters
        ----------
        dic: dict(float or Parameter)
            A dictionary with the parameters names and values to be assigned.
        """
        if dic is not None:
            for key, val in zip(dic.keys(), dic.values()):
                if key in self.__dict__.keys():
                    if isinstance(self.__dict__[key], Parameter):
                        if isinstance(val, Parameter):
                            self.__dict__[key] = val
                        else:
                            d = self.__dict__[key].__dict__
                            self.__dict__[key] = Parameter(val, input_dimensional=d['_input_dimensional'],
                                                           units=d['_units'],
                                                           description=d['_description'],
                                                           scale_object=d['_scale_object'],
                                                           return_dimensional=d['_return_dimensional'])
                    else:
                        self.__dict__[key] = val

    def __str__(self):
        s = ""
        for key, val in zip(self.__dict__.keys(), self.__dict__.values()):
            if 'params' not in key and key[0] != '_':
                if val is None:
                    pass
                elif isinstance(val, Parameter):
                    if val.input_dimensional:
                        units = val.units
                        efval = val.dimensional_value
                    else:
                        efval = val.nondimensional_value
                        if val.nondimensional_value == val.dimensional_value:
                            units = ""
                        else:
                            units = "[nondim]"
                    s += "'" + key + "': " + str(efval) + "  " + units + "  (" + val.description + "),\n"
                elif isinstance(val, (np.ndarray, list, tuple)) and isinstance(val[0], Parameter):
                    for i, v in enumerate(val):
                        if v.input_dimensional:
                            units = v.units
                            efval = v.dimensional_value
                        else:
                            efval = v.nondimensional_value
                            if v.nondimensional_value == v.dimensional_value:
                                units = ""
                            else:
                                units = "[nondim]"
                        s += "'" + key + "["+str(i+1)+"]': " + str(efval) + "  " + units + "  (" + v.description + "),\n"
                else:
                    s += "'"+key+"': "+str(val)+",\n"
        return s

    def _list_params(self):
        return self._name+" Parameters:\n"+self.__str__()

    def print_params(self):
        """Print the parameters contained in the container."""
        print(self._list_params())

    @staticmethod
    def create_params_array(values, input_dimensional=None, units=None, scale_object=None, description=None,
                            return_dimensional=None):

        if hasattr(values, "__iter__"):
            ls = len(values)
            if not isinstance(input_dimensional, list):
                if input_dimensional is None:
                    input_dimensional = True
                idx = ls * [input_dimensional]
            else:
                idx = input_dimensional
            if not isinstance(units, list):
                if units is None:
                    units = ""
                u = ls * [units]
            else:
                u = units
            if not isinstance(description, list):
                if description is None:
                    description = ""
                d = ls * [description]
            else:
                d = description
            if not isinstance(scale_object, list):
                s = ls * [scale_object]
            else:
                s = scale_object
            if not isinstance(return_dimensional, list):
                if return_dimensional is None:
                    return_dimensional = False
                rd = ls * [return_dimensional]
            else:
                rd = return_dimensional
            arr = list()
            for i, val in enumerate(values):
                arr.append(Parameter(val, input_dimensional=idx[i], units=u[i], scale_object=s[i], description=d[i],
                                     return_dimensional=rd[i]))
        else:
            arr = values * [Parameter(0.e0, input_dimensional=input_dimensional, units=units, scale_object=scale_object,
                                      description=description, return_dimensional=return_dimensional)]

        return np.array(arr, dtype=object)

    def __repr__(self):
        s = super(Params, self).__repr__()+"\n"+self._list_params()
        return s

    def load_from_file(self, filename, **kwargs):
        """Function to load previously saved Params object with the method :meth:`save_to_file`.

        Parameters
        ----------
        filename: str
            The file name where the Params object was saved.
        kwargs: dict
            Keyword arguments to pass to the :mod:`pickle` module method.
        """
        f = open(filename, 'rb')
        tmp_dict = pickle.load(f, **kwargs)
        f.close()

        self.__dict__.clear()
        self.__dict__.update(tmp_dict)

    def save_to_file(self, filename, **kwargs):
        """Function to save the Params object to a file with the :mod:`pickle` module.

        Parameters
        ----------
        filename: str
            The file name where to save the Params object.
        kwargs: dict
            Keyword arguments to pass to the :mod:`pickle` module method.
        """
        f = open(filename, 'wb')
        pickle.dump(self.__dict__, f, **kwargs)
        f.close()

class ScaleParams(Params):
    """Class containing the model scales parameters.

    Parameters
    ----------
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.

    Attributes
    ----------
    scale: Parameter
        The characteristic meridional space scale, :math:`L_y = \\pi \\, L`, in meters [:math:`m`].
    f0: Parameter
        Coriolis parameter, in [:math:`s^{-1}`].
    n: Parameter
        Model domain aspect ratio, :math:`n = 2 L_y/L_x` .
    rra: Parameter
        Earth radius, in meters [:math:`m`].
    phi0_npi: Parameter
        Latitude expressed in fraction of :math:`\\pi` .
    deltap: Parameter
        Difference of pressure between the center of the two atmospheric layers, in [:math:`Pa`].
    """
    _name = "Scale"

    def __init__(self, dic=None):

        Params.__init__(self, dic)

        # -----------------------------------------------------------
        # Scale parameters for the ocean and the atmosphere
        # -----------------------------------------------------------

        self.scale = Parameter(5.e6, units='[m]', description="characteristic space scale (L*pi)",
                               return_dimensional=True)
        self.f0 = Parameter(1.032e-4, units='[s^-1]', description="Coriolis parameter at the middle of the domain",
                            return_dimensional=True)
        self.n = Parameter(1.3e0, input_dimensional=False, description="aspect ratio (n = 2 L_y / L_x)")
        self.rra = Parameter(6370.e3, units='[m]', description="earth radius", return_dimensional=True)
        self.phi0_npi = Parameter(0.25e0, input_dimensional=False, description="latitude expressed in fraction of pi")
        self.deltap = Parameter(5.e4, units='[Pa]', description='pressure difference between the two atmospheric layers',
                                return_dimensional=True)
        self.set_params(dic)

    # ----------------------------------------
    # Some derived parameters (Domain, beta)
    # ----------------------------------------

    @property
    def L(self):
        """Parameter: Typical length scale :math:`L`  of the model, in meters [:math:`m`]."""
        return Parameter(self.scale / np.pi, units=self.scale.units, description='Typical length scale L',
                         return_dimensional=True)

    @property
    def L_y(self):
        """Parameter: The meridional extent :math:`L_y = \\pi \\, L` of the model's domain, in meters [:math:`m`]."""
        return Parameter(self.scale, units=self.scale.units, description='The meridional extent of the model domain',
                         return_dimensional=True)

    @property
    def L_x(self):
        """Parameter: The zonal extent :math:`L_x = 2 \\pi \\, L / n` of the model's domain, in meters [:math:`m`]."""
        return Parameter(2 * self.scale / self.n, units=self.scale.units,
                         description='The zonal extent of the model domain',
                         return_dimensional=True)

    @property
    def phi0(self):
        """Parameter: The reference latitude :math:`\\phi_0` at the center of the domain, expressed in radians [:math:`rad`]."""
        return Parameter(self.phi0_npi * np.pi, units='[rad]',
                         description="The reference latitude of the center of the domain",
                         return_dimensional=True)

    @property
    def beta(self):
        """Parameter: The meridional gradient of the Coriolis parameter at :math:`\\phi_0`, expressed in [:math:`m^{-1} s^{-1}`]. """
        return Parameter(self.L / self.rra * np.cos(self.phi0) / np.sin(self.phi0), input_dimensional=False,
                         units='[m^-1][s^-1]', scale_object=self,
                         description="Meridional gradient of the Coriolis parameter at phi_0")

class AtmosphericParams(Params):
    """Class containing the atmospheric parameters.

    Parameters
    ----------
    scale_params: ScaleParams
        The scale parameters object of the model.
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.

    Attributes
    ----------
    kd: Parameter
        Atmosphere bottom friction coefficient [:math:`s^{-1}`].
    kdp: Parameter
        Atmosphere internal friction coefficient [:math:`s^{-1}`].
    sigma: Parameter
        Static stability of the atmosphere [:math:`[m^2 s^{-2} Pa^{-2}`].
    """

    _name = "Atmospheric"

    def __init__(self, scale_params, dic=None):

        Params.__init__(self, dic)

        self._scale_params = scale_params

        # Parameters for the atmosphere
        self.kd = Parameter(0.1, input_dimensional=False, scale_object=scale_params, units='[s^-1]',
                            description="atmosphere bottom friction coefficient")
        self.kdp = Parameter(0.01, input_dimensional=False, scale_object=scale_params, units='[s^-1]',
                             description="atmosphere internal friction coefficient")
        self.sigma = Parameter(0.2e0, input_dimensional=False, scale_object=scale_params, units='[m^2][s^-2][Pa^-2]',
                               description="static stability of the atmosphere")

        self.set_params(dic)

    @property
    def sig0(self):
        """Parameter: Static stability of the atmosphere divided by 2."""
        return Parameter(self.sigma / 2, input_dimensional=False, scale_object=self._scale_params, units='[m^2][s^-2][Pa^-2]',
                         description="0.5 * static stability of the atmosphere")

class AtmosphericTemperatureParams(Params):
    """Class containing the atmospheric temperature parameters.

    Parameters
    ----------
    scale_params: ScaleParams
        The scale parameters object of the model.
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.

    Attributes
    ----------
    hd: None or Parameter
        Newtonian cooling coefficient.
        Newtonian cooling is disabled if `None`.
    thetas: None or ~numpy.ndarray(float)
        Coefficients of the Newtonian cooling spectral decomposition (non-dimensional).
        Newtonian cooling is disabled if `None`.
    gamma: None or Parameter
        Specific heat capacity of the atmosphere [:math:`J m^{-2} K^{-1}`].
        Heat exchange scheme is disabled if `None`.
    C: None or ~numpy.ndarray(Parameter)
        Spectral decomposition of the constant short-wave radiation of the atmosphere [:math:`W m^{-2}`].
        Heat exchange scheme is disabled if `None`.
    eps: None or Parameter
        Emissivity coefficient for the grey-body atmosphere
        Heat exchange scheme is disabled if `None`.
    T0: None or Parameter
        Stationary solution for the 0-th order atmospheric temperature [:math:`K`].
        Heat exchange scheme is disabled if `None`.
    sc: None or Parameter
        Ratio of surface to atmosphere temperature
        Heat exchange scheme is disabled if `None`.
    hlambda: None or Parameter
        Sensible + turbulent heat exchange between ocean and atmosphere [:math:`W m^{-2} K^{-1}`].
        Heat exchange scheme is disabled if `None`.
    """
    _name = "Atmospheric Temperature"

    def __init__(self, scale_params, dic=None):

        Params.__init__(self, dic)

        self._scale_params = scale_params

        self.hd = Parameter(0.045, input_dimensional=False, units='[s]', scale_object=scale_params,
                            description="Newtonian cooling coefficient")
        self.thetas = None  # Radiative equilibrium mean temperature decomposition on the model's modes

        self.gamma = None
        self.C = None
        self.eps = None
        self.T0 = None
        self.sc = None
        self.hlambda = None

        self.set_params(dic)

    def set_insolation(self, value, pos=None):
        """Function to define the spectral decomposition of the constant short-wave radiation of the atmosphere (insolation)
        :math:`C_{{\\rm a}, i}` (:attr:`~.AtmosphericTemperatureParams.C`).

        Parameters
        ----------
        value: float, int or iterable
            Value to set. If a scalar is given, the `pos` parameter should be provided to indicate which component to set.
            If an iterable is provided, create a vector of spectral decomposition parameters corresponding to it.
        pos: int, optional
            Indicate in which component to set the `value`.
        """

        # TODO: - check for the dimensionality of the arguments

        if isinstance(value, (float, int)) and pos is not None and self.C is not None:
            self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params,
                                    description="spectral component "+str(pos+1)+" of the short-wave radiation of the atmosphere",
                                    return_dimensional=True)
        elif hasattr(value, "__iter__"):
            self._create_insolation(value)
        else:
            warnings.warn('A scalar value was provided, but without the `pos` argument indicating in which ' +
                          'component of the spectral decomposition to put it: Spectral decomposition unchanged !' +
                          'Please specify it or give a vector as `value`.')

    def _create_insolation(self, values):

        if hasattr(values, "__iter__"):
            dim = len(values)
        else:
            dim = values
            values = dim * [0.]

        d = ["spectral component "+str(pos+1)+" of the short-wave radiation of the atmosphere" for pos in range(dim)]

        self.C = self.create_params_array(values, units='[W][m^-2]', scale_object=self._scale_params,
                                          description=d, return_dimensional=True)

    def set_thetas(self, value, pos=None):
        """Function to define the spectral decomposition of the Newtonian cooling
        :math:`\\theta^\\star` (:attr:`~.AtmosphericTemperatureParams.thetas`).

        Parameters
        ----------
        value: float, int or iterable
            Value to set. If a scalar is given, the `pos` parameter should be provided to indicate which component to set.
            If an iterable is provided, create a vector of spectral decomposition parameters corresponding to it.
        pos: int, optional
            Indicate in which component to set the `value`.
        """

        # TODO: - check for the dimensionality of the arguments

        if isinstance(value, (float, int)) and pos is not None and self.thetas is not None:
            self.thetas[pos] = Parameter(value, scale_object=self._scale_params,
                                         description="spectral components "+str(pos+1)+" of the temperature profile",
                                         return_dimensional=False, input_dimensional=False)
        elif hasattr(value, "__iter__"):
            self._create_thetas(value)
        else:
            warnings.warn('A scalar value was provided, but without the `pos` argument indicating in which ' +
                          'component of the spectral decomposition to put it: Spectral decomposition unchanged !' +
                          'Please specify it or give a vector as `value`.')

    def _create_thetas(self, values):

        if hasattr(values, "__iter__"):
            dim = len(values)
        else:
            dim = values
            values = dim * [0.]

        d = ["spectral component "+str(pos+1)+" of the temperature profile" for pos in range(dim)]

        self.thetas = self.create_params_array(values, scale_object=self._scale_params,
                                               description=d, return_dimensional=False, input_dimensional=False)

class OceanicParams(Params):
    """Class containing the oceanic parameters

    Parameters
    ----------
    scale_params: ScaleParams
        The scale parameters object of the model.
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.

    Attributes
    ----------
    gp: Parameter
        Reduced gravity in [:math:`m \\, s^{-2}`].
    r: Parameter
        Friction coefficient at the bottom of the ocean in [:math:`s^{-1}`].
    h: Parameter
        Depth of the water layer of the ocean, in meters [:math:`m`].
    d: Parameter
        The strength of the ocean-atmosphere mechanical coupling in [:math:`s^{-1}`].
    """
    _name = "Oceanic"

    def __init__(self, scale_params, dic=None):

        Params.__init__(self, dic)

        self._scale_params = scale_params

        self.gp = Parameter(3.1e-2, units='[m][s^-2]', return_dimensional=True, scale_object=scale_params,
                            description='reduced gravity')
        self.r = Parameter(1.e-8, units='[s^-1]', scale_object=scale_params,
                           description="frictional coefficient at the bottom of the ocean")
        self.h = Parameter(5.e2, units='[m]', return_dimensional=True, scale_object=scale_params,
                           description="depth of the water layer of the ocean")
        self.d = Parameter(1.e-8, units='[s^-1]', scale_object=scale_params,
                           description="strength of the ocean-atmosphere mechanical coupling")

        self.set_params(dic)

class OceanicTemperatureParams(Params):
    """Class containing the oceanic temperature parameters

    Parameters
    ----------
    scale_params: ScaleParams
        The scale parameters object of the model.
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.

    Attributes
    ----------
    gamma: None or Parameter
        Specific heat capacity of the ocean [:math:`J m^{-2} K^{-1}`].
        Heat exchange scheme is disabled if `None`.
    C: None or ~numpy.ndarray(Parameter)
        Spectral Decomposition of the constant short-wave radiation of the ocean [:math:`W m^{-2}`].
        Heat exchange scheme is disabled if `None`.
    T0: None or Parameter
        Stationary solution for the 0-th order oceanic temperature [:math:`K`].
        Heat exchange scheme is disabled if `None`.
    """

    _name = "Oceanic Temperature"

    def __init__(self, scale_params, dic=None):

        Params.__init__(self, dic)

        self._scale_params = scale_params

        self.gamma = Parameter(2.e8, units='[J][m^-2][K^-1]', scale_object=scale_params, return_dimensional=True,
                               description='specific heat capacity of the ocean')
        self.C = None

        self.T0 = Parameter(285.0, units='[K]', scale_object=scale_params, return_dimensional=True,
                            description="stationary solution for the 0-th order oceanic temperature")

        self.set_params(dic)

    def set_insolation(self, value, pos=None):
        """Function to define the spectral decomposition of the constant short-wave radiation of the ocean (insolation)
        :math:`C_{{\\rm o}, i}` (:attr:`~.OceanicTemperatureParams.C`).

        Parameters
        ----------
        value: float, int or iterable
            Value to set. If a scalar is given, the `pos` parameter should be provided to indicate which component to set.
            If an iterable is provided, create a vector of spectral decomposition parameters corresponding to it.
        pos: int, optional
            Indicate in which component to set the `value`.
        """

        if isinstance(value, (float, int)) and pos is not None and self.C is not None:
            self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params,
                                    description="spectral component "+str(pos+1)+" of the short-wave radiation of the ocean",
                                    return_dimensional=True)
        elif hasattr(value, "__iter__"):
            self._create_insolation(value)
        else:
            warnings.warn('A scalar value was provided, but without the `pos` argument indicating in which ' +
                          'component of the spectral decomposition to put it: Spectral decomposition unchanged !' +
                          'Please specify it or give a vector as `value`.')

    def _create_insolation(self, values):

        if hasattr(values, "__iter__"):
            dim = len(values)
        else:
            dim = values
            values = dim * [0.]

        d = ["spectral component "+str(pos+1)+" of the short-wave radiation of the ocean" for pos in range(dim)]

        self.C = self.create_params_array(values, units='[W][m^-2]', scale_object=self._scale_params,
                                          description=d, return_dimensional=True)

class GroundParams(Params):
    """Class containing the ground parameters

    Parameters
    ----------
    scale_params: ScaleParams
        The scale parameters object of the model.
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.

    Attributes
    ----------
    hk: None or ~numpy.ndarray(float)
        Orography spectral decomposition coefficients (non-dimensional), an array of shape (:attr:`~QgParams.nmod` [0],).
        Orography is disabled (flat) if `None`.
    orographic_basis: str
        String to select which component basis modes to use to develop the orography in series.
        Can be either 'atmospheric' or 'ground'. Default to 'atmospheric'.
    """
    _name = "Ground"

    def __init__(self, scale_params, dic=None):

        Params.__init__(self, dic)

        self._scale_params = scale_params

        self.hk = None  # spectral orography coefficients

        self.orographic_basis = "atmospheric"

        self.set_params(dic)

    def set_orography(self, value, pos=None, basis="atmospheric"):
        """Function to define the spectral decomposition of the orography profile
        :math:`h_k` (:attr:`~.GroundParams.hk`).

        Parameters
        ----------
        value: float, int or iterable
            Value to set. If a scalar is given, the `pos` parameter should be provided to indicate which component to set.
            If an iterable is provided, create a vector of spectral decomposition parameters corresponding to it.
        pos: int, optional
            Indicate in which component to set the `value`.
        basis: str, optional
            Indicate which basis should be used to decompose the orography. Can be either `atmospheric`, `oceanic` or `ground`.
            Default to `atmospheric`.
        """

        # TODO: - check for the dimensionality of the arguments
        #       - check that inner products are symbolic if basis is not 'atmospheric'

        self.orographic_basis = basis

        if isinstance(value, (float, int)) and pos is not None and self.hk is not None:
            self.hk[pos] = Parameter(value, scale_object=self._scale_params,
                                     description="spectral components "+str(pos+1)+" of the orography",
                                     return_dimensional=False, input_dimensional=False)
        elif hasattr(value, "__iter__"):
            self._create_orography(value)
        else:
            warnings.warn('A scalar value was provided, but without the `pos` argument indicating in which ' +
                          'component of the spectral decomposition to put it: Spectral decomposition unchanged !' +
                          'Please specify it or give a vector as `value`.')

    def _create_orography(self, values):

        if hasattr(values, "__iter__"):
            dim = len(values)
        else:
            dim = values
            values = dim * [0.]

        d = ["spectral component "+str(pos+1)+" of the orography" for pos in range(dim)]

        self.hk = self.create_params_array(values, scale_object=self._scale_params,
                                           description=d, return_dimensional=False, input_dimensional=False)

class GroundTemperatureParams(Params):
    """Class containing the ground temperature parameters

    Parameters
    ----------
    scale_params: ScaleParams
        The scale parameters object of the model.
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.

    Attributes
    ----------
    gamma: None or Parameter
        Specific heat capacity of the ground [:math:`J m^{-2} K^{-1}`].
        Heat exchange scheme is disabled if `None`.
    C: None or ~numpy.ndarray(Parameter)
        Spectral decomposition of the constant short-wave radiation of the ground [:math:`W m^{-2}`].
        Heat exchange scheme is disabled if `None`.
    T0: None or Parameter
        Stationary solution for the 0-th order ground temperature [:math:`K`].
        Heat exchange scheme is disabled if `None`.
    """

    _name = "Ground Temperature"

    def __init__(self, scale_params, dic=None):

        Params.__init__(self, dic)

        self._scale_params = scale_params

        self.gamma = Parameter(2.e8, units='[J][m^-2][K^-1]', scale_object=scale_params, return_dimensional=True,
                               description='specific heat capacity of the ground')
        self.C = None

        self.T0 = Parameter(285.0, units='[K]', scale_object=scale_params, return_dimensional=True,
                            description="stationary solution for the 0-th order ground temperature")

        self.set_params(dic)

    def set_insolation(self, value, pos=None):
        """Function to define the decomposition of the constant short-wave radiation of the ground (insolation)
        :math:`C_{{\\rm g}, i}` (:attr:`~.GroundTemperatureParams.C`).

        Parameters
        ----------
        value: float, int or iterable
            Value to set. If a scalar is given, the `pos` parameter should be provided to indicate which component to set.
            If an iterable is provided, create a vector of spectral decomposition parameters corresponding to it.
        pos: int, optional
            Indicate in which component to set the `value`.
        """

        # TODO: - check for the dimensionality of the arguments

        if isinstance(value, (float, int)) and pos is not None and self.C is not None:
            self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params,
                                    description="spectral component "+str(pos+1)+" of the short-wave radiation of the ground",
                                    return_dimensional=True)
        elif hasattr(value, "__iter__"):
            self._create_insolation(value)
        else:
            warnings.warn('A scalar value was provided, but without the `pos` argument indicating in which ' +
                          'component of the spectral decomposition to put it: Spectral decomposition unchanged !' +
                          'Please specify it or give a vector as `value`.')

    def _create_insolation(self, values):

        if hasattr(values, "__iter__"):
            dim = len(values)
        else:
            dim = values
            values = dim * [0.]

        d = ["spectral component "+str(pos+1)+" of the short-wave radiation of the ground" for pos in range(dim)]

        self.C = self.create_params_array(values, units='[W][m^-2]', scale_object=self._scale_params,
                                          description=d, return_dimensional=True)

class QgParams(Params):
    """General qgs parameters container.

    Parameters
    ----------
    dic: dict(float or Parameter), optional
        A dictionary with the parameters names and values to be assigned.
    scale_params: None or ScaleParams, optional
        Scale parameters instance.
        If `None`, create a new ScaleParams instance. Default to None.
        Default to `None`.
    atmospheric_params: bool, None or AtmosphericParams, optional
        Atmospheric parameters instance.
        If 'True`, create a new AtmosphericParams instance.
        If `None`, atmospheric parameters are disabled.
        Default to `True`.
    atemperature_params: bool, None or AtmosphericTemperatureParams, optional
        Atmospheric temperature parameters instance.
        If 'True`, create a new AtmosphericTemperatureParams instance.
        If `None`, atmospheric temperature parameters are disabled.
        Default to `True`.
    oceanic_params: bool, None or OceanicParams, optional
        Oceanic parameters instance.
        If 'True`, create a new OceanicParams instance.
        If `None`, oceanic parameters are disabled.
        Default to `None`.
    otemperature_params: bool, None or OceanicTemperatureParams, optional
        Oceanic temperature parameters instance.
        If 'True`, create a new OceanicTemperatureParams instance.
        If `None`, oceanic temperature parameters are disabled.
        Default to `None`.
    ground_params: bool, None or GroundParams, optional
        Ground parameters instance.
        If 'True`, create a new GroundParams instance.
        If `None`, ground parameters are disabled.
        Default to `True`.
    gtemperature_params: bool, None or GroundTemperatureParams, optional
        Ground temperature parameters instance.
        If 'True`, create a new GroundTemperatureParams instance.
        If `None`, ground temperature parameters are disabled.
        Default to `None`.

    Attributes
    ----------
    scale_params: ScaleParams
        Scale parameters instance.
    atmospheric_params: None or AtmosphericParams
        Atmospheric parameters instance.
        If `None`, atmospheric parameters are disabled.
    atemperature_params: None or AtmosphericTemperatureParams
        Atmospheric temperature parameters instance.
        If `None`, atmospheric temperature parameters are disabled.
    oceanic_params: None or OceanicParams
        Oceanic parameters instance.
        If `None`, oceanic parameters are disabled.
    ground_params: None or GroundParams
        Ground parameters instance
        If `None`, ground parameters are disabled.
    gotemperature_params: None, OceanicTemperatureParams or GroundTemperatureParams
        Ground or Oceanic temperature parameters instance.
        If `None`, ground and oceanic temperature parameters are disabled.
    time_unit: float
        Dimensional unit of time to be used to represent the data.
    rr: Parameter
        `Gas constant`_ of `dry air`_ in [:math:`J \\, kg^{-1} \\, K^{-1}`].
    sb: float
        `Stefan-Boltzmann constant`_ in [:math:`J \\, m^{-2} \\, s^{-1} \\, K^{-4}`].

    .. _Gas constant: https://en.wikipedia.org/wiki/Gas_constant
    .. _dry air: https://en.wikipedia.org/wiki/Gas_constant#Specific_gas_constant
    .. _Stefan-Boltzmann constant: https://en.wikipedia.org/wiki/Stefan%E2%80%93Boltzmann_constant

    """
    _name = "General"

    def __init__(self, dic=None, scale_params=None,
                 atmospheric_params=True, atemperature_params=True,
                 oceanic_params=None, otemperature_params=None,
                 ground_params=True, gtemperature_params=None):

        Params.__init__(self, dic)

        # General scale parameters object (Mandatory param block)
        if scale_params is None:
            self.scale_params = ScaleParams(dic)
        else:
            self.scale_params = scale_params

        # Atmospheric parameters object
        if atmospheric_params is True:
            self.atmospheric_params = AtmosphericParams(self.scale_params, dic=dic)
        else:
            self.atmospheric_params = atmospheric_params

        # Atmospheric temperature parameters object
        if atmospheric_params is True:
            self.atemperature_params = AtmosphericTemperatureParams(self.scale_params, dic=dic)
        else:
            self.atemperature_params = atemperature_params

        if oceanic_params is True:
            self.oceanic_params = OceanicParams(self.scale_params, dic)
        else:
            self.oceanic_params = oceanic_params

        if ground_params is True:
            self.ground_params = GroundParams(self.scale_params, dic)
        else:
            self.ground_params = ground_params

        if otemperature_params is True:
            self.gotemperature_params = OceanicTemperatureParams(self.scale_params, dic)
        else:
            self.gotemperature_params = otemperature_params

        if gtemperature_params is True:
            self.gotemperature_params = GroundTemperatureParams(self.scale_params, dic)
        else:
            self.gotemperature_params = gtemperature_params

        self._atmospheric_basis = None
        self._oceanic_basis = None
        self._ground_basis = None
        self._number_of_dimensions = 0

        self._number_of_atmospheric_modes = 0
        self._number_of_oceanic_modes = 0
        self._number_of_ground_modes = 0
        self._ams = None
        self._oms = None
        self._gms = None

        self._atmospheric_latex_var_string = list()
        self._atmospheric_var_string = list()
        self._oceanic_latex_var_string = list()
        self._oceanic_var_string = list()
        self._ground_latex_var_string = list()
        self._ground_var_string = list()
        self._components_units = [r'm$^2$s$^{-1}$', r'K', r'm$^2$s$^{-1}$', r'K']
        self.time_unit = 'days'

        # Physical constants

        self.rr = Parameter(287.058e0, return_dimensional=True, units='[J][kg^-1][K^-1]',
                            scale_object=self.scale_params, description="gas constant of dry air")
        self.sb = Parameter(5.67e-8, return_dimensional=True, units='[J][m^-2][s^-1][K^-4]',
                            scale_object=self.scale_params, description="Stefan-Boltzmann constant")

        self.set_params(dic)

    # -----------------------------------------------------------
    # Derived Quantities (Parameters)
    # -----------------------------------------------------------

    @property
    def LR(self):
        """float: Reduced Rossby deformation radius :math:`L_\\text{R} = \\sqrt{g' \\, h } / f_0` ."""
        op = self.oceanic_params
        scp = self.scale_params
        if op is not None:
            try:
                return np.sqrt(op.gp * op.h) / scp.f0
            except:
                return None
        else:
            return None

    @property
    def G(self):
        """float: The :math:`G = - L^2/L_R^2` parameter."""
        scp = self.scale_params
        if self.LR is not None:
            try:
                return -scp.L**2 / self.LR**2
            except:
                return None
        else:
            return None

    @property
    def Cpgo(self):
        """float: The :math:`C\'_{{\\rm g/\\rm o},i} = R C_{{\\rm g/\\rm o},i} / (\\gamma_{\\rm g/\\rm o} L^2 f_0^3)` parameter."""
        gotp = self.gotemperature_params
        scp = self.scale_params
        if gotp is not None:
            try:
                return gotp.C / (gotp.gamma * scp.f0) * self.rr / (scp.f0 ** 2 * scp.L ** 2)
            except:
                return None
        else:
            return None

    @property
    def Lpgo(self):
        """float: The :math:`\\lambda\'_{{\\rm g/\\rm o}} = \\lambda/(\\gamma_{\\rm g/\\rm o} f_0)` parameter."""
        atp = self.atemperature_params
        gotp = self.gotemperature_params
        scp = self.scale_params
        if atp is not None and gotp is not None:
            try:
                return atp.hlambda / (gotp.gamma * scp.f0)
            except:
                return None
        else:
            return None

    @property
    def Cpa(self):
        """float: The :math:`C\'_{{\\rm a},i} = R C_{{\\rm a},i} / (2 \\gamma_{\\rm a} L^2 f_0^3)` parameter."""
        atp = self.atemperature_params
        scp = self.scale_params
        if atp is not None:
            try:
                return atp.C / (atp.gamma * scp.f0) * self.rr / (scp.f0 ** 2 * scp.L ** 2) / 2
            except:
                return None
        else:
            return None

    @property
    def Lpa(self):
        """float: The :math:`\\lambda\'_{\\rm a} = \\lambda / (\\gamma_{\\rm a} f_0)` parameter."""
        atp = self.atemperature_params
        scp = self.scale_params
        if atp is not None:
            try:
                return atp.hlambda / (atp.gamma * scp.f0)
            except:
                return None
        else:
            return None

    @property
    def sbpgo(self):
        """float: Long wave radiation lost by ground/ocean to the atmosphere :math:`s_{B,{\\rm g/\\rm o}} = 4\\,\\sigma_B \\, T_{{\\rm a},0}^3 / (\\gamma_{\\rm g/\\rm o} f_0)`."""
        gotp = self.gotemperature_params
        scp = self.scale_params
        if gotp is not None:
            try:
                return 4 * self.sb * gotp.T0 ** 3 / (gotp.gamma * scp.f0)
            except:
                return None
        else:
            return None

    @property
    def sbpa(self):
        """float: Long wave radiation from atmosphere absorbed by ground/ocean :math:`s_{B,{\\rm a}} = 4\\,\\epsilon_{\\rm a}\\, \\sigma_B \\, T_{{\\rm a},0}^3 / (\\gamma_{\\rm g/\\rm o} f_0)`."""
        atp = self.atemperature_params
        gotp = self.gotemperature_params
        scp = self.scale_params
        if gotp is not None and atp is not None:
            try:
                return 8 * atp.eps * self.sb * atp.T0 ** 3 / (gotp.gamma * scp.f0)
            except:
                return None
        else:
            return None

    @property
    def LSBpgo(self):
        """float: Long wave radiation from ground/ocean absorbed by atmosphere :math:`S_{B,{\\rm g/\\rm o}} = 2\\,\\epsilon_{\\rm a}\\, \\sigma_B \\, T_{{\\rm a},0}^3 / (\\gamma_{\\rm a} f_0)`."""
        atp = self.atemperature_params
        gotp = self.gotemperature_params
        scp = self.scale_params
        if atp is not None and gotp is not None:
            try:
                return 2 * atp.eps * self.sb * gotp.T0 ** 3 / (atp.gamma * scp.f0)
            except:
                return None
        else:
            return None

    @property
    def LSBpa(self):
        """float: Long wave radiation lost by atmosphere to space & ground/ocean :math:`S_{B,{\\rm a}} = 8\\,\\epsilon_{\\rm a}\\, \\sigma_B \\, T_{{\\rm a},0}^3 / (\\gamma_{\\rm a} f_0)`."""
        atp = self.atemperature_params
        scp = self.scale_params
        if atp is not None:
            try:
                return 8 * atp.eps * self.sb * atp.T0 ** 3 / (atp.gamma * scp.f0)
            except:
                return None
        else:
            return None

    # The following properties might be refactored if the unit system of the model get more widespread across modules.
    @property
    def streamfunction_scaling(self):
        """float: Dimensional scaling of the streamfunction fields."""
        return self.scale_params.L**2 * self.scale_params.f0

    @property
    def temperature_scaling(self):
        """float: Dimensional scaling of the temperature fields."""
        return self.streamfunction_scaling * self.scale_params.f0 / self.rr

    @property
    def geopotential_scaling(self):
        """float: Dimensional scaling of the geopotential height."""
        return self.scale_params.f0 / 9.81

    def set_params(self, dic):
        """Set the specified parameters values.

        Parameters
        ----------
        dic: dict(float or Parameter)
            A dictionary with the parameters names and values to be assigned.
        """
        if dic is not None:
            for key, val in zip(dic.keys(), dic.values()):
                if key in self.__dict__.keys():
                    self.__dict__[key] = val

            if 'scale_params' in self.__dict__.keys():
                self.scale_params.set_params(dic)
            if 'atmospheric_params' in self.__dict__.keys():
                if self.atmospheric_params is not None:
                    self.atmospheric_params.set_params(dic)

            if 'atemperature_params' in self.__dict__.keys():
                if self.atemperature_params is not None:
                    self.atemperature_params.set_params(dic)

            if 'oceanic_params' in self.__dict__.keys():
                if self.oceanic_params is not None:
                    self.oceanic_params.set_params(dic)

            if 'ground_params' in self.__dict__.keys():
                if self.ground_params is not None:
                    self.ground_params.set_params(dic)

            if 'otemperature_params' in self.__dict__.keys():
                if self.gotemperature_params is not None:
                    self.gotemperature_params.set_params(dic)

            if 'gtemperature_params' in self.__dict__.keys():
                if self.gotemperature_params is not None:
                    self.gotemperature_params.set_params(dic)

    def print_params(self):
        """Print all the parameters in the container."""
        s = self._list_params()+"\n"
        if 'scale_params' in self.__dict__.keys():
            s += self.scale_params._list_params()+"\n"
        if 'atmospheric_params' in self.__dict__.keys():
            if self.atmospheric_params is not None:
                s += self.atmospheric_params._list_params()+"\n"

        if 'atemperature_params' in self.__dict__.keys():
            if self.atemperature_params is not None:
                s += self.atemperature_params._list_params()+"\n"

        if 'oceanic_params' in self.__dict__.keys():
            if self.oceanic_params is not None:
                s += self.oceanic_params._list_params()+"\n"

        if 'ground_params' in self.__dict__.keys():
            if self.ground_params is not None:
                s += self.ground_params._list_params()+"\n"

        if 'gotemperature_params' in self.__dict__.keys():
            if self.gotemperature_params is not None:
                s += self.gotemperature_params._list_params() + "\n"

        print("Qgs v0.2.5 parameters summary")
        print("=============================\n")
        print(s)

    @property
    def ndim(self):
        """int: Total number of variables of the model."""
        return self._number_of_dimensions

    @property
    def nmod(self):
        """(int, int): Atmospheric and ground/oceanic number of modes."""
        if self._number_of_oceanic_modes != 0:
            return [self._number_of_atmospheric_modes, self._number_of_oceanic_modes]
        else:
            return [self._number_of_atmospheric_modes, self._number_of_ground_modes]

    @property
    def var_string(self):
        """list(str): List of model's variable names."""
        ls = list()
        for var in self._atmospheric_var_string+self._oceanic_var_string+self._ground_var_string:
            ls.append(var)

        return ls

    @property
    def latex_var_string(self):
        """list(str): List of model's variable names, ready for use in latex."""
        ls = list()
        for var in self._atmospheric_latex_var_string+self._oceanic_latex_var_string+self._ground_latex_var_string:
            ls.append(r'{\ '[0:-1] + var + r'}')

        return ls

    @property
    def latex_components_units(self):
        """list(str): The units of every model's components variables, as a list of latex strings."""
        return self._components_units

    def get_variable_units(self, i):
        """Return the units of a model's variable as a string containing latex symbols.

        Parameters
        ----------
        i: int
            The number of the variable.

        Returns
        -------
        str:
            The string with the units of the variable.
        """
        if i >= self.ndim:
            warnings.warn("Variable " + str(i) + " doesn't exist, cannot return its units.")
            return None
        else:
            natm = self.nmod[0]
            ngoc = self.nmod[1]
            if i < natm:
                return self._components_units[0]
            if natm <= i < 2 * natm:
                return self._components_units[1]
            if self.oceanic_basis is not None:
                if 2 * natm <= i < 2 * natm + ngoc:
                    return self._components_units[2]
                if 2 * natm + ngoc <= i < 2 * natm + 2 * ngoc:
                    return self._components_units[3]
            if self.ground_basis is not None:
                if 2 * natm <= i < 2 * natm + ngoc:
                    return self._components_units[3]

    @property
    def dimensional_time(self):
        """float: Return the conversion factor between the non-dimensional time and the dimensional time unit specified
        in :attr:`.time_unit`"""
        c = 24 * 3600
        if self.time_unit == 'hours':
            c = 3600
        if self.time_unit == 'days':
            c = 24 * 3600
        if self.time_unit == 'years':
            c = 24 * 3600 * 365
        return 1 / (self.scale_params.f0 * c)

    # -------------------------------------------------------------------
    # Config setters to be used with symbolic inner products
    # -------------------------------------------------------------------

    @property
    def atmospheric_basis(self):
        """Basis: The atmospheric basis of functions used to project the PDEs onto."""
        return self._atmospheric_basis

    @atmospheric_basis.setter
    def atmospheric_basis(self, basis):
        self._ams = None
        self._oms = None
        self._gms = None

        self._atmospheric_basis = basis
        self._number_of_atmospheric_modes = len(basis.functions)
        self._number_of_dimensions = 2 * self._number_of_atmospheric_modes
        if self._number_of_oceanic_modes != 0:
            self._number_of_dimensions += 2 * self._number_of_oceanic_modes
        if self._number_of_ground_modes != 0:
            self._number_of_dimensions += self._number_of_ground_modes

        if self.ground_params is not None and self.ground_params.orographic_basis == "atmospheric_basis":
            self.ground_params.set_orography(self._number_of_atmospheric_modes * [0.e0])

        if self.atemperature_params is not None:
            self.atemperature_params.set_thetas(self._number_of_atmospheric_modes * [0.e0])

    @property
    def oceanic_basis(self):
        """Basis: The oceanic basis of functions used to project the PDEs onto."""
        return self._oceanic_basis

    @oceanic_basis.setter
    def oceanic_basis(self, basis):
        self._ams = None
        self._oms = None
        self._gms = None

        self._oceanic_basis = basis

        if self.atemperature_params is not None:
            # disable the Newtonian cooling
            self.atemperature_params.thetas = None
            self.atemperature_params.hd = None

            self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]', scale_object=self.scale_params,
                                                       description='specific heat capacity of the atmosphere',
                                                       return_dimensional=True)
            self.atemperature_params.set_insolation(self.nmod[0] * [0.e0])
            self.atemperature_params.set_insolation(100.0, 0)
            self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False,
                                                     description="emissivity coefficient for the grey-body atmosphere")
            self.atemperature_params.T0 = Parameter(270.0, units='[K]', scale_object=self.scale_params,
                                                    return_dimensional=True,
                                                    description="stationary solution for the 0-th order atmospheric temperature")
            self.atemperature_params.sc = Parameter(1., input_dimensional=False,
                                                    description="ratio of surface to atmosphere temperature")
            self.atemperature_params.hlambda = Parameter(20.00, units='[W][m^-2][K^-1]', scale_object=self.scale_params,
                                                         return_dimensional=True,
                                                         description="sensible+turbulent heat exchange between ocean and atmosphere")

        if self.gotemperature_params is not None:
            self._number_of_ground_modes = 0
            self._number_of_oceanic_modes = len(basis)
            self._number_of_dimensions = 2 * self._number_of_atmospheric_modes + 2 * self._number_of_oceanic_modes
            self.gotemperature_params.set_insolation(self.nmod[0] * [0.e0])
            self.gotemperature_params.set_insolation(350.0, 0)
            # if setting an ocean, then disable the orography
            if self.ground_params is not None:
                self.ground_params.hk = None

    @property
    def ground_basis(self):
        """Basis: The ground basis of functions used to project the PDEs onto."""
        return self._ground_basis

    @ground_basis.setter
    def ground_basis(self, basis):
        self._ams = None
        self._oms = None
        self._gms = None

        self._ground_basis = basis

        if self.atemperature_params is not None:
            # disable the Newtonian cooling
            self.atemperature_params.thetas = None
            self.atemperature_params.hd = None

            self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]', scale_object=self.scale_params,
                                                       description='specific heat capacity of the atmosphere',
                                                       return_dimensional=True)
            self.atemperature_params.set_insolation(self.nmod[0] * [0.e0])
            self.atemperature_params.set_insolation(100.0, 0)
            self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False,
                                                     description="emissivity coefficient for the grey-body atmosphere")
            self.atemperature_params.T0 = Parameter(270.0, units='[K]', scale_object=self.scale_params,
                                                    return_dimensional=True,
                                                    description="stationary solution for the 0-th order atmospheric temperature")
            self.atemperature_params.sc = Parameter(1., input_dimensional=False,
                                                    description="ratio of surface to atmosphere temperature")
            self.atemperature_params.hlambda = Parameter(20.00, units='[W][m^-2][K^-1]', scale_object=self.scale_params,
                                                         return_dimensional=True,
                                                         description="sensible+turbulent heat exchange between ocean and atmosphere")

        if self.gotemperature_params is not None:
            self._number_of_ground_modes = len(basis)
            self._number_of_oceanic_modes = 0
            self._number_of_dimensions = 2 * self._number_of_atmospheric_modes + 2 * self._number_of_oceanic_modes
            # if orography is disabled, enable it!
            if self.ground_params is not None:
                if self.ground_params.hk is None:
                    if self.ground_params.orographic_basis == 'atmospheric':
                        self.ground_params.set_orography(self._number_of_atmospheric_modes * [0.e0])
                    else:
                        self.ground_params.set_orography(self._number_of_ground_modes * [0.e0])
                    self.ground_params.set_orography(0.1, 1)
            self.gotemperature_params.set_insolation(self.nmod[0] * [0.e0])
            self.gotemperature_params.set_insolation(350.0, 0)

    def set_atmospheric_modes(self, basis, auto=False):
        """Function to configure the atmospheric modes (basis functions) used to project the PDEs onto.

        Parameters
        ----------
        basis: Basis
            Basis object containing the definition of the atmospheric modes.
        auto: bool, optional
            Automatically instantiate the parameters container needed to describe the atmospheric models parameters.
            Default is False.

        Examples
        --------

        >>> from qgs.params.params import QgParams
        >>> from qgs.basis.fourier import contiguous_channel_basis
        >>> q = QgParams()
        >>> atm_basis = contiguous_channel_basis(2, 2, 1.5)
        >>> q.set_atmospheric_modes(atm_basis)
        >>> q.atmospheric_basis
        [sqrt(2)*cos(y), 2*sin(y)*cos(n*x), 2*sin(y)*sin(n*x), sqrt(2)*cos(2*y), 2*sin(2*y)*cos(n*x), 2*sin(2*y)*sin(n*x), 2*sin(y)*cos(2*n*x), 2*sin(y)*sin(2*n*x), 2*sin(2*y)*cos(2*n*x), 2*sin(2*y)*sin(2*n*x)]

        """

        if auto:
            if self.atemperature_params is None:
                self.atemperature_params = AtmosphericTemperatureParams(self.scale_params)
            if self.atmospheric_params is None:
                self.atmospheric_params = AtmosphericParams(self.scale_params)

        self.atmospheric_basis = basis

        self._atmospheric_latex_var_string = list()
        self._atmospheric_var_string = list()
        for i in range(self.nmod[0]):
            self._atmospheric_latex_var_string.append(r'psi_{\rm a,' + str(i + 1) + "}")
            self._atmospheric_var_string.append(r'psi_a_' + str(i + 1))
        for i in range(self.nmod[0]):
            self._atmospheric_latex_var_string.append(r'theta_{\rm a,' + str(i + 1) + "}")
            self._atmospheric_var_string.append(r'theta_a_' + str(i + 1))

    def set_oceanic_modes(self, basis, auto=True):
        """Function to configure the oceanic modes (basis functions) used to project the PDEs onto.

        Parameters
        ----------
        basis: Basis
            Basis object containing the definition of the oceanic modes.
        auto: bool, optional
            Automatically instantiate or not the parameters container needed to describe the oceanic models parameters.
            Default is True.

        Examples
        --------

        >>> from qgs.params.params import QgParams
        >>> from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis
        >>> q = QgParams()
        >>> atm_basis = contiguous_channel_basis(2, 2, 1.5)
        >>> oc_basis = contiguous_basin_basis(2, 4, 1.5)
        >>> q.set_atmospheric_modes(atm_basis)
        >>> q.set_oceanic_modes(oc_basis)
        >>> q.oceanic_basis
        [2*sin(y)*sin(0.5*n*x), 2*sin(2*y)*sin(0.5*n*x), 2*sin(3*y)*sin(0.5*n*x), 2*sin(4*y)*sin(0.5*n*x), 2*sin(y)*sin(1.0*n*x), 2*sin(2*y)*sin(1.0*n*x), 2*sin(3*y)*sin(1.0*n*x), 2*sin(4*y)*sin(1.0*n*x)]

        """
        if self._atmospheric_basis is None:  # Presently, the ocean can not yet be set independently of an atmosphere.
            print('Atmosphere modes not set up. Add an atmosphere before adding an ocean!')
            print('Oceanic setup aborted.')
            return

        if auto:
            if self.gotemperature_params is None or isinstance(self.gotemperature_params, GroundTemperatureParams):
                self.gotemperature_params = OceanicTemperatureParams(self.scale_params)
            if self.oceanic_params is None:
                self.oceanic_params = OceanicParams(self.scale_params)

            self.ground_params = None

        self.oceanic_basis = basis

        self._oceanic_latex_var_string = list()
        self._oceanic_var_string = list()
        self._ground_latex_var_string = list()
        self._ground_var_string = list()
        for i in range(self.nmod[1]):
            self._oceanic_latex_var_string.append(r'psi_{\rm o,' + str(i + 1) + "}")
            self._oceanic_var_string.append(r'psi_o_' + str(i + 1))
        for i in range(self.nmod[1]):
            self._oceanic_latex_var_string.append(r'theta_{\rm o,' + str(i + 1) + "}")
            self._oceanic_var_string.append(r'theta_o_' + str(i + 1))

    def set_ground_modes(self, basis=None, auto=True):
        """Function to configure the ground modes (basis functions) used to project the PDEs onto.

        Parameters
        ----------
        basis: None or Basis, optional
            Basis object containing the definition of the ground modes. If `None`, use the basis of the atmosphere.
            Default to `None`.
        auto: bool, optional
            Automatically instantiate or not the parameters container needed to describe the ground models parameters.
            Default is True.

        Examples
        --------

        >>> from qgs.params.params import QgParams
        >>> from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis
        >>> q = QgParams()
        >>> atm_basis = contiguous_channel_basis(2, 2, 1.5)
        >>> q.set_atmospheric_modes(atm_basis)
        >>> q.set_ground_modes()
        >>> q.ground_basis
        [sqrt(2)*cos(y), 2*sin(y)*cos(n*x), 2*sin(y)*sin(n*x), sqrt(2)*cos(2*y), 2*sin(2*y)*cos(n*x), 2*sin(2*y)*sin(n*x), 2*sin(y)*cos(2*n*x), 2*sin(y)*sin(2*n*x), 2*sin(2*y)*cos(2*n*x), 2*sin(2*y)*sin(2*n*x)]
        """
        if self._atmospheric_basis is None:  # Presently, the ground can not yet be set independently of an atmosphere.
            print('Atmosphere modes not set up. Add an atmosphere before adding the ground!')
            print('Ground setup aborted.')
            return

        if auto:
            if self.gotemperature_params is None or isinstance(self.gotemperature_params, OceanicTemperatureParams):
                self.gotemperature_params = GroundTemperatureParams(self.scale_params)
            if self.ground_params is None:
                self.ground_params = GroundParams(self.scale_params)

            self.oceanic_params = None

        if basis is not None:
            self.ground_basis = basis
        else:
            self.ground_basis = self._atmospheric_basis

        self._oceanic_var_string = list()
        self._oceanic_latex_var_string = list()
        self._ground_latex_var_string = list()
        self._ground_var_string = list()
        for i in range(self.nmod[1]):
            self._ground_latex_var_string.append(r'theta_{\rm g,' + str(i + 1) + "}")
            self._ground_var_string.append(r'theta_g_' + str(i + 1))

    # -------------------------------------------------------------------
    # Specific basis setters
    # -------------------------------------------------------------------

    def set_atmospheric_channel_fourier_modes(self, nxmax, nymax, auto=False, mode='analytic'):
        """Function to configure and set the basis for contiguous spectral blocks of atmospheric modes on a channel.

        Parameters
        ----------
        nxmax: int
            Maximum x-wavenumber to fill the spectral block up to.
        nymax: int
            Maximum :math:`y`-wavenumber to fill the spectral block up to.
        auto: bool, optional
            Automatically instantiate the parameters container needed to describe the atmospheric models parameters.
            Default is `False`.
        mode: str, optional
            Mode to set the inner products: Either `analytic` or `symbolic`:
            `analytic` for inner products computed with formula or `symbolic` using `Sympy`_.
            Default to `analytic`.

        Examples
        --------

        >>> from qgs.params.params import QgParams
        >>> q = QgParams()
        >>> q.set_atmospheric_channel_fourier_modes(2, 2)
        >>> q.ablocks
        array([[1, 1],
               [1, 2],
               [2, 1],
               [2, 2]])

        .. _Sympy: https://www.sympy.org/

        """

        if mode == 'symbolic':
            basis = contiguous_channel_basis(nxmax, nymax, self.scale_params.n)
            self.set_atmospheric_modes(basis, auto)
        else:
            self._set_atmospheric_analytic_fourier_modes(nxmax, nymax, auto)

    def set_oceanic_basin_fourier_modes(self, nxmax, nymax, auto=True, mode='analytic'):
        """Function to configure and set the basis for contiguous spectral blocks of oceanic modes on a closed basin.

        Parameters
        ----------
        nxmax: int
            Maximum x-wavenumber to fill the spectral block up to.
        nymax: int
            Maximum :math:`y`-wavenumber to fill the spectral block up to.
        auto: bool, optional
            Automatically instantiate the parameters container needed to describe the atmospheric models parameters.
            Default is `True`.
        mode: str, optional
            Mode to set the inner products: Either `analytic` or `symbolic`.
            `analytic` for inner products computed with formula or `symbolic` using `Sympy`_.
            Default to `analytic`.

        Examples
        --------

        >>> from qgs.params.params import QgParams
        >>> q = QgParams()
        >>> q.set_atmospheric_channel_fourier_modes(2, 2)
        >>> q.set_oceanic_basin_fourier_modes(2, 4)
        >>> q.oblocks
        array([[1, 1],
               [1, 2],
               [1, 3],
               [1, 4],
               [2, 1],
               [2, 2],
               [2, 3],
               [2, 4]])

        .. _Sympy: https://www.sympy.org/
        """

        if mode == 'symbolic':
            basis = contiguous_basin_basis(nxmax, nymax, self.scale_params.n)
            self.set_oceanic_modes(basis, auto)
        else:
            self._set_oceanic_analytic_fourier_modes(nxmax, nymax, auto)

    def set_ground_channel_fourier_modes(self, nxmax=None, nymax=None, auto=True, mode='analytic'):
        """Function to configure and set the basis for contiguous spectral blocks of ground modes on a channel.

        Parameters
        ----------
        nxmax: int
            Maximum x-wavenumber to fill the spectral block up to.
        nymax: int
            Maximum :math:`y`-wavenumber to fill the spectral block up to.
        auto: bool, optional
            Automatically instantiate the parameters container needed to describe the atmospheric models parameters.
            Default is `True`.
        mode: str, optional
            Mode to set the inner products: Either `analytic` or `symbolic`.
            `analytic` for inner products computed with formula or `symbolic` using `Sympy`_.
            Default to `analytic`.

        Examples
        --------

        >>> from qgs.params.params import QgParams
        >>> q = QgParams()
        >>> q.set_atmospheric_channel_fourier_modes(2,4)
        >>> q.set_ground_channel_fourier_modes()
        >>> q.gblocks
        array([[1, 1],
               [1, 2],
               [1, 3],
               [1, 4],
               [2, 1],
               [2, 2],
               [2, 3],
               [2, 4]])

        .. _Sympy: https://www.sympy.org/
        """

        if mode == "symbolic":
            if nxmax is not None and nymax is not None:
                basis = contiguous_channel_basis(nxmax, nymax, self.scale_params.n)
            else:
                basis = None
            self.set_ground_modes(basis, auto)
        else:
            self._set_ground_analytic_fourier_modes(nxmax, nymax, auto)

    # -------------------------------------------------------------------
    # Model configs setter to be used with analytic inner products
    # -------------------------------------------------------------------

    @property
    def ablocks(self):
        """~numpy.ndarray(int): Spectral blocks detailing the model's atmospheric modes :math:`x`- and :math:`y`-wavenumber.
         Array of shape (:attr:`~QgParams.nmod` [0], 2)."""
        return self._ams

    @ablocks.setter
    def ablocks(self, value):
        self._ams = value
        basis = ChannelFourierBasis(self._ams, self.scale_params.n)
        self._atmospheric_basis = basis

        namod = 0
        for i in range(self.ablocks.shape[0]):
            if self.ablocks[i, 0] == 1:
                namod += 3
            else:
                namod += 2

        self._number_of_atmospheric_modes = namod
        self._number_of_dimensions = 2 * namod
        if self._number_of_oceanic_modes != 0:
            self._number_of_dimensions += self._number_of_oceanic_modes
        if self._number_of_ground_modes != 0:
            self._number_of_dimensions += self._number_of_ground_modes
        if self.ground_params is not None:
            self.ground_params.orographic_basis = 'atmospheric'
            self.ground_params.set_orography(namod * [0.e0])
            self.ground_params.set_orography(0.1, 1)
        if self.atemperature_params is not None:
            self.atemperature_params.set_thetas(namod * [0.e0])
            self.atemperature_params.set_thetas(0.1, 0)

    @property
    def oblocks(self):
        """~numpy.ndarray(int): Spectral blocks detailing the model's oceanic modes :math:`x`-and :math:`y`-wavenumber.
         Array of shape (:attr:`~QgParams.nmod` [1], 2)."""
        return self._oms

    @oblocks.setter
    def oblocks(self, value):
        self._oms = value
        self._gms = None
        basis = BasinFourierBasis(self._oms, self.scale_params.n)
        self._oceanic_basis = basis
        self._ground_basis = None

        if self.atemperature_params is not None:
            # disable the Newtonian cooling
            self.atemperature_params.thetas = None  # np.zeros(self.nmod[0])
            self.atemperature_params.hd = None  # Parameter(0.0, input_dimensional=False)

            self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]', scale_object=self.scale_params,
                                                       description='specific heat capacity of the atmosphere',
                                                       return_dimensional=True)
            self.atemperature_params.set_insolation(self.nmod[0] * [0.e0])
            self.atemperature_params.set_insolation(100.0, 0)
            self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False,
                                                     description="emissivity coefficient for the grey-body atmosphere")
            self.atemperature_params.T0 = Parameter(270.0, units='[K]', scale_object=self.scale_params,
                                                    return_dimensional=True,
                                                    description="stationary solution for the 0-th order atmospheric temperature")
            self.atemperature_params.sc = Parameter(1., input_dimensional=False,
                                                    description="ratio of surface to atmosphere temperature")
            self.atemperature_params.hlambda = Parameter(20.00, units='[W][m^-2][K^-1]', scale_object=self.scale_params,
                                                         return_dimensional=True,
                                                         description="sensible+turbulent heat exchange between ocean and atmosphere")

        if self.gotemperature_params is not None:
            self._number_of_ground_modes = 0
            self._number_of_oceanic_modes = self.oblocks.shape[0]
            self._number_of_dimensions = 2 * (self._number_of_oceanic_modes + self._number_of_atmospheric_modes)
            self.gotemperature_params.set_insolation(self.nmod[0] * [0.e0])
            self.gotemperature_params.set_insolation(350.0, 0)
            # if setting an ocean, then disable the orography
            if self.ground_params is not None:
                self.ground_params.hk = None

    @property
    def gblocks(self):
        """~numpy.ndarray(int): Spectral blocks detailing the model's ground modes :math:`x`-and :math:`y`-wavenumber.
         Array of shape (:attr:`~QgParams.nmod` [1], 2)."""
        return self._gms

    @gblocks.setter
    def gblocks(self, value):
        self._oms = None
        self._gms = value
        basis = ChannelFourierBasis(self._gms, self.scale_params.n)
        self._oceanic_basis = None
        self._ground_basis = basis

        if self.atemperature_params is not None:
            # disable the Newtonian cooling
            self.atemperature_params.thetas = None  # np.zeros(self.nmod[0])
            self.atemperature_params.hd = None  # Parameter(0.0, input_dimensional=False)

            self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]',
                                                       scale_object=self.scale_params,
                                                       description='specific heat capacity of the atmosphere',
                                                       return_dimensional=True)
            self.atemperature_params.set_insolation(self.nmod[0] * [0.e0])
            self.atemperature_params.set_insolation(100.0, 0)
            self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False,
                                                     description="emissivity coefficient for the grey-body atmosphere")
            self.atemperature_params.T0 = Parameter(270.0, units='[K]', scale_object=self.scale_params,
                                                    return_dimensional=True,
                                                    description="stationary solution for the 0-th order atmospheric temperature")
            self.atemperature_params.sc = Parameter(1., input_dimensional=False,
                                                    description="ratio of surface to atmosphere temperature")
            self.atemperature_params.hlambda = Parameter(20.00, units='[W][m^-2][K^-1]',
                                                         scale_object=self.scale_params,
                                                         return_dimensional=True,
                                                         description="sensible+turbulent heat exchange between ocean and atmosphere")

        if self.gotemperature_params is not None:
            gmod = 0
            for i in range(self.gblocks.shape[0]):
                if self.ablocks[i, 0] == 1:
                    gmod += 3
                else:
                    gmod += 2
            self._number_of_ground_modes = gmod
            self._number_of_oceanic_modes = 0
            self._number_of_dimensions = 2 * self._number_of_atmospheric_modes + self._number_of_ground_modes
            # if orography is disabled, enable it!
            if self.ground_params is not None:
                self.ground_params.orographic_basis = 'atmospheric'
                if self.ground_params.hk is None:
                    self.ground_params.set_orography(self.nmod[0] * [0.e0])
                    self.ground_params.set_orography(0.1, 1)
            self.gotemperature_params.set_insolation(self.nmod[0] * [0.e0])
            self.gotemperature_params.set_insolation(350.0, 0)

    def _set_atmospheric_analytic_fourier_modes(self, nxmax, nymax, auto=False):

        res = np.zeros((nxmax * nymax, 2), dtype=int)
        i = 0
        for nx in range(1, nxmax + 1):
            for ny in range(1, nymax+1):
                res[i, 0] = nx
                res[i, 1] = ny
                i += 1

        if auto:
            if self.atemperature_params is None:
                self.atemperature_params = AtmosphericTemperatureParams(self.scale_params)
            if self.atmospheric_params is None:
                self.atmospheric_params = AtmosphericParams(self.scale_params)

        self.ablocks = res

        self._atmospheric_latex_var_string = list()
        self._atmospheric_var_string = list()
        for i in range(self.nmod[0]):
            self._atmospheric_latex_var_string.append(r'psi_{\rm a,' + str(i + 1) + "}")
            self._atmospheric_var_string.append(r'psi_a_' + str(i + 1))
        for i in range(self.nmod[0]):
            self._atmospheric_latex_var_string.append(r'theta_{\rm a,' + str(i + 1) + "}")
            self._atmospheric_var_string.append(r'theta_a_' + str(i + 1))

    def _set_oceanic_analytic_fourier_modes(self, nxmax, nymax, auto=True):

        if self._ams is None:
            print('Atmosphere modes not set up. Add an atmosphere before adding an ocean!')
            print('Oceanic setup aborted.')
            return
        res = np.zeros((nxmax * nymax, 2), dtype=int)
        i = 0
        for nx in range(1, nxmax + 1):
            for ny in range(1, nymax+1):
                res[i, 0] = nx
                res[i, 1] = ny
                i += 1

        if auto:
            if self.gotemperature_params is None or isinstance(self.gotemperature_params, GroundTemperatureParams):
                self.gotemperature_params = OceanicTemperatureParams(self.scale_params)
            if self.oceanic_params is None:
                self.oceanic_params = OceanicParams(self.scale_params)

            self.ground_params = None

        self.oblocks = res

        self._oceanic_latex_var_string = list()
        self._oceanic_var_string = list()
        self._ground_latex_var_string = list()
        self._ground_var_string = list()
        for i in range(self.nmod[1]):
            self._oceanic_latex_var_string.append(r'psi_{\rm o,' + str(i + 1) + "}")
            self._oceanic_var_string.append(r'psi_o_' + str(i + 1))
        for i in range(self.nmod[1]):
            self._oceanic_latex_var_string.append(r'theta_{\rm o,' + str(i + 1) + "}")
            self._oceanic_var_string.append(r'theta_o_' + str(i + 1))

    def _set_ground_analytic_fourier_modes(self, nxmax=None, nymax=None, auto=True):

        if self._ams is None:
            print('Atmosphere modes not set up. Add an atmosphere before adding the ground!')
            print('Ground setup aborted.')
            return

        if nxmax is None or nymax is None:
            res = self._ams.copy()
        else:
            res = np.zeros((nxmax * nymax, 2), dtype=int)
            i = 0
            for nx in range(1, nxmax + 1):
                for ny in range(1, nymax+1):
                    res[i, 0] = nx
                    res[i, 1] = ny
                    i += 1

        if auto:
            if self.gotemperature_params is None or isinstance(self.gotemperature_params, OceanicTemperatureParams):
                self.gotemperature_params = GroundTemperatureParams(self.scale_params)
            if self.ground_params is None:
                self.ground_params = GroundParams(self.scale_params)

            self.oceanic_params = None

        self.gblocks = res

        self._oceanic_var_string = list()
        self._oceanic_latex_var_string = list()
        self._ground_latex_var_string = list()
        self._ground_var_string = list()
        for i in range(self.nmod[1]):
            self._ground_latex_var_string.append(r'theta_{\rm g,' + str(i + 1) + "}")
            self._ground_var_string.append(r'theta_g_' + str(i + 1))

max_stars_repo_name: Climdyn/qgs

max_issues_repo_name: Climdyn/qgs

max_forks_repo_name: Climdyn/qgs