From: Jean-Philippe ARGAUD Date: Wed, 10 Mar 2021 05:39:10 +0000 (+0100) Subject: Improvement of user *_STUDY X-Git-Tag: V9_7_0b1~15 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=ac131d2923c613e689a13d812379b8fbcd26d8f6;p=modules%2Fadao.git Improvement of user *_STUDY --- diff --git a/bin/AdaoCatalogGenerator.py b/bin/AdaoCatalogGenerator.py index 04dce79..d7b0e3e 100644 --- a/bin/AdaoCatalogGenerator.py +++ b/bin/AdaoCatalogGenerator.py @@ -41,7 +41,7 @@ try: import adao import daEficas import daYacsSchemaCreator - import daCore.AssimilationStudy + import daCore.Aidsm import daYacsSchemaCreator.infos_daComposant as infos except: logging.fatal("Import of ADAO python modules failed !" + @@ -49,7 +49,10 @@ except: traceback.print_exc() sys.exit(1) +#=============================================================================== + #----------- Templates Part ---------------# + begin_catalog_file = """# -*- coding: utf-8 -*- # # Copyright (C) 2008-2021 EDF R&D @@ -85,7 +88,7 @@ from Accas import * JdC = JDC_CATA ( code = '%s', execmodul = None, - regles = ( AU_MOINS_UN ('ASSIMILATION_STUDY','CHECKING_STUDY'), AU_PLUS_UN ('ASSIMILATION_STUDY','CHECKING_STUDY')), + regles = ( AU_MOINS_UN ('ASSIMILATION_STUDY','OPTIMIZATION_STUDY','REDUCTION_STUDY','CHECKING_STUDY'), AU_PLUS_UN ('ASSIMILATION_STUDY','OPTIMIZATION_STUDY','REDUCTION_STUDY','CHECKING_STUDY')), ) VERSION_CATALOGUE='%s' @@ -314,6 +317,52 @@ ASSIMILATION_STUDY = PROC(nom="ASSIMILATION_STUDY", Observers = F_Observers("f") ) +OPTIMIZATION_STUDY = PROC(nom="OPTIMIZATION_STUDY", + op=None, + repetable = "n", + StudyName = SIMP(statut="o", typ = "TXM", defaut="ADAO Calculation Case"), + StudyRepertory = SIMP(statut="f", typ = "Repertoire", validators=FunctionVal(ChDir), min=1, max=1), + Debug = SIMP(statut="f", typ = "I", into=(0, 1), defaut=0), + ExecuteInContainer = SIMP(statut="f", typ = "TXM", min=1, max=1, defaut = "No", into=("No", "Mono", "Multi")), + AlgorithmParameters = F_AlgorithmParameters("o",({optim_names}), AlgorithmParametersInNS), + Background = F_Background("o", BackgroundInNS), + BackgroundError = F_BackgroundError("f", BackgroundErrorInNS), + Observation = F_Observation("o", ObservationInNS), + ObservationError = F_ObservationError("f", ObservationErrorInNS), + ObservationOperator = F_ObservationOperator("o"), + EvolutionModel = F_EvolutionModel("f"), + EvolutionError = F_EvolutionError("f", EvolutionErrorInNS), + ControlInput = F_ControlInput("f"), + UserDataInit = F_Init("f"), + UserPostAnalysis = F_UserPostAnalysis("o"), + InputVariables = F_variables("f"), + OutputVariables = F_variables("f"), + Observers = F_Observers("f") + ) + +REDUCTION_STUDY = PROC(nom="REDUCTION_STUDY", + op=None, + repetable = "n", + StudyName = SIMP(statut="o", typ = "TXM", defaut="ADAO Calculation Case"), + StudyRepertory = SIMP(statut="f", typ = "Repertoire", validators=FunctionVal(ChDir), min=1, max=1), + Debug = SIMP(statut="f", typ = "I", into=(0, 1), defaut=0), + ExecuteInContainer = SIMP(statut="f", typ = "TXM", min=1, max=1, defaut = "No", into=("No", "Mono", "Multi")), + AlgorithmParameters = F_AlgorithmParameters("o",({reduc_names}), AlgorithmParametersInNS), + Background = F_Background("o", BackgroundInNS), + BackgroundError = F_BackgroundError("o", BackgroundErrorInNS), + Observation = F_Observation("o", ObservationInNS), + ObservationError = F_ObservationError("o", ObservationErrorInNS), + ObservationOperator = F_ObservationOperator("o"), + EvolutionModel = F_EvolutionModel("f"), + EvolutionError = F_EvolutionError("f", EvolutionErrorInNS), + ControlInput = F_ControlInput("f"), + UserDataInit = F_Init("f"), + UserPostAnalysis = F_UserPostAnalysis("o"), + InputVariables = F_variables("f"), + OutputVariables = F_variables("f"), + Observers = F_Observers("f") + ) + CHECKING_STUDY = PROC(nom="CHECKING_STUDY", op=None, repetable = "n", @@ -335,7 +384,7 @@ CHECKING_STUDY = PROC(nom="CHECKING_STUDY", #----------- End of Templates Part ---------------# - +#=============================================================================== #----------- Begin generation script -----------# @@ -352,29 +401,39 @@ args = my_parser.parse_args() # Generates into a string mem_file = io.StringIO() -# Start file +# Initial step: On ouvre le fichier from time import strftime mem_file.write(unicode(begin_catalog_file, 'utf-8').format(**{'date':strftime("%Y-%m-%d %H:%M:%S")})) -# Step initial: on obtient la liste des algos +# Step 0: on obtient la liste des algos algos_names = "" +optim_names = "" +reduc_names = "" check_names = "" decl_algos = "" -assim_study_object = daCore.AssimilationStudy.AssimilationStudy() +algal_names = "" +assim_study_object = daCore.Aidsm.Aidsm() algos_list = assim_study_object.get_available_algorithms() del assim_study_object for algo_name in algos_list: if algo_name in infos.AssimAlgos: logging.debug("An assimilation algorithm is found: " + algo_name) algos_names += "\"" + algo_name + "\", " - elif algo_name in infos.CheckAlgos: + if algo_name in infos.OptimizationAlgos: + logging.debug("An optimization algorithm is found: " + algo_name) + optim_names += "\"" + algo_name + "\", " + if algo_name in infos.ReductionAlgos: + logging.debug("A reduction algorithm is found: " + algo_name) + reduc_names += "\"" + algo_name + "\", " + if algo_name in infos.CheckAlgos: logging.debug("A checking algorithm is found: " + algo_name) check_names += "\"" + algo_name + "\", " - else: - logging.debug("This algorithm is not considered: " + algo_name) + if algo_name in infos.AssimAlgos+infos.OptimizationAlgos+infos.ReductionAlgos+infos.CheckAlgos: + # Pour filtrer sur les algorithmes vraiment interfacés, car il peut y en avoir moins que "algos_list" + algal_names += "\"" + algo_name + "\", " -# Step 1: A partir des infos, on cree les fonctions qui vont permettre -# d'entrer les donnees utilisateur +# Step 1: A partir des infos, on crée les fonctions qui vont permettre +# d'entrer les données utilisateur for data_input_name in infos.DataTypeDict: logging.debug('A data input Type is found: ' + data_input_name) data_name = data_input_name @@ -382,11 +441,11 @@ for data_input_name in infos.DataTypeDict: data_default = "" ms_default = "" - # On recupere les differentes facon d'entrer les donnees + # On récupère les différentes façon d'entrer les données for basic_type in infos.DataTypeDict[data_input_name]: data_into += "\"" + basic_type + "\", " - # On choisit le default + # On choisit le défaut data_default = "\"" + infos.DataTypeDefaultDict[data_input_name] + "\"" if data_input_name in infos.DataSValueDefaultDict: ms_default = " defaut=\"" + infos.DataSValueDefaultDict[data_input_name] + "\"," @@ -396,10 +455,10 @@ for data_input_name in infos.DataTypeDict: 'data_into' : data_into, 'data_default' : data_default, 'ms_default' : ms_default, - 'algos_names' : algos_names+check_names, + #~ 'algos_names' : algal_names, })) -# Step 2: On cree les fonctions qui permettent de rentrer les donnees des algorithmes +# Step 2: On crée les fonctions qui permettent de rentrer les données des algorithmes for assim_data_input_name in infos.AssimDataDict: logging.debug("An input function data input is found: " + assim_data_input_name) # assim_name = assim_data_input_name @@ -425,7 +484,7 @@ for assim_data_input_name in infos.AssimDataDict: 'default_choice' : default_choice, })) -# Step 3: On ajoute les fonctions representant les options possibles +# Step 3: On ajoute les fonctions représentant les options possibles for opt_name in infos.OptDict: logging.debug("An optional node is found: " + opt_name) data_name = opt_name @@ -446,14 +505,14 @@ for opt_name in infos.OptDict: 'data_into' : data_into, 'data_default' : data_default, 'ms_default' : ms_default, - 'algos_names' : algos_names+check_names, + #~ 'algos_names' : algal_names, })) -# Step 4: On ajoute la methode optionnelle init -# TODO uniformiser avec le step 3 +# Step 3bis: On ajoute la méthode optionnelle init +# TODO si possible uniformiser avec le step 3 mem_file.write(unicode(init_method, 'utf-8')) -# Step 5: Add observers +# Step 4: On ajoute les observers decl_choices = "" for obs_var in infos.ObserversList: decl_choices += observers_choice.format(**{'var_name':obs_var}) @@ -462,12 +521,11 @@ mem_file.write(unicode(observers_method, 'utf-8').format(**{ 'decl_choices' : decl_choices, })) -# Step 5: Add algorithmic choices - -all_names = eval((algos_names+check_names)) +# Step 5: On ajoute les choix algorithmiques +all_names = eval((algal_names)) all_algo_defaults = "" for algo in all_names: - assim_study_object = daCore.AssimilationStudy.AssimilationStudy() + assim_study_object = daCore.Aidsm.Aidsm() assim_study_object.setAlgorithmParameters(Algorithm=algo) par_dict = assim_study_object.get("AlgorithmRequiredParameters",False) par_keys = sorted(par_dict.keys()) @@ -505,14 +563,16 @@ for algo in all_names: }) mem_file.write(unicode(algo_choices, 'utf-8').format(**{'all_algo_defaults':unicode(all_algo_defaults, 'utf-8')})) -# Final step: Add algorithm and assim_study +# Step 6: On ajoute l'algorithme et le assim_study mem_file.write(unicode(assim_study, 'utf-8').format(**{ 'algos_names':algos_names, + 'optim_names':optim_names, + 'reduc_names':reduc_names, 'check_names':check_names, 'decl_algos':decl_algos, })) -# Write file +# Final step: On écrit le fichier if sys.version_info.major > 2: with open(os.path.join(args.catalog_path, args.catalog_name), "w", encoding='utf8') as final_file: final_file.write(mem_file.getvalue()) @@ -520,3 +580,7 @@ else: with open(os.path.join(args.catalog_path, args.catalog_name), "wr") as final_file: final_file.write(mem_file.getvalue().encode('utf-8')) mem_file.close() + +#----------- End generation script -----------# + +#=============================================================================== diff --git a/doc/en/ref_assimilation_keywords.rst b/doc/en/ref_assimilation_keywords.rst index 5ac482e..0c03804 100644 --- a/doc/en/ref_assimilation_keywords.rst +++ b/doc/en/ref_assimilation_keywords.rst @@ -21,18 +21,28 @@ Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D -.. index:: single: ASSIMILATION_STUDY .. _section_ref_assimilation_keywords: List of commands and keywords for an ADAO calculation case ---------------------------------------------------------- This set of commands is related to the description of a calculation case, -that is a *Data Assimilation* procedure or an *Optimization* procedure. The -terms are ordered in alphabetical order, except the first, which describes -choice between calculation or checking. +that is a *Data Assimilation* procedure or an *Optimization* procedure. -The different commands are the following: +The first term describes the choice between calculation or checking. In the +graphical interface, each of the three types of calculation, individually more +oriented to *data assimilation*, *optimization methods* or *methods with +reduction* (some algorithms are simultaneously in various categories), is +imperatively indicated by one of these commands: + +.. include:: snippets/ASSIMILATION_STUDY.rst + +.. include:: snippets/OPTIMIZATION_STUDY.rst + +.. include:: snippets/REDUCTION_STUDY.rst + +The other terms are ordered in alphabetical order. The different commands are +the following: .. include:: snippets/ASSIMILATION_STUDY.rst diff --git a/doc/en/snippets/ASSIMILATION_STUDY.rst b/doc/en/snippets/ASSIMILATION_STUDY.rst index 458414f..d960bcf 100644 --- a/doc/en/snippets/ASSIMILATION_STUDY.rst +++ b/doc/en/snippets/ASSIMILATION_STUDY.rst @@ -1,6 +1,6 @@ .. index:: single: ASSIMILATION_STUDY **ASSIMILATION_STUDY** - *Required command*. This is the general command describing the data - assimilation or optimization case. It hierarchically contains all the other - commands. + *Required command*. This is the general command describing one case using + data assimilation methods. In the graphical interface, it hierarchically + contains all the other commands. diff --git a/doc/en/snippets/OPTIMIZATION_STUDY.rst b/doc/en/snippets/OPTIMIZATION_STUDY.rst new file mode 100644 index 0000000..0d84fc5 --- /dev/null +++ b/doc/en/snippets/OPTIMIZATION_STUDY.rst @@ -0,0 +1,6 @@ +.. index:: single: OPTIMIZATION_STUDY + +**OPTIMIZATION_STUDY** + *Required command*. This is the general command describing one case using + optimization methods. In the graphical interface, it hierarchically + contains all the other commands. diff --git a/doc/en/snippets/REDUCTION_STUDY.rst b/doc/en/snippets/REDUCTION_STUDY.rst new file mode 100644 index 0000000..99f3db9 --- /dev/null +++ b/doc/en/snippets/REDUCTION_STUDY.rst @@ -0,0 +1,6 @@ +.. index:: single: REDUCTION_STUDY + +**REDUCTION_STUDY** + *Required command*. This is the general command describing one case using + methods with reduction. In the graphical interface, it hierarchically + contains all the other commands. diff --git a/doc/en/tutorials_in_salome.rst b/doc/en/tutorials_in_salome.rst index ecfa568..2fc9a8a 100644 --- a/doc/en/tutorials_in_salome.rst +++ b/doc/en/tutorials_in_salome.rst @@ -325,12 +325,14 @@ Adding parameters to control the data assimilation algorithm One can add some optional parameters to control the data assimilation algorithm calculation. This is done by using optional parameters in the "*AlgorithmParameters*" command of the ADAO case definition, which is a keyword -of the "*ASSIMILATION_STUDY*" general command. This keyword requires an explicit -definition of the values from default ones, or from a Python dictionary, -containing some key/value pairs. The list of possible optional parameters are -given in the section :ref:`section_reference` and its subsections. The -recommandation is to use the explicit definition of values from the default list -of optionnal parameters, as here with the "*MaximumNumberOfSteps*": +of the general case ommand (to choose between "*ASSIMILATION_STUDY*", +"*OPTIMIZATION_STUDY*" or "*REDUCTION_STUDY*"). This keyword requires an +explicit definition of the values from default ones, or from a Python +dictionary, containing some key/value pairs. The list of possible optional +parameters are given in the section :ref:`section_reference` and its +subsections. The recommandation is to use the explicit definition of values +from the default list of optionnal parameters, as here with the +"*MaximumNumberOfSteps*": .. _adao_scriptentry02: .. image:: images/adao_scriptentry02.png diff --git a/doc/fr/ref_assimilation_keywords.rst b/doc/fr/ref_assimilation_keywords.rst index b0a4a14..8d18515 100644 --- a/doc/fr/ref_assimilation_keywords.rst +++ b/doc/fr/ref_assimilation_keywords.rst @@ -21,21 +21,30 @@ Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D -.. index:: single: ASSIMILATION_STUDY .. _section_ref_assimilation_keywords: Liste des commandes et mots-clés pour un cas d'assimilation de données ou d'optimisation ---------------------------------------------------------------------------------------- Ce jeu de commandes est lié à la description d'un cas de calcul, qui est une -procédure d'*Assimilation de Données* ou d'*Optimisation*. Les termes sont -classés par ordre alphabétique, sauf le premier, qui décrit le choix entre le -calcul ou la vérification. +procédure d'*Assimilation de Données*, de *Méthodes avec Réduction* ou de +méthodes *Optimisation*. -Les différentes commandes sont les suivantes: +Le premier terme décrit le choix entre un calcul ou une vérification. Dans +l'interface graphique, chacun des trois types de calculs, individuellement +plutôt orientés soit *assimilation de données*, soit "méthodes d'optimisation*, +"soit *méthodes avec réduction* (sachant que certains sont simultanément dans +plusieurs catégories), est impérativement désigné par l'une ces commandes: .. include:: snippets/ASSIMILATION_STUDY.rst +.. include:: snippets/OPTIMIZATION_STUDY.rst + +.. include:: snippets/REDUCTION_STUDY.rst + +Les autres termes sont classés par ordre alphabétique. Les différentes +commandes sont les suivantes: + .. include:: snippets/AlgorithmParameters.rst .. include:: snippets/Background.rst diff --git a/doc/fr/snippets/ASSIMILATION_STUDY.rst b/doc/fr/snippets/ASSIMILATION_STUDY.rst index 57fc2af..05f7f06 100644 --- a/doc/fr/snippets/ASSIMILATION_STUDY.rst +++ b/doc/fr/snippets/ASSIMILATION_STUDY.rst @@ -1,6 +1,6 @@ .. index:: single: ASSIMILATION_STUDY **ASSIMILATION_STUDY** - *Commande obligatoire*. C'est la commande générale qui décrit le cas - d'assimilation de données ou d'optimisation. Elle contient hiérarchiquement - toutes les autres commandes. + *Commande obligatoire*. C'est la commande générale qui décrit un cas + utilisant des méthodes d'assimilation de données. Dans l'interface graphique, + elle contient hiérarchiquement toutes les autres commandes. diff --git a/doc/fr/snippets/OPTIMIZATION_STUDY.rst b/doc/fr/snippets/OPTIMIZATION_STUDY.rst new file mode 100644 index 0000000..bca5e0f --- /dev/null +++ b/doc/fr/snippets/OPTIMIZATION_STUDY.rst @@ -0,0 +1,6 @@ +.. index:: single: OPTIMIZATION_STUDY + +**OPTIMIZATION_STUDY** + *Commande obligatoire*. C'est la commande générale qui décrit un cas + utilisant des méthodes d'optimisation. Dans l'interface graphique, elle + contient hiérarchiquement toutes les autres commandes. diff --git a/doc/fr/snippets/REDUCTION_STUDY.rst b/doc/fr/snippets/REDUCTION_STUDY.rst new file mode 100644 index 0000000..6a58da1 --- /dev/null +++ b/doc/fr/snippets/REDUCTION_STUDY.rst @@ -0,0 +1,6 @@ +.. index:: single: REDUCTION_STUDY + +**REDUCTION_STUDY** + *Commande obligatoire*. C'est la commande générale qui décrit un cas + utilisant des méthodes contenant de la réduction. Dans l'interface graphique, + elle contient hiérarchiquement toutes les autres commandes. diff --git a/doc/fr/tutorials_in_salome.rst b/doc/fr/tutorials_in_salome.rst index a63402f..806559a 100644 --- a/doc/fr/tutorials_in_salome.rst +++ b/doc/fr/tutorials_in_salome.rst @@ -338,9 +338,10 @@ Ajout de paramètres pour contrôler l'algorithme d'assimilation de données ------------------------------------------------------------------------- On peut ajouter des paramètres optionnels pour contrôler le calcul de -l'algorithme d'assimilation de données. Ceci se fait en utilisant les paramètres -optionnels dans la commande "*AlgorithmParameters*" de la définition du cas -ADAO, qui est un mot-clé de la commande générale "*ASSIMILATION_STUDY*". Ce +l'algorithme d'assimilation de données. Ceci se fait en utilisant les +paramètres optionnels dans la commande "*AlgorithmParameters*" de la définition +du cas ADAO, qui est un mot-clé de la commande générale de cas (à choisir entre +"*ASSIMILATION_STUDY*", "*OPTIMIZATION_STUDY*" ou "*REDUCTION_STUDY*"). Ce mot-clé nécessite une définition explicite des valeurs à partir de valeurs par défaut, ou à partir d'un dictionnaire Python, contenant des paires clé/valeur. La liste des paramètres optionnels possibles sont donnés dans la section diff --git a/i18n/adao_en.qm b/i18n/adao_en.qm index 2813893..a07e9a9 100644 Binary files a/i18n/adao_en.qm and b/i18n/adao_en.qm differ diff --git a/i18n/adao_en.ts b/i18n/adao_en.ts index 2cf3001..a6443a4 100644 --- a/i18n/adao_en.ts +++ b/i18n/adao_en.ts @@ -4,7 +4,15 @@ @default ASSIMILATION_STUDY - Data assimilation or optimization + Data assimilation + + + OPTIMIZATION_STUDY + Optimisation methods + + + REDUCTION_STUDY + Methods with reduction CHECKING_STUDY diff --git a/i18n/adao_fr.qm b/i18n/adao_fr.qm index 95fed4c..85a3ed0 100644 Binary files a/i18n/adao_fr.qm and b/i18n/adao_fr.qm differ diff --git a/i18n/adao_fr.ts b/i18n/adao_fr.ts index 2f694cf..2728ced 100644 --- a/i18n/adao_fr.ts +++ b/i18n/adao_fr.ts @@ -4,7 +4,15 @@ @default ASSIMILATION_STUDY - Assimilation de donnees ou optimisation + Assimilation de donnees + + + OPTIMIZATION_STUDY + Methodes d'optimisation + + + REDUCTION_STUDY + Methodes avec reduction CHECKING_STUDY diff --git a/src/daComposant/daCore/Interfaces.py b/src/daComposant/daCore/Interfaces.py index 08f497c..c67e3e2 100644 --- a/src/daComposant/daCore/Interfaces.py +++ b/src/daComposant/daCore/Interfaces.py @@ -185,6 +185,12 @@ class _COMViewer(GenericCaseViewer): if "ASSIMILATION_STUDY" in __multilines: __suppparameters.update({'StudyType':"ASSIMILATION_STUDY"}) __multilines = __multilines.replace("ASSIMILATION_STUDY","dict") + elif "OPTIMIZATION_STUDY" in __multilines: + __suppparameters.update({'StudyType':"ASSIMILATION_STUDY"}) + __multilines = __multilines.replace("OPTIMIZATION_STUDY", "dict") + elif "REDUCTION_STUDY" in __multilines: + __suppparameters.update({'StudyType':"ASSIMILATION_STUDY"}) + __multilines = __multilines.replace("REDUCTION_STUDY", "dict") elif "CHECKING_STUDY" in __multilines: __suppparameters.update({'StudyType':"CHECKING_STUDY"}) __multilines = __multilines.replace("CHECKING_STUDY", "dict") diff --git a/src/daEficas/generator_adao.py b/src/daEficas/generator_adao.py index 2665b30..f075315 100644 --- a/src/daEficas/generator_adao.py +++ b/src/daEficas/generator_adao.py @@ -95,16 +95,23 @@ class AdaoGenerator(PythonGenerator): def generate_da(self): + self.text_da += "#-*- coding: utf-8 -*-\n" + self.text_da += "study_config = {}\n" + + # Extraction de Study_type if "__CHECKING_STUDY__StudyName" in self.dictMCVal.keys(): self.type_of_study = "CHECKING_STUDY" + self.text_da += "study_config['StudyType'] = '\"CHECKING_STUDY\"'\n" + elif "__OPTIMIZATION_STUDY__StudyName" in self.dictMCVal.keys(): + self.type_of_study = "OPTIMIZATION_STUDY" + self.text_da += "study_config['StudyType'] = '\"ASSIMILATION_STUDY\"'\n" + elif "__REDUCTION_STUDY__StudyName" in self.dictMCVal.keys(): + self.type_of_study = "REDUCTION_STUDY" + self.text_da += "study_config['StudyType'] = '\"ASSIMILATION_STUDY\"'\n" else: self.type_of_study = "ASSIMILATION_STUDY" + self.text_da += "study_config['StudyType'] = '\"ASSIMILATION_STUDY\"'\n" - self.text_da += "#-*- coding: utf-8 -*-\n" - self.text_da += "study_config = {}\n" - - # Extraction de Study_type - self.text_da += "study_config['StudyType'] = '" + self.type_of_study + "'\n" # Extraction de StudyName self.text_da += "study_config['Name'] = '" + self.dictMCVal["__"+self.type_of_study+"__StudyName"] + "'\n" # Extraction de Debug