source: trunk/libIGCM/AA_TimeSeries_Checker @ 338

Last change on this file since 338 was 338, checked in by mmaipsl, 14 years ago

Improve TimeSeries? Checker to run during a job execution, not just on the end.

File size: 17.5 KB
Line 
1#!/bin/ksh
2#-Q- cesium #!/bin/ksh
3#-Q- cesium ######################
4#-Q- cesium ## CESIUM   CEA ##
5#-Q- cesium ######################
6#-Q- cesium #MSUB -r TimeSeries             # Nom du job               
7#-Q- cesium #MSUB -N 1              # Reservation du noeud
8#-Q- cesium #MSUB -n 1              # Reservation du processus
9#-Q- cesium #MSUB -T 86400          # Limite de temps elapsed du job
10#-Q- cesium #MSUB -E "-j o"
11#-Q- cesium #MSUB -E "-S /bin/ksh"
12#-Q- platine #!/usr/bin/ksh
13#-Q- platine ###################
14#-Q- platine ## PLATINE   CEA ##
15#-Q- platine ###################
16#-Q- platine #BSUB -J TimeSeries                     # Nom du job
17#-Q- platine #BSUB -N                        # message a la fin du job
18#-Q- platine #BSUB -n 1                      # reservation des processeurs pour le job
19#-Q- platine #BSUB -W 1:00                   # Limite temps
20#-Q- platine #BSUB -q post                   # Passage en queue post
21#-Q- sx8brodie #!/bin/ksh
22#-Q- sx8brodie #######################
23#-Q- sx8brodie ## SX8BRODIE   IDRIS ##
24#-Q- sx8brodie #######################
25#-Q- sx8brodie # Temps Elapsed max. d'une requete hh:mm:ss
26#-Q- sx8brodie # @ wall_clock_limit = 20:00:00
27#-Q- sx8brodie # Nom du travail LoadLeveler
28#-Q- sx8brodie # @ job_name   = TimeSeries
29#-Q- sx8brodie # Fichier de sortie standard du travail       
30#-Q- sx8brodie # @ output     = $(job_name).$(jobid)
31#-Q- sx8brodie # Fichier de sortie d'erreur du travail
32#-Q- sx8brodie # @ error      =  $(job_name).$(jobid)
33#-Q- sx8brodie # pour recevoir un mail en cas de depassement du temps Elapsed (ou autre pb.)
34#-Q- sx8brodie # @ notification = error
35#-Q- sx8brodie # @ environment  = $DEBUG_debug ; $MODIPSL ; $libIGCM ; $libIGCM_SX ; $SUBMIT_DIR ; $REBUILD_DIR ; $RebuildFromArchive ; $POST_DIR ; $MASTER ; $RebuildFrequency ; $PeriodDateBegin ; $PeriodDateEnd ; $NbRebuildDir ; $StandAlone ; $CompletedFlag ; $TsTask ; $CompToRead ; $FlagToRead ; $RESOL_ATM ; $RESOL_OCE ; $RESOL_ICE ; $RESOL_MBG ; $RESOL_SRF ; $RESOL_SBG ; $MASTER
36#-Q- aix6 #!/bin/ksh
37#-Q- aix6 #######################
38#-Q- aix6 ##   VARGAS   IDRIS  ##
39#-Q- aix6 #######################
40#-Q- aix6 # Temps Elapsed max. d'une requete hh:mm:ss
41#-Q- aix6 # @ wall_clock_limit = 20:00:00
42#-Q- aix6 # Nom du travail LoadLeveler
43#-Q- aix6 # @ job_name   = TimeSeries
44#-Q- aix6 # Fichier de sortie standard du travail
45#-Q- aix6 # @ output     = $(job_name).$(jobid)
46#-Q- aix6 # Fichier de sortie d'erreur du travail
47#-Q- aix6 # @ error      =  $(job_name).$(jobid)
48#-Q- aix6 # pour recevoir un mail en cas de depassement du temps Elapsed (ou autre pb.)
49#-Q- aix6 # @ notification = error
50#-Q- aix6 # @ environment  = $DEBUG_debug ; $MODIPSL ; $libIGCM ; $libIGCM_SX ; $SUBMIT_DIR ; $REBUILD_DIR ; $RebuildFromArchive ; $POST_DIR ; $MASTER ; $RebuildFrequency ; $DateBegin ; $PeriodDateBegin ; $PeriodDateEnd ; $NbRebuildDir ; $StandAlone ; $CompletedFlag ; $TsTask ; $CompToRead ; $FlagToRead ; $RESOL_ATM ; $RESOL_OCE ; $RESOL_ICE ; $RESOL_MBG ; $RESOL_SRF ; $RESOL_SBG ; $MASTER
51#-Q- aix6 # @ queue
52#-Q- sx8brodie # @ queue
53#-Q- sx8mercure #!/bin/ksh
54#-Q- sx8mercure ######################
55#-Q- sx8mercure ## SX8MERCURE   CEA ##
56#-Q- sx8mercure ######################
57#-Q- sx8mercure #PBS -N TimeSeries                   # Nom du job
58#-Q- sx8mercure #PBS -j o                    # regroupement des stdout et stderr
59#-Q- sx8mercure #PBS -S /usr/bin/ksh         # shell de soumission
60#-Q- sx8mercure #PBS -l memsz_job=1gb        # Limite memoire a 1 Go
61#-Q- sx8mercure #PBS -l cputim_job=24:00:00   # Limite temps a 2 heures
62#-Q- sx8mercure #PBS -q scalaire
63#-Q- sx9mercure #!/bin/ksh
64#-Q- sx9mercure #########################
65#-Q- sx9mercure ## CESIUM FOR SX9  CEA ##
66#-Q- sx9mercure #########################
67#-Q- sx9mercure #MSUB -r TimeSeries             # Nom du job               
68#-Q- sx9mercure #MSUB -N 1              # Reservation du noeud
69#-Q- sx9mercure #MSUB -n 1              # Reservation du processus
70#-Q- sx9mercure #MSUB -T 86400          # Limite de temps elapsed du job
71#-Q- sx9mercure #MSUB -E "-j o"
72#-Q- sx9mercure #MSUB -E "-S /bin/ksh"
73#-Q- titane #!/bin/ksh
74#-Q- titane ######################
75#-Q- titane ## TITANE   CEA ##
76#-Q- titane ######################
77#-Q- titane #MSUB -r TimeSeries             # Nom du job               
78#-Q- titane #MSUB -N 1              # Reservation du noeud
79#-Q- titane #MSUB -n 1              # Reservation du processus
80#-Q- titane #MSUB -T 86400          # Limite de temps elapsed du job
81#-Q- titane #MSUB -E "-j o"
82#-Q- titane #MSUB -E "-S /bin/ksh"
83#-Q- titane ##MSUB -e nco.out        # Sortie standard
84#-Q- titane ##MSUB -o nco.out        # Sortie standard
85#-Q- lxiv8 ######################
86#-Q- lxiv8 ## OBELIX      LSCE ##
87#-Q- lxiv8 ######################
88#-Q- lxiv8 #PBS -N TimeSeries
89#-Q- lxiv8 #PBS -m a
90#-Q- lxiv8 #PBS -j oe
91#-Q- lxiv8 #PBS -q medium
92#-Q- lxiv8 #PBS -o TimeSeries.$$
93#-Q- lxiv8 #PBS -S /bin/ksh
94#-Q- default #!/bin/ksh
95#-Q- default ##################
96#-Q- default ## DEFAULT HOST ##
97#-Q- default ##################
98
99#**************************************************************
100# Author: Sebastien Denvil
101# Contact: Sebastien.Denvil@ipsl.jussieu.fr
102# $Date: $
103# $Author: $
104# $Revision: $
105# IPSL (2006)
106#  This software is governed by the CeCILL licence see libIGCM/libIGCM_CeCILL.LIC
107# History:
108# Modification:
109#
110#**************************************************************
111
112# Check that everything went well during time series production
113# Display a short report
114# Launch what's missing
115# For use during a run (not on the end : PeriodState=Completed), it will complete
116# all TS to last PeriodDateEnd value, give by run.card->Configuration->OldPrefix string.
117
118
119# Chemin vers libIGCM
120libIGCM=${libIGCM:=::modipsl::/libIGCM}
121# Attention : à changer si la machine de post-traitement n'est pas la frontale du serveur de calcul !
122#             voir précence de la variable MirrorlibIGCM dans votre couche systÚme.
123
124# Type de run
125EXPERIMENT=${EXPERIMENT:=historical}
126
127# Nom du job
128JobName=${JobName:=HISTORC2}
129
130# répertoire courrant
131CURRENT_DIR=$( pwd )
132
133# Emplacement des cartes
134CARD_DIR=${CARD_DIR:=${CURRENT_DIR}/${EXPERIMENT}/${JobName}}
135
136# répertoire de stockate des sorties des create_ts
137POST_DIR=${POST_DIR:=${CARD_DIR}/OutScript}
138
139
140if [ ! -d ${CARD_DIR} ]; then
141    echo "No ${CARD_DIR}, we stop here"
142    exit
143fi
144
145########################################################################
146
147. ${libIGCM}/libIGCM_debug/libIGCM_debug.ksh ;
148. ${libIGCM}/libIGCM_sys/libIGCM_sys.ksh     #; IGCM_debug_Check
149. ${libIGCM}/libIGCM_card/libIGCM_card.ksh   #; IGCM_card_Check
150. ${libIGCM}/libIGCM_date/libIGCM_date.ksh   #; IGCM_date_Check
151
152########################################################################
153
154# First of all
155IGCM_card_DefineArrayFromSection       ${CARD_DIR}/config.card UserChoices
156typeset option
157for option in ${config_UserChoices[*]} ; do
158    IGCM_card_DefineVariableFromOption ${CARD_DIR}/config.card UserChoices ${option}
159done
160
161echo
162IGCM_debug_Print 1 "DefineArrayFromOption  : config_UserChoices"
163IGCM_debug_PrintVariables 3 config_UserChoices_JobName
164#IGCM_debug_PrintVariables 3 config_UserChoices_SpaceName
165#IGCM_debug_PrintVariables 3 config_UserChoices_ExperimentName
166IGCM_debug_PrintVariables 3 config_UserChoices_CalendarType
167IGCM_debug_PrintVariables 3 config_UserChoices_DateBegin
168IGCM_debug_PrintVariables 3 config_UserChoices_DateEnd
169
170if [ -f ${CARD_DIR}/run.card ] ; then
171    IGCM_card_DefineVariableFromOption ${CARD_DIR}/run.card Configuration OldPrefix
172    IGCM_card_DefineVariableFromOption ${CARD_DIR}/run.card Configuration PeriodState
173    IGCM_debug_PrintVariables 3 run_Configuration_OldPrefix
174    IGCM_debug_PrintVariables 3 run_Configuration_PeriodState
175    if [ X${run_Configuration_PeriodState} != X"Completed" ] ; then
176        DateEnd=$( IGCM_date_ConvertFormatToGregorian $( echo ${run_Configuration_OldPrefix} | awk -F'_' '{print $2}' ) )
177    else
178        DateEnd=$( IGCM_date_ConvertFormatToGregorian ${config_UserChoices_DateEnd} )
179    fi
180else
181    DateEnd=$( IGCM_date_ConvertFormatToGregorian ${config_UserChoices_DateEnd} )
182fi
183
184echo
185echo "DateEnd for TimeSeries_Checker : " ${DateEnd}
186IGCM_date_GetYearMonth ${DateEnd}   YearEnd   MonthEnd
187echo "YearEnd MonthEnd for TimeSeries_Checker : " ${YearEnd} ${MonthEnd}
188echo
189
190#set -vx
191
192#====================================================
193#R_SAVE : Job output directory
194if ( [ ! X${config_UserChoices_SpaceName} = X ] && [ ! X${config_UserChoices_ExperimentName} = X ] ) ; then
195    FreeName=$( echo ${config_UserChoices_JobName} | sed 's/.*_//' )
196    R_SAVE=${R_OUT}/${config_UserChoices_TagName}/${config_UserChoices_SpaceName}/${config_UserChoices_ExperimentName}/${FreeName}
197    R_DODS=${config_UserChoices_TagName}/${config_UserChoices_SpaceName}/${config_UserChoices_ExperimentName}/${FreeName}
198else
199    R_SAVE=${R_OUT}/${config_UserChoices_TagName}/${config_UserChoices_JobName}
200    R_DODS=${config_UserChoices_TagName}/${config_UserChoices_JobName}
201fi
202
203IGCM_card_DefineArrayFromSection ${CARD_DIR}/config.card ListOfComponents
204
205for comp in ${config_ListOfComponents[*]} ; do
206    # Debug Print
207    IGCM_debug_Print 1 ${comp}
208    # Define component
209    IGCM_card_DefineArrayFromOption ${CARD_DIR}/config.card ListOfComponents ${comp}
210    eval compname=\${config_ListOfComponents_${comp}[0]} > /dev/null 2>&1
211    eval comptagname=\${config_ListOfComponents_${comp}[1]} > /dev/null 2>&1
212
213    # Read libIGCM compatibility version in ${compname}.card
214    card=${CARD_DIR}/COMP/${compname}.card
215
216    # Read and Build Output File stuff
217    #IGCM_debug_Print 1 "DefineArrayFromOption  : ${compname}_OutputFiles ${card}"
218    IGCM_card_DefineArrayFromOption ${card} OutputFiles List
219    ListFilesName=${compname}_OutputFiles_List
220    eval FileName0=\${${ListFilesName}[0]} > /dev/null 2>&1
221    #
222    if [ X${FileName0} != X${NULL_STR} ] ; then
223        #
224        #IGCM_debug_Print 1 "Component      : ${compname}"
225        #
226        # INITIALISATION
227        #
228        eval NbFiles=\${#${ListFilesName}[@]} > /dev/null 2>&1
229        i=2
230        #
231        until [ $i -ge $NbFiles ]; do
232            #
233            eval flag_post=\${${ListFilesName}[$i]} > /dev/null 2>&1
234            #
235            if [ X${flag_post} != XNONE ] ; then
236                #
237                # First of all
238                #
239                IGCM_card_DefineArrayFromSection ${card} ${flag_post}
240                #
241                IGCM_card_DefineArrayFromOption ${card} ${flag_post} TimeSeriesVars
242                IGCM_card_DefineArrayFromOption ${card} ${flag_post} Patches
243                if [ X"$( eval echo \${${compname}_${flag_post}_TimeSeriesVars[*]} )" = X"Option not found ${flag_post}" ] ; then
244                    # New TimeSeriesVar description, with 2D, 3D and associate ChunckJob.
245                    unset ListDimension
246                    ListDimension[0]=2D
247                    ListDimension[1]=3D
248                    TimeSeries=false
249                    TimeSeries2D=false
250                    TimeSeries3D=false
251                    iLoop=${#ListDimension[*]}
252                    j=0
253                    until [ $j -ge ${iLoop} ]; do
254                        Dimension=${ListDimension[${j}]}
255                        IGCM_card_DefineArrayFromOption ${card} ${flag_post} TimeSeriesVars${Dimension}
256                        IGCM_card_DefineVariableFromOption ${card} ${flag_post} ChunckJob${Dimension}
257                        #
258                        # Time series WITHOUT chunk
259                        #
260                        if [ ! $( eval echo \${${compname}_${flag_post}_TimeSeriesVars${Dimension}} ) = ${NULL_STR} ] ; then
261                            if [ $( eval echo \${${compname}_${flag_post}_ChunckJob${Dimension}} ) = NONE ] ; then
262                                IGCM_debug_Print 2 "${Dimension} time series activated for ${flag_post}"
263                                eval TimeSeries${Dimension}=true
264                                chunck=false
265                            fi
266                        fi
267                        #
268                        # Time series WITH chunk
269                        #
270                        if [ ! $( eval echo \${${compname}_${flag_post}_TimeSeriesVars${Dimension}} ) = ${NULL_STR} ] ; then
271                            chunck_size=$( eval echo \${${compname}_${flag_post}_ChunckJob${Dimension}} )
272                            if [ ! ${chunck_size} = NONE ] &&  [ ! ${chunck_size} = OFF ] ; then
273                                IGCM_debug_Print 2 "${Dimension} time series activated with ${chunck_size} chunck for ${flag_post}"
274                                eval TimeSeriesChunck${Dimension}=true
275                                chunck=true
276                            fi
277                        fi
278                        (( j=j+1 ))
279
280                        #
281                        #  ICI ON TESTE QUE LES FICHIERS TS SONT LA!
282                        #
283                        #
284                        FILE=$( echo ${flag_post} | awk "-FPost_" '{print $2}' )
285                        IGCM_card_DefineArrayFromOption ${card}    Post_${FILE} TimeSeriesVars${Dimension}
286                        IGCM_card_DefineVariableFromOption ${card} Post_${FILE} ChunckJob${Dimension}
287                        #
288                        FlagDir=$( echo ${FILE} | awk -F "_" '{print $1}' )
289                        case ${FlagDir} in
290                            *Y)  TS_Dir=TS_YE  ;;
291                            *M)  TS_Dir=TS_MO  ;;
292                            *D)  TS_Dir=TS_DA  ;;
293                         3H|HF)  TS_Dir=TS_HF  ;;
294                           INS) TS_Dir=TS_INS ;;
295                        esac
296
297                        #
298                        # If TimeSeriesVars list is empty we skip
299                        #
300                        if [ $( eval echo \${${compname}_Post_${FILE}_TimeSeriesVars${Dimension}} ) = ${NULL_STR} ] ; then
301                            IGCM_debug_Print 2 "Empty TS : ${compname}_Post_${FILE}_TimeSeriesVars${Dimension}"
302                            #(( i=i+3 ))
303                            continue
304                        fi
305                        #
306                        # We need LIST of variables not allready produced (useful for standalone mode)
307                        #
308                        DateBegin=$( IGCM_date_ConvertFormatToGregorian ${config_UserChoices_DateBegin} )
309                        IGCM_date_GetYearMonth ${DateBegin} YearBegin MonthBegin
310
311                        # Si on n'a pas de chunck, dans ce cas chunck_size=la durée de la simulation en années
312                        YearsChunckLength=$( echo ${chunck_size} | sed -e "s/[yY]//" )
313                        if ( [ ${chunck} = false ] || [ X${YearsChunckLength} = XOFF ] ) ; then
314                            YearsChunckLength=$(( YearEnd - YearBegin + 1 ))
315                        fi
316
317                        NbYearsChunckLoop=$(( ( YearEnd - YearBegin + 1 ) / YearsChunckLength ))
318                        Reste=$(( ( YearEnd - YearBegin + 1 ) % YearsChunckLength ))
319
320                        if [ ${Reste} -ne 0 ] ; then
321                            NbYearsChunckLoop=$(( NbYearsChunckLoop + 1 ))
322                        fi
323
324                        if [ ${NbYearsChunckLoop} -eq 1 ] ; then
325                            PeriodDateEnd=${DateEnd}
326                        else
327                            DaysInYear=$( IGCM_date_DaysInYear ${YearBegin} )
328                            PeriodDateEnd=$( IGCM_date_AddDaysToGregorianDate ${DateBegin} $(( YearsChunckLength * DaysInYear - 1 )) )
329                        fi
330                        #
331                        DIRECTORY=${R_SAVE}/${comp}/Analyse/${TS_Dir}
332                        YearsChunckLoop=1
333                        ChunckDebut=${DateBegin}
334                        ChunckFin=${PeriodDateEnd}
335                        while [ ${YearsChunckLoop} -le ${NbYearsChunckLoop} ] ; do
336                            countTotal=0
337                            countGood=0
338                            countBad=0
339                            for var in $( eval echo \${${compname}_Post_${FILE}_TimeSeriesVars${Dimension}[*]} ) ; do
340                                TestedFile=${config_UserChoices_JobName}_${ChunckDebut}_${ChunckFin}_${FlagDir}_${var}.nc
341                                #
342                                if [ ! -f ${DIRECTORY}/${TestedFile} ] ; then
343                                    (( countBad = countBad + 1 ))
344                                    [ ${countBad} = 1 ] && IGCM_debug_Print 3 "Missing time series from ${FILE} :"
345                                    IGCM_debug_Print 3 ${DIRECTORY}/${TestedFile}
346                                else
347                                    (( countGood = countGood + 1 ))
348                                fi
349                                (( countTotal = countTotal + 1 ))
350                            done
351
352                            SuccessRate=$(( countGood * 100 / countTotal ))
353                            if [ ${SuccessRate} -ne 100 ] ; then
354                                IGCM_debug_Print 2 -e "\033[1;31m${SuccessRate}% files OK.\033[m for period ${ChunckDebut}-${ChunckFin}"
355                            else
356                                IGCM_debug_Print 2 -e "\033[1;32m${SuccessRate}% files OK.\033[m for period ${ChunckDebut}-${ChunckFin}"
357                            fi
358
359                            echo
360                            if ( [ ${chunck} = true ] && [ ${SuccessRate} -ne 100 ] ) ; then
361                                IGCM_debug_Print 2 -e "\033[1;31mSubmit ${FILE} chunck ${Dimension}\033[m period ${ChunckDebut}-${ChunckFin}"
362                                listVarEnv="libIGCM,SUBMIT_DIR,POST_DIR,DateBegin,PeriodDateEnd,TsTask,CompToRead,FlagToRead"
363                                export libIGCM=${libIGCM}
364                                export SUBMIT_DIR=${CARD_DIR}
365                                export POST_DIR=${POST_DIR}
366                                export DateBegin=${ChunckDebut}
367                                export PeriodDateEnd=${ChunckFin}
368                                export TsTask=Chunck${Dimension}
369                                export CompToRead=${comp}
370                                export FlagToRead=${i}
371                                export listVarEnv=${listVarEnv}
372                                IGCM_sys_MkdirWork ${POST_DIR}
373                                IGCM_debug_Verif_Exit
374                                IGCM_sys_QsubPost create_ts
375                                echo
376                            fi
377
378                            if ( [ ${chunck} = false ] && [ ${SuccessRate} -ne 100 ] ) ; then
379                                eval Launch${Dimension}=true
380                            fi
381
382                            # Date update
383                            ChunckDebut=$( IGCM_date_AddDaysToGregorianDate ${ChunckFin} 1 )
384
385                            (( YearsChunckLoop = YearsChunckLoop + 1 ))
386
387                            if [ ${YearsChunckLoop} -eq ${NbYearsChunckLoop} ] ; then
388                                ChunckFin=${DateEnd}
389                            else
390                                #PeriodDateEnd=$(( YearBegin + YearsChunckLength - 1 ))
391                                ChunckFin=$( IGCM_date_AddDaysToGregorianDate ${ChunckDebut} $(( YearsChunckLength * DaysInYear - 1 )) )
392                            fi
393                        done
394                    done
395                else
396                    ListDimension[0]=""
397                    TimeSeries=true
398                    TimeSeries2D=false
399                    TimeSeries3D=false
400                    TimeSeriesChunck2D=false
401                    TimeSeriesChunck3D=false
402                fi
403            fi
404            (( i=i+3 ))
405        done
406    fi
407done # comp loop
408
409echo
410
411DateBegin=$( IGCM_date_ConvertFormatToGregorian ${config_UserChoices_DateBegin} )
412IGCM_date_GetYearMonth ${DateBegin} YearBegin MonthBegin
413
414if [ X${Launch2D} = Xtrue ] ; then
415    IGCM_debug_Print 2 -e "\033[1;31mSubmit 2D\033[m without chunck period ${DateBegin}-${DateEnd}"
416    listVarEnv="libIGCM,SUBMIT_DIR,POST_DIR,DateBegin,PeriodDateEnd,TsTask"
417    export libIGCM=${libIGCM}
418    export SUBMIT_DIR=${CARD_DIR}
419    export POST_DIR=${POST_DIR}
420    export DateBegin=${DateBegin}
421    export PeriodDateEnd=${DateEnd}
422    export TsTask=2D
423    export listVarEnv=${listVarEnv}
424    IGCM_sys_MkdirWork ${POST_DIR}
425    IGCM_debug_Verif_Exit
426    IGCM_sys_QsubPost create_ts
427    echo
428fi
429
430if [ X${Launch3D} = Xtrue ] ; then
431    IGCM_debug_Print 2 -e "\033[1;31mSubmit 3D\033[m without chunck period ${DateBegin}-${DateEnd}"
432    listVarEnv="libIGCM,SUBMIT_DIR,POST_DIR,DateBegin,PeriodDateEnd,TsTask"
433    export libIGCM=${libIGCM}
434    export SUBMIT_DIR=${CARD_DIR}
435    export POST_DIR=${POST_DIR}
436    export DateBegin=${DateBegin}
437    export PeriodDateEnd=${DateEnd}
438    export TsTask=3D
439    export listVarEnv=${listVarEnv}
440    IGCM_sys_MkdirWork ${POST_DIR}
441    IGCM_debug_Verif_Exit
442    IGCM_sys_QsubPost create_ts
443    echo
444fi
Note: See TracBrowser for help on using the repository browser.