1#!/usr/local/bin/bash 2# 3# Copyright 2009-2021 The VOTCA Development Team (http://www.votca.org) 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18#-------------------defines---------------- 19 20if [[ $1 = "--help" ]]; then 21 cat <<EOF 22${0##*/}, version %version% 23 24This file defines some commonly used functions: 25 26EOF 27sed -n 's/^\(.*\)([)] {[^#]*#\(.*\)$/* \1 -- \2/p' ${0} 28echo 29exit 0 30fi 31 32export BASH #need in CsgFunctions.pm 33 34shopt -s extglob 35 36msg() { #echos a msg on the screen and send it to the logfile if logging is enabled 37 local color colors="blue cyan cyann green red purp" 38 if [[ -z ${CSGNOCOLOR} ]]; then 39 local blue="[34;01m" 40 local cyan="[36;01m" 41 local cyann="[36m" 42 local green="[32;01m" 43 local red="[31;01m" 44 local purp="[35;01m" 45 local off="[0m" 46 else 47 local blue cyan cyann green red purp off 48 fi 49 if [[ $1 = "--color" ]]; then 50 [[ -z $2 ]] && die "${FUNCNAME[0]}: missing argument after --color" 51 is_part "$2" "${colors}" || die "${FUNCNAME[0]}: Unknown color ($colors allowed)" 52 color="${!2}" 53 shift 2 54 else 55 off="" 56 fi 57 if [[ $1 = "--to-stderr" ]]; then 58 shift 59 [[ -z $* ]] && return 60 if [[ -n ${CSGLOG} ]]; then 61 echo -e "${color}$*${off}" >&4 62 echo -e "$*" >&2 63 else 64 echo -e "${color}$*${off}" >&2 65 fi 66 else 67 [[ -z $* ]] && return 68 if [[ -n ${CSGLOG} ]]; then 69 echo -e "${color}$*${off}" >&3 70 echo -e "$*" 71 else 72 echo -e "${color}$*${off}" 73 fi 74 fi 75} 76export -f msg 77 78show_callstack() { #show the current callstack 79 local space line 80 if [[ -n $CSG_CALLSTACK ]]; then 81 echo "$CSG_CALLSTACK" 82 space="$(echo "$CSG_CALLSTACK" | sed -n '$s/[^[:space:]].*$/ /p')" 83 else 84 space="" 85 fi 86 [[ $0 = *"bash" ]] || echo "${space}${0} - linenumber ${BASH_LINENO[ $(( ${#FUNCNAME[@]} -2 ))]}" 87 for ((c=${#FUNCNAME[*]}-1;c>0;c--)); do 88 [[ ${FUNCNAME[$c]} = main ]] && continue #main is useless as the info was printed 2 lines above 89 space+=" " 90 if [[ $0 = *csg_call || $0 = *inverse.sh ]]; then 91 echo "${space}${FUNCNAME[$c]} - linenumber ${BASH_LINENO[ $(( $c - 1 )) ]} in ${BASH_SOURCE[$c]}" 92 else 93 echo "${space}${FUNCNAME[$c]} - linenumber ${BASH_LINENO[ $(( $c - 1 )) ]} (see 'csg_call --cat function ${FUNCNAME[$c]}')" 94 fi 95 done 96 [[ $1 = "--extra" ]] || return 0 97 shift 98 for i in "$@"; do 99 space+=" " 100 echo "${space}${i} - linenumber ???" 101 done 102} 103export -f show_callstack 104 105unset -f die 106die () { #make the iterative frame work stopp 107 local pid pids c place 108 #Output callstack to stderr in case die was executed in $( ) 109 echo -e "\nCallstack:" >&2 110 show_callstack >&2 111 [[ -z $CSGLOG ]] && place="Details can be found above" || place="For details see the logfile $CSGLOG" 112 [[ ${CSG_RUNTEST} && ${CSGLOG} ]] && tail -n 200 "${CSGLOG}" >&4 113 msg --color red --to-stderr "$(csg_banner "ERROR:" "$@" "$place")" 114 if [[ -n ${CSG_MASTER_PID} ]]; then 115 #grabbing the pid group would be easier, but it would not work on AIX 116 pid="$$" 117 pids="$$" 118 c=0 119 #find the parent of pid until we reach CSG_MASTER_PID 120 until [[ ${CSG_MASTER_PID} -eq $pid ]]; do 121 #get the parent pid using BSD style due to AIX 122 pid=$(ps -o ppid= -p "$pid" 2>/dev/null) 123 [[ -z $pid ]] && pids="0" && break 124 #store them in inverse order to kill parents before the child 125 pids="$pid $pids" 126 ((c++)) 127 #at max 10000 iterations 128 if [[ $c -eq 10000 ]]; then 129 #failback to default, see comment below 130 pids="0" 131 break 132 fi 133 done 134 if [[ -n ${CSGLOG} ]]; then 135 echo "${FUNCNAME[0]}: (called from $$) CSG_MASTER_PID is $CSG_MASTER_PID" >&2 136 echo "${FUNCNAME[0]}: pids to kill: $pids" >&2 137 fi 138 kill $pids 139 else 140 #send kill signal to all process within the process groups 141 kill 0 142 fi 143 exit 1 144} 145export -f die 146 147cat_external() { #takes a two tags and shows content of the according script 148 local script 149 script="$(source_wrapper $1 $2)" || die "${FUNCNAME[0]}: source_wrapper $1 $2 failed" 150 if [[ $1 = "function" ]]; then 151 type $2 | sed '1d' 152 else 153 cat "${script/ *}" 154 fi 155} 156export -f cat_external 157 158do_external() { #takes two tags, find the according script and excute it 159 local script tags quiet="no" ret 160 [[ $1 = "-q" ]] && quiet="yes" && shift 161 script="$(source_wrapper $1 $2)" || die "${FUNCNAME[0]}: source_wrapper $1 $2 failed" 162 tags="$1 $2" 163 [[ $1 != "function" && ! -x ${script/ *} ]] && die "${FUNCNAME[0]}: subscript '${script/ *}' (from tags $tags), is not executable! (Run chmod +x ${script/ *})" 164 #print this message to stderr to allow $(do_external ) and do_external XX > 165 [[ $quiet = "no" ]] && echo "Running subscript '${script##*/}${3:+ }${@:3}' (from tags $tags) dir ${script%/*}" >&2 166 # in debugmode we don't need to do anything special for $1 = function as set -x is already done 167 if [[ -n $CSGDEBUG ]] && [[ -n "$(sed -n '1s@bash@XXX@p' "${script/ *}")" ]]; then 168 CSG_CALLSTACK="$(show_callstack)" "${BASH}" -x $script "${@:3}" 169 elif [[ -n $CSGDEBUG && -n "$(sed -n '1s@perl@XXX@p' "${script/ *}")" ]]; then 170 local perl_debug="$(mktemp perl_debug.XXX)" ret 171 PERLDB_OPTS="NonStop=1 AutoTrace=1 frame=2 LineInfo=$perl_debug" perl -dS $script "${@:3}" 172 ret=$? 173 cat "$perl_debug" 2>&1 174 [[ $ret -eq 0 ]] 175 elif [[ $1 != "function" && -n "$(sed -n '1s@perl@XXX@p' "${script/ *}")" ]]; then 176 CSG_CALLSTACK="$(show_callstack --extra "${script/ *}")" $script "${@:3}" 177 else 178 CSG_CALLSTACK="$(show_callstack)" $script "${@:3}" 179 fi || die "${FUNCNAME[0]}: subscript" "$script ${*:3}" "(from tags $tags) failed" 180} 181export -f do_external 182 183critical() { #executes arguments as command and calls die if not succesful 184 local quiet="no" 185 [[ $1 = "-q" ]] && quiet="yes" && shift 186 [[ -z $1 ]] && die "${FUNCNAME[0]}: missing argument" 187 #print this message to stderr because $(critical something) is used very often 188 [[ $quiet = "no" ]] && echo "Running critical command '$*'" >&2 189 "$@" || die "${FUNCNAME[0]}: '$*' failed" 190} 191export -f critical 192 193for_all (){ #do something for all interactions (1st argument) 194 local bondtype ibondtype rbondtype bondtypes name interactions quiet="no" 195 [[ $1 = "-q" ]] && quiet="yes" && shift 196 [[ -z $1 || -z $2 ]] && die "${FUNCNAME[0]}: need at least two arguments" 197 bondtypes="$1" 198 shift 199 interactions=( $(csg_get_interaction_property --all name) ) 200 min=( $(csg_get_interaction_property --all min) ) 201 [[ ${#min[@]} -ne ${#interactions[@]} ]] && die "${FUNCNAME[0]}: one interaction has no name or min" 202 name=$(has_duplicate "${interactions[@]}") && die "${FUNCNAME[0]}: interaction name $name appears twice" 203 for bondtype in $bondtypes; do 204 #check that type is bonded or non-bonded 205 is_part "$bondtype" "non-bonded bonded angle bond dihedral" || die "for_all: Argument 1 needs to be non-bonded, bonded, angle, bond or dihedral" 206 [[ $quiet = "no" ]] && echo "For all $bondtype" >&2 207 #internal bondtype 208 is_part "$bondtype" "angle bond dihedral bonded" && ibondtype="bonded" || ibondtype="non-bonded" 209 interactions=( $(csg_get_property --allow-empty cg.$ibondtype.name) ) #filter me away 210 for name in "${interactions[@]}"; do 211 #check if interaction is actually angle, bond or dihedral 212 if is_part "$bondtype" "angle bond dihedral"; then 213 rbondtype=$(bondtype="$ibondtype" bondname="$name" csg_get_interaction_property bondtype) 214 [[ $rbondtype = $bondtype ]] || continue 215 fi 216 #print this message to stderr to avoid problem with $(for_all something) 217 [[ $quiet = no ]] && echo "for_all: run '$*' for interaction named '$name'" >&2 218 #we need to use bash -c here to allow things like $(csg_get_interaction_property name) in arguments 219 #write variable defines in the front is better, that export 220 #no need to run unset afterwards 221 bondtype="$ibondtype" \ 222 bondname="$name" \ 223 CSG_CALLSTACK="$(show_callstack)" \ 224 "${BASH}" -c "$*" || die "${FUNCNAME[0]}: ${BASH} -c '$*' failed for interaction named '$name'" 225 done 226 done 227} 228export -f for_all 229 230csg_get_interaction_property () { #gets an interaction property from the xml file, should only be called from inside a for_all loop or with --all option 231 local ret allow_empty="no" for_all="no" xmltype 232 while [[ $1 = --* ]]; do 233 case $1 in 234 --allow-empty) 235 allow_empty="yes";; 236 --all) 237 for_all="yes";; 238 *) 239 die "${FUNCNAME[0]}: Unknow option '$1'";; 240 esac 241 shift 242 done 243 [[ -n $1 ]] || die "${FUNCNAME[0]}: Missing argument" 244 245 if [[ $for_all = "yes" ]]; then 246 [[ $1 = "bondtype" ]] && die "${FUNCNAME[0]}: --all + bondtype not implemented yet" 247 local t 248 for t in non-bonded bonded; do 249 ret+=" $(csg_get_property --allow-empty "cg.$t.$1")" #filter me away 250 done 251 ret="$(echo "$ret" | trim_all)" 252 [[ -z $ret ]] && die "${FUNCNAME[0]}: Not a single interaction has a value for the property $1" 253 echo "$ret" 254 return 0 255 fi 256 257 #make these this case work even without name or type (called by csg_call) 258 if [[ $1 = "name" ]]; then 259 [[ -n $bondname ]] && echo "$bondname" && return 0 260 die "${FUNCNAME[0]}: bondname is undefined (when calling from csg_call set it by --ia-name option)" 261 fi 262 if [[ $1 = "bondtype" ]]; then 263 #bondtype is special -> dirty hack - removed whenever issue 13 is fixed 264 [[ -z "$bondtype" ]] && die "${FUNCNAME[0]}: bondtype is undefined (when calling from csg_call set it by --ia-type option)" 265 #for_all notation for any kind of bonded interaction, find the real type 266 if [[ $bondtype = "bonded" ]]; then 267 [[ -z ${bondname} ]] && die "${FUNCNAME[0]}: bondtype 'bonded' needs a bondname (when calling from csg_call set it by --ia-name option) or change type to angle, bond or dihedral" 268 [[ -n "$(type -p csg_property)" ]] || die "${FUNCNAME[0]}: Could not find csg_property" 269 mapping="$(csg_get_property --allow-empty cg.inverse.map)" #make error message more useful 270 [[ -z ${mapping} ]] && die "${FUNCNAME[0]}: bondtype 'bonded' needs a mapping file (cg.inverse.map in xml) to determine the actual bond type (when calling from csg_call better use --ia-type bond, angle or dihedral)" 271 local map names=() ret= ret2 dup 272 for map in ${mapping}; do 273 [[ -f "$(get_main_dir)/$map" ]] || die "${FUNCNAME[0]}: Mapping file '$map' for bonded interaction not found in maindir" 274 names+=( $(critical -q csg_property --file "$(get_main_dir)/$map" --path cg_molecule.topology.cg_bonded.*.name --print . --short) ) 275 [[ -n ${names[@]} ]] && dup=$(has_duplicate "${names[@]}") && die "${FUNCNAME[0]}: cg_bonded name '$dup' appears twice in file(s) $mapping" 276 ret2="$(critical -q csg_property --file "$(get_main_dir)/$map" --path cg_molecule.topology.cg_bonded.* --filter name="$bondname" --print . --with-path | trim_all)" 277 ret2="$(echo "$ret2" | critical sed -n 's/.*cg_bonded\.\([^[:space:]]*\) .*/\1/p')" 278 if [[ -n $ret2 ]]; then 279 [[ -n $ret ]] && die "${FUNCNAME[0]}: Found cg_bonded type for name '$bondname' twice" 280 ret="${ret2}" 281 fi 282 done 283 [[ -z $ret ]] && die "${FUNCNAME[0]}: Could not find a bonded definition with name '$bondname' in the mapping file(s) '$mapping'. Make sure to use the same name in the settings file (or --ia-name when calling from csg_call) and the mapping file." 284 echo "$ret" 285 else 286 echo "$bondtype" 287 fi 288 return 0 289 fi 290 291 [[ -n "$CSGXMLFILE" ]] || die "${FUNCNAME[0]}: CSGXMLFILE is undefined (when calling from csg_call set it by --options option)" 292 [[ -n $bondtype ]] || die "${FUNCNAME[0]}: bondtype is undefined (when calling from csg_call set it by --ia-type option)" 293 [[ -n $bondname ]] || die "${FUNCNAME[0]}: bondname is undefined (when calling from csg_call set it by --ia-name option)" 294 295 #map bondtype back to tags in xml file (for csg_call) 296 case "$bondtype" in 297 "non-bonded") 298 xmltype="non-bonded";; 299 "bonded"|"bond"|"angle"|"dihedral") 300 xmltype="bonded";; 301 *) 302 msg "Unknown bondtype '$bondtype' - assume non-bonded" 303 xmltype="non-bonded";; 304 esac 305 306 [[ -n "$(type -p csg_property)" ]] || die "${FUNCNAME[0]}: Could not find csg_property" 307 #the --filter/--path(!=.) option will make csg_property fail if $1 does not exist 308 #so no critical here 309 ret="$(csg_property --file $CSGXMLFILE --short --path cg.${xmltype} --filter name=$bondname --print $1 | trim_all)" 310 #overwrite with function call value 311 [[ -z $ret && -n $2 ]] && ret="$2" 312 [[ -z $ret ]] && echo "${FUNCNAME[0]}: No value for '$1' found in $CSGXMLFILE, trying ${VOTCA_CSG_DEFAULTS}" >&2 313 # if still empty fetch it from defaults file 314 if [[ -z $ret && -f ${VOTCA_CSG_DEFAULTS} ]]; then 315 ret="$(critical -q csg_property --file "${VOTCA_CSG_DEFAULTS}" --short --path cg.${xmltype}.$1 --print . | trim_all)" 316 [[ $allow_empty = "yes" && -n "$res" ]] && msg "WARNING: '${FUNCNAME[0]} $1' was called with --allow-empty, but a default was found in '${VOTCA_CSG_DEFAULTS}'" 317 #from time to time the default is only given in the non-bonded section 318 [[ -z $ret ]] && ret="$(critical -q csg_property --file "${VOTCA_CSG_DEFAULTS}" --short --path cg.non-bonded.$1 --print . | trim_all)" 319 [[ -n $ret ]] && echo "${FUNCNAME[0]}: value for '$1' from ${VOTCA_CSG_DEFAULTS}: $ret" >&2 320 fi 321 [[ $allow_empty = "no" && -z $ret ]] && die "${FUNCNAME[0]}: Could not get '$1' for interaction with name '$bondname' from ${CSGXMLFILE} and no default was found in ${VOTCA_CSG_DEFAULTS}" 322 [[ -z $ret ]] && echo "${FUNCNAME[0]}: returning emtpy value for '$1'" >&2 323 echo "${ret}" 324} 325export -f csg_get_interaction_property 326 327csg_get_property () { #get an property from the xml file 328 local ret allow_empty 329 if [[ $1 = "--allow-empty" ]]; then 330 shift 331 allow_empty="yes" 332 else 333 allow_empty="no" 334 fi 335 [[ -n $1 ]] || die "${FUNCNAME[0]}: Missing argument" 336 [[ -n "$CSGXMLFILE" ]] || die "${FUNCNAME[0]}: CSGXMLFILE is undefined (when calling from csg_call set it by --options option)" 337 [[ -n "$(type -p csg_property)" ]] || die "${FUNCNAME[0]}: Could not find csg_property" 338 #csg_property only fails if xml file is bad otherwise result is empty 339 #leave the -q here to avoid flooding with messages 340 ret="$(critical -q csg_property --file "$CSGXMLFILE" --path ${1} --short --print . | trim_all)" 341 #overwrite with function call value 342 [[ -z $ret && -n $2 ]] && ret="$2" 343 [[ -z $ret ]] && echo "${FUNCNAME[0]}: No value for '$1' found in $CSGXMLFILE, trying ${VOTCA_CSG_DEFAULTS}" >&2 344 #if still empty fetch it from defaults file 345 if [[ -z $ret && -f ${VOTCA_CSG_DEFAULTS} ]]; then 346 ret="$(critical -q csg_property --file "${VOTCA_CSG_DEFAULTS}" --path "${1}" --short --print . | trim_all)" 347 [[ $allow_empty = "yes" && -n "$res" ]] && msg "WARNING: '${FUNCNAME[0]} $1' was called with --allow-empty, but a default was found in '${VOTCA_CSG_DEFAULTS}'" 348 #avoid endless recursion 349 [[ $1 = cg.inverse.program && -n $ret ]] && sim_prog="$ret" || \ 350 sim_prog="$(csg_get_property cg.inverse.program)" #no problem to call recursively as sim_prog has a default 351 if [[ -z $ret ]] && [[ $1 = *${sim_prog}* ]]; then 352 local path=${1/${sim_prog}/sim_prog} 353 ret="$(critical -q csg_property --file "${VOTCA_CSG_DEFAULTS}" --path "${path}" --short --print . | trim_all)" 354 fi 355 [[ -n $ret ]] && echo "${FUNCNAME[0]}: value for '$1' from ${VOTCA_CSG_DEFAULTS}: $ret" >&2 356 [[ $allow_empty = "yes" && -n "$res" ]] && msg "WARNING: '${FUNCNAME[0]} $1' was called with --allow-empty, but a default was found in '${VOTCA_CSG_DEFAULTS}'" 357 fi 358 [[ $allow_empty = "no" && -z $ret ]] && die "${FUNCNAME[0]}: Could not get '$1' from ${CSGXMLFILE} and no default was found in ${VOTCA_CSG_DEFAULTS}" 359 [[ -z $ret ]] && echo "${FUNCNAME[0]}: returning emtpy value for '$1'" >&2 360 echo "${ret}" 361} 362export -f csg_get_property 363 364trim_all() { #make multiple lines into one and strip white space from beginning and the end, reads from stdin 365 [[ -n "$(type -p tr)" ]] || die "${FUNCNAME[0]}: Could not find tr" 366 tr '\n' ' ' | sed -e s'/^[[:space:]]*//' -e s'/[[:space:]]*$//' || die "${FUNCNAME[0]}: sed of argument $i failed" 367} 368export -f trim_all 369 370mark_done () { #mark a task (1st argument) as done in the restart file 371 local file 372 [[ -n $1 ]] || die "${FUNCNAME[0]}: Missing argument" 373 file="$(get_restart_file)" 374 is_done "$1" || echo "$1 done" >> "${file}" 375} 376export -f mark_done 377 378is_done () { #checks if something is already do in the restart file 379 local file 380 [[ -n $1 ]] || die "${FUNCNAME[0]}: Missing argument" 381 file="$(get_restart_file)" 382 [[ -f ${file} ]] || return 1 383 [[ -n "$(sed -n "/^$1 done\$/p" ${file})" ]] && return 0 384 return 1 385} 386export -f is_done 387 388is_int() { #checks if all arguments are integers 389 local i 390 [[ -z $1 ]] && die "${FUNCNAME[0]}: Missing argument" 391 for i in "$@"; do 392 [[ -n $i && -z ${i//[0-9]} ]] || return 1 393 done 394 return 0 395} 396export -f is_int 397 398to_int() { #convert all given numbers to int using awk's int function 399 local i 400 [[ -z $1 ]] && die "${FUNCNAME[0]}: Missing argument" 401 for i in "$@"; do 402 is_num "$i" || die "${FUNCNAME[0]}: $i is not a number" 403 awk -v x="$i" 'BEGIN{ print ( int(x) ) }' || die "${FUNCNAME[0]}: awk failed" 404 done 405 return 0 406} 407export -f to_int 408 409is_part() { #checks if 1st argument is part of the set given by other arguments 410 [[ -z $1 || -z $2 ]] && die "${FUNCNAME[0]}: Missing argument" 411 [[ " ${@:2} " = *" $1 "* ]] 412} 413export -f is_part 414 415has_duplicate() { #check if one of the arguments is double 416 local i j 417 [[ -z $1 ]] && die "${FUNCNAME[0]}: Missing argument" 418 for ((i=1;i<$#;i++)); do 419 for ((j=i+1;j<=$#;j++)); do 420 [[ ${!i} = ${!j} ]] && echo ${!i} && return 0 421 done 422 done 423 return 1 424} 425export -f has_duplicate 426 427remove_duplicate() { #remove duplicates list of arguments 428 local i j out=() c 429 [[ -z $1 ]] && die "${FUNCNAME[0]}: Missing argument" 430 for ((i=1;i<=$#;i++)); do 431 c=0 432 for ((j=0;j<${#out[@]};j++)); do 433 [[ ${!i} = ${out[j]} ]] && ((c++)) 434 done 435 [[ $c -eq 0 ]] && out+=( "${!i}" ) 436 done 437 echo "${out[@]}" 438} 439export -f remove_duplicate 440 441is_num() { #checks if all arguments are numbers 442 local i res 443 [[ -z $1 ]] && die "${FUNCNAME[0]}: Missing argument" 444 for i in "$@"; do 445 res=$(awk -v x="$i" 'BEGIN{ print ( x+0==x ) }') || die "${FUNCNAME[0]}: awk failed" 446 [[ $res -eq 1 ]] || return 1 447 unset res 448 done 449 return 0 450} 451export -f is_num 452 453get_stepname() { #get the dir name of a certain step number (1st argument) 454 local name 455 [[ -n $1 ]] || die "${FUNCNAME[0]}: Missing argument" 456 if [[ $1 = "--trunc" ]]; then 457 echo "step_" 458 return 0 459 fi 460 is_int "${1}" || die "${FUNCNAME[0]}: needs a int as argument, but got $1" 461 name="$(printf step_%03i "$1")" 462 [[ -z $name ]] && die "${FUNCNAME[0]}: Could not get stepname" 463 echo "$name" 464} 465export -f get_stepname 466 467update_stepnames(){ #updated the current working step to a certain number (1st argument) 468 local thisstep laststep nr 469 [[ -n $1 ]] || die "${FUNCNAME[0]}: Missing argument" 470 nr="$1" 471 is_int "$nr" || die "${FUNCNAME[0]}: needs a int as argument, but got $nr" 472 [[ -z $CSG_MAINDIR ]] && die "${FUNCNAME[0]}: CSG_MAINDIR is undefined" 473 [[ -d $CSG_MAINDIR ]] || die "${FUNCNAME[0]}: $CSG_MAINDIR is not dir" 474 thisstep="$(get_stepname $nr)" 475 export CSG_THISSTEP="$CSG_MAINDIR/$thisstep" 476 if [[ $nr -gt 0 ]]; then 477 laststep="$(get_stepname $((nr-1)) )" 478 export CSG_LASTSTEP="$CSG_MAINDIR/$laststep" 479 fi 480} 481export -f update_stepnames 482 483get_current_step_dir() { #print the directory of the current step 484 [[ -z $CSG_THISSTEP ]] && die "${FUNCNAME[0]}: \$CSG_THISSTEP is undefined (when calling from csg_call export it yourself)" 485 if [[ $1 = "--no-check" ]]; then 486 : 487 else 488 [[ -d $CSG_THISSTEP ]] || die "${FUNCNAME[0]}: $CSG_THISSTEP is not dir" 489 fi 490 echo "$CSG_THISSTEP" 491 492} 493export -f get_current_step_dir 494 495get_last_step_dir() { #print the directory of the last step 496 [[ -z $CSG_LASTSTEP ]] && die "${FUNCNAME[0]}: CSG_LASTSTEP is undefined (when calling from csg_call export it yourself)" 497 [[ -d $CSG_LASTSTEP ]] || die "${FUNCNAME[0]}: $CSG_LASTSTEP is not dir" 498 echo "$CSG_LASTSTEP" 499} 500export -f get_last_step_dir 501 502get_main_dir() { #print the main directory 503 [[ -z $CSG_MAINDIR ]] && die "${FUNCNAME[0]}: CSG_MAINDIR is defined" 504 [[ -d $CSG_MAINDIR ]] || die "${FUNCNAME[0]}: $CSG_MAINDIR is not dir" 505 echo "$CSG_MAINDIR" 506} 507export -f get_main_dir 508 509get_current_step_nr() { #print the main directory 510 local name nr 511 name=$(get_current_step_dir) 512 nr=$(get_step_nr $name) 513 echo "$nr" 514} 515export -f get_current_step_nr 516 517get_step_nr() { #print the number of a certain step directory (1st argument) 518 local nr trunc 519 trunc=$(get_stepname --trunc) 520 [[ -n $1 ]] || die "${FUNCNAME[0]}: Missing argument" 521 nr=${1##*/} 522 nr=${nr#$trunc} 523 #convert to base 10 and cut leading zeros 524 nr=$((10#$nr)) 525 is_int "$nr" || die "${FUNCNAME[0]}: Could not fetch step nr, got $nr" 526 echo "$nr" 527} 528export -f get_step_nr 529 530cp_from_main_dir() { #copy something from the main directory 531 critical pushd "$(get_main_dir)" 532 if [[ $1 = "--rename" ]]; then 533 shift 534 [[ $# -eq 2 && -n $1 && -n $2 ]] || die "${FUNCNAME[0]}: with --rename option has to be called with exactly 2 (non-empty) arguments" 535 echo "cp_from_main_dir: '$1' to '$2'" 536 critical cp $1 "$(dirs -l +1)/$2" 537 else 538 echo "cp_from_main_dir: '$@'" 539 critical cp $@ "$(dirs -l +1)" 540 fi 541 critical popd 542} 543export -f cp_from_main_dir 544 545cp_from_last_step() { #copy something from the last step 546 if [[ $1 = "--rename" ]]; then 547 shift 548 [[ $# -eq 2 && -n $1 && -n $2 ]] || die "${FUNCNAME[0]}: with --rename option has to be called with exactly 2 (non-empty) arguments" 549 echo "cp_from_last_step: '$1' to '$2'" 550 critical pushd "$(get_last_step_dir)" 551 critical cp $1 "$(dirs -l +1)/$2" 552 critical popd 553 else 554 echo "cp_from_last_step: '$@'" 555 critical pushd "$(get_last_step_dir)" 556 critical cp $@ "$(dirs -l +1)" 557 critical popd 558 fi 559} 560export -f cp_from_last_step 561 562get_time() { #gives back current time in sec from 1970 563 date +%s || die "${FUNCNAME[0]}: date +%s failed" 564} 565export -f get_time 566 567get_number_tasks() { #get the number of possible tasks from the xml file or determine it automatically under some systems 568 local tasks 569 tasks="$(csg_get_property cg.inverse.simulation.tasks)" 570 [[ $tasks = "auto" ]] && tasks=0 571 is_int "$tasks" || die "${FUNCNAME[0]}: cg.inverse.simulation.tasks needs to be a number or 'auto', but I got $(csg_get_property cg.inverse.simulation.tasks)" 572 if [[ $tasks -eq 0 ]]; then #auto-detect 573 if [[ -r /proc/cpuinfo ]]; then #linux 574 tasks=$(sed -n '/processor/p' /proc/cpuinfo | sed -n '$=') 575 elif [[ -x /usr/sbin/sysctl ]]; then #mac os 576 tasks=$(/usr/sbin/sysctl -n hw.ncpu) 577 elif [[ -x /usr/sbin/lsdev ]]; then #AIX 578 tasks=$(/usr/sbin/lsdev | sed -n '/Processor/p' | sed -n '$=') 579 fi 580 is_int "${tasks}" || tasks=1 #failback in case we got non-int 581 fi 582 if [[ ${CSG_NUM_THREADS} ]]; then 583 is_int "${CSG_NUM_THREADS}" || die "${FUNCNAME[0]}: value of CSG_NUM_THREADS needs to be a number, but I got ${CSG_NUM_THREADS}" 584 msg --color blue --to-stderr "${FUNCNAME[0]}: Overwriting cg.inverse.simulation.tasks with '${CSG_NUM_THREADS}'" 585 tasks="${CSG_NUM_THREADS}" 586 fi 587 echo "$tasks" 588} 589export -f get_number_tasks 590 591get_table_comment() { #get comment lines from a table and add common information, which include the git id and other information 592 local version co 593 [[ -n "$(type -p csg_call)" ]] || die "${FUNCNAME[0]}: Could not find csg_call" 594 version="$(csg_call --version)" || die "${FUNCNAME[0]}: csg_call --version failed" 595 echo "Created on $(date) by $USER@$HOSTNAME" 596 echo "called from $version" | sed "s/csg_call/${0##*/}/" 597 [[ -n "${CSGXMLFILE}" ]] && echo "settings file: '$(globalize_file "${CSGXMLFILE}")'" 598 echo "working directory: $PWD" 599 if [[ -f $1 ]]; then 600 co=$(sed -n 's/^[#@][[:space:]]*//p' "$1") || die "${FUNCNAME[0]}: sed failed" 601 [[ -n $co ]] && echo "Comments from $(globalize_file $1):\n$co" 602 fi 603} 604export -f get_table_comment 605 606csg_inverse_clean() { #clean out the main directory 607 local i files log t 608 [[ -n $1 ]] && t="$1" || t="30" 609 log="$(csg_get_property cg.inverse.log_file 2>/dev/null)" 610 echo -e "So, you want to clean?\n" 611 echo "I will remove:" 612 files="$(ls -d done ${log} $(get_stepname --trunc)* *~ 2>/dev/null)" 613 if [[ -z $files ]]; then 614 echo "Nothing to clean" 615 else 616 msg --color red $files 617 msg --color blue "\nCTRL-C to stop it" 618 for ((i=$t;i>0;i--)); do 619 echo -n "$i " 620 sleep 1 621 done 622 rm -rf $files 623 msg --color green "\n\nDone, hope you are happy now" 624 fi 625} 626export -f csg_inverse_clean 627 628check_path_variable() { #check if a variable contains only valid paths 629 local old_IFS dir 630 [[ -z $1 ]] && die "${FUNCNAME[0]}: Missing argument" 631 for var in "$@"; do 632 [[ -z $var ]] && continue 633 old_IFS="$IFS" 634 IFS=":" 635 for dir in ${!var}; do 636 [[ -z $dir ]] && continue 637 [[ $dir = *votca* ]] || continue #to many error otherwise 638 [[ -d $dir ]] || die "${FUNCNAME[0]}: $dir from variable $var is not a directory" 639 done 640 IFS="$old_IFS" 641 done 642} 643export -f check_path_variable 644 645add_to_csgshare() { #added an directory to the csg internal search directories 646 local dir end="no" 647 [[ $1 = "--at-the-end" ]] && end="yes" && shift 648 [[ -z $1 ]] && die "${FUNCNAME[0]}: Missing argument" 649 for dirlist in "$@"; do 650 old_IFS="$IFS" 651 IFS=":" 652 for dir in $dirlist; do 653 #dir maybe contains $PWD or something 654 eval dir="$dir" 655 [[ -d $dir ]] || die "${FUNCNAME[0]}: Could not find scriptdir $dir" 656 dir="$(globalize_dir "$dir")" 657 if [[ $end = "yes" ]]; then 658 export CSGSHARE="${CSGSHARE}${CSGSHARE:+:}$dir" 659 export PERL5LIB="${PERL5LIB}${PERL5LIB:+:}$dir" 660 else 661 export CSGSHARE="$dir${CSGSHARE:+:}$CSGSHARE" 662 export PERL5LIB="$dir${PERL5LIB:+:}$PERL5LIB" 663 fi 664 done 665 IFS="$old_IFS" 666 done 667 check_path_variable CSGSHARE PERL5LIB 668} 669export -f add_to_csgshare 670 671globalize_dir() { #convert a local directory to a global one 672 [[ -z $1 ]] && die "${FUNCNAME[0]}: missing argument" 673 [[ -d $1 ]] || die "${FUNCNAME[0]}: '$1' is not a dir" 674 cd "$1" 675 pwd 676} 677export -f globalize_dir 678 679globalize_file() { #convert a local file name to a global one 680 [[ -z $1 ]] && die "${FUNCNAME[0]}: missing argument" 681 [[ -f $1 ]] || die "${FUNCNAME[0]}: '$1' is not a file" 682 local dir 683 [[ ${1%/*} = ${1} ]] && dir="." || dir="${1%/*}" 684 echo "$(globalize_dir "$dir")/${1##*/}" 685} 686export -f globalize_file 687 688source_function() { #source an extra function file 689 local function_file 690 [[ -n $1 ]] || die "${FUNCNAME[0]}: Missing argument" 691 function_file=$(source_wrapper functions $1) || die "${FUNCNAME[0]}: source_wrapper functions $1 failed" 692 source ${function_file} || die "${FUNCNAME[0]}: source ${function_file} failed" 693} 694export -f source_function 695 696csg_banner() { #print a big banner 697 local i l=0 list=() 698 [[ -z $1 ]] && return 0 699 for i in "$@"; do 700 while [[ -n $i && -z ${i/*\\n*} ]]; do 701 list[$l]="${i%%\\n*}" 702 ((l++)) 703 i="${i#*\\n}" 704 done 705 list[$l]=$i 706 ((l++)) 707 done 708 709 l="1" 710 for i in "${list[@]}"; do 711 [[ ${#l} -lt ${#i} ]] && l="${i}" 712 done 713 714 echo "####${l//?/#}" 715 echo "# ${l//?/ } #" 716 for i in "${list[@]}"; do 717 printf "# %-${#l}s #\n" "$i" 718 done 719 echo "# ${l//?/ } #" 720 echo "####${l//?/#}" 721} 722export -f csg_banner 723 724csg_calc() { #simple calculator, a + b, ... 725 local res ret=0 err="1e-2" 726 [[ -z $1 || -z $2 || -z $3 ]] && die "${FUNCNAME[0]}: Needs 3 arguments, but got '$*'" 727 is_num "$1" || die "${FUNCNAME[0]}: First argument of csg_calc should be a number, but got '$1'" 728 is_num "$3" || die "${FUNCNAME[0]}: Third argument of csg_calc should be a number, but got '$3'" 729 [[ -n "$(type -p awk)" ]] || die "${FUNCNAME[0]}: Could not find awk" 730 #we use awk -v because then " 1 " or "1\n" is equal to 1 731 case "$2" in 732 "+"|"-"|'*'|"/"|"^") 733 res="$(awk -v x="$1" -v y="$3" "BEGIN{print ( x $2 y ) }")" || die "${FUNCNAME[0]}: awk -v x='$1' -v y='$3' 'BEGIN{print ( x $2 y ) }' failed" 734 true;; 735 '>'|'<' ) 736 res="$(awk -v x="$1" -v y="$3" "BEGIN{print ( x $2 y )}")" || die "${FUNCNAME[0]}: awk -v x='$1' -v y='$3' 'BEGIN{print ( x $2 y )}' failed" 737 #awk return 1 for true and 0 for false, shell exit codes are the other way around 738 ret="$((1-$res))" 739 #return value matters 740 res="" 741 true;; 742 "="|"==") 743 #this is really tricky... case x=0,y=0 is catched by (x==y) after that |x-y|/max(|x|,|y|) will work expect for x,y beginng close to zero 744 res="$(awk -v x="$1" -v y="$3" -v e="$err" \ 745 'function max(x,y){return (x>y)?x:y;} function abs(x){return (x<0)?-x:x;} BEGIN{if (x==y){print 1;}else{if (abs(x-y)<e){print 1;}else{ print ( abs(x-y)/max(abs(x),abs(y)) < e );}}}')" \ 746 || die "${FUNCNAME[0]}: awk for =/== failed" 747 #awk return 1 for true and 0 for false, shell exit codes are the other way around 748 ret="$((1-$res))" 749 #return value matters 750 res="" 751 true;; 752 *) 753 die "${FUNCNAME[0]}: unknow operation" 754 true;; 755 esac 756 [[ -n $res ]] && echo "$res" 757 return $ret 758} 759export -f csg_calc 760 761show_csg_tables() { #show all concatinated csg tables 762 local old_IFS dir 763 old_IFS="$IFS" 764 IFS=":" 765 echo "#The order in which scripts get called" 766 echo "#CSGSHARE is $CSGSHARE" 767 for dir in ${CSGSHARE}; do 768 [[ -f $dir/csg_table ]] || continue 769 echo "#From: $dir/csg_table" 770 #remove comments and empty line, trim begin and end, tab to spaces 771 sed -e '/^#/d' -e '/^[[:space:]]*$/d' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/[[:space:]]\+/ /g' "$dir/csg_table" 772 done 773 IFS="$old_IFS" 774} 775export -f show_csg_tables 776 777get_command_from_csg_tables() { #print the name of script belonging to certain tags (1st, 2nd argument) 778 [[ -z $1 || -z $2 ]] && die "${FUNCNAME[0]}: Needs two tags" 779 show_csg_tables | \ 780 sed -e '/^#/d' | \ 781 sed -n "s/^$1 $2 \(.*\)$/\1/p" | \ 782 sed -n '1p' 783} 784export -f get_command_from_csg_tables 785 786source_wrapper() { #print the full name of a script belonging to two tags (1st, 2nd argument) 787 [[ -z $1 || -z $2 ]] && die "${FUNCNAME[0]}: Needs two tags" 788 local cmd script 789 if [[ $1 = "function" ]]; then 790 [[ $(type -t "$2") = "function" ]] || die "${FUNCNAME[0]}: could not find any function called '$2' (when calling from csg_call you might need to add --simprog option or set cg.inverse.program in the xml file)" 791 echo "$2" 792 else 793 cmd=$(get_command_from_csg_tables "$1" "$2") || die "${FUNCNAME[0]}: get_command_from_csg_tables '$1' '$2' failed" 794 [[ -z $cmd ]] && die "${FUNCNAME[0]}: Could not get any script from tags '$1' '$2'" 795 #cmd might contain option after the script name 796 script="${cmd%% *}" 797 real_script="$(find_in_csgshare "$script")" 798 echo "${cmd/${script}/${real_script}}" 799 fi 800} 801export -f source_wrapper 802 803find_in_csgshare() { #find a script in csg script search path 804 [[ -z $1 ]] && die "${FUNCNAME[0]}: missing argument" 805 #global path 806 if [[ -z ${1##/*} ]]; then 807 [[ -f $1 ]] || die "${FUNCNAME[0]}: $1 is a script with global path, but was not found" 808 echo "$1" && return 809 fi 810 local old_IFS dir 811 old_IFS="$IFS" 812 IFS=":" 813 for dir in ${CSGSHARE}; do 814 [[ -f $dir/$1 ]] && break 815 done 816 IFS="$old_IFS" 817 [[ -f $dir/$1 ]] && echo "$dir/$1" && return 818 die "${FUNCNAME[0]}: Could not find script $1 in $CSGSHARE" 819} 820export -f find_in_csgshare 821 822if [ -z "$(type -p mktemp)" ]; then 823 #do not document this 824 mktemp() { 825 [[ $1 = "-u" ]] && shift 826 [[ -z $1 ]] && die "${FUNCNAME[0]}: missing argument" 827 [[ -z ${1##*X} ]] || die "${FUNCNAME[0]}: argument has to end at least with X" 828 local end trunc i l tmp newend 829 end=${1##*[^X]} 830 trunc=${1%${end}} 831 l=${end//[^X]} 832 l=${#l} 833 while true; do 834 newend="$end" 835 for ((i=0;i<$l;i++)); do 836 newend="${newend/X/${RANDOM:0:1}}" 837 done 838 tmp="${trunc}${newend}" 839 [[ -f $tmp ]] || break 840 done 841 echo "$tmp" 842 } 843 export -f mktemp 844fi 845 846enable_logging() { #enables the logging to a certain file (1st argument) or the logfile taken from the xml file 847 local log 848 if [[ -z $1 ]]; then 849 log="$(csg_get_property cg.inverse.log_file 2>/dev/null)" 850 else 851 log="$1" 852 fi 853 log="${PWD}/${log##*/}" 854 export CSGLOG="$log" 855 if [[ -f $CSGLOG ]]; then 856 exec 3>&1 4>&2 >> "$CSGLOG" 2>&1 857 echo -e "\n\n#################################" 858 echo "# Appending to existing logfile #" 859 echo -e "#################################\n\n" 860 msg --color blue "Appending to existing logfile ${CSGLOG##*/}" 861 else 862 exec 3>&1 4>&2 >> "$CSGLOG" 2>&1 863 msg "For a more verbose log see: ${CSGLOG##*/}" 864 fi 865} 866export -f enable_logging 867 868get_restart_file() { #print the name of the restart file to use 869 local file 870 file="$(csg_get_property cg.inverse.restart_file)" 871 [[ -z ${file/*\/*} ]] && die "${FUNCNAME[0]}: cg.inverse.restart_file has to be a local file with slash '/'" 872 echo "$file" 873} 874export -f get_restart_file 875 876check_for_obsolete_xml_options() { #check xml file for obsolete options 877 local i 878 for i in cg.inverse.mpi.tasks cg.inverse.mpi.cmd cg.inverse.parallel.tasks cg.inverse.parallel.cmd \ 879 cg.inverse.gromacs.mdrun.bin cg.inverse.espresso.bin cg.inverse.scriptdir cg.inverse.gromacs.grompp.topol \ 880 cg.inverse.gromacs.grompp.index cg.inverse.gromacs.g_rdf.topol cg.inverse.convergence_check \ 881 cg.inverse.convergence_check_options.name_glob cg.inverse.convergence_check_options.limit \ 882 cg.inverse.espresso.table_end cg.inverse.gromacs.traj_type cg.inverse.gromacs.topol_out \ 883 cg.inverse.espresso.blockfile cg.inverse.espresso.blockfile_out cg.inverse.espresso.n_steps \ 884 cg.inverse.espresso.exclusions cg.inverse.espresso.debug cg.inverse.espresso.n_snapshots \ 885 cg.non-bonded.inverse.espresso.index1 cg.non-bonded.inverse.espresso.index2 cg.inverse.espresso.success \ 886 cg.inverse.espresso.scriptdir cg.non-bonded.inverse.post_update_options.kbibi.type \ 887 cg.inverse.imc.numpy.bin cg.inverse.imc.octave.bin cg.inverse.imc.matlab.bin cg.inverse.imc.solver \ 888 cg.non-bonded.inverse.imc.reg \ 889 ; do 890 [[ -z "$(csg_get_property --allow-empty $i)" ]] && continue #filter me away 891 new="" 892 case $i in 893 cg.inverse.mpi.tasks|cg.inverse.parallel.tasks) 894 new="cg.inverse.simulation.tasks";; 895 cg.inverse.gromacs.mdrun.bin|cg.inverse.espresso.bin) 896 new="${i/bin/command}";; 897 cg.inverse.scriptdir) 898 new="${i/dir/path}";; 899 cg.inverse.gromacs.grompp.index) 900 new="${i/.grompp}";; 901 cg.inverse.gromacs.grompp.topol) 902 new="cg.inverse.gromacs.topol_in";; 903 cg.inverse.gromacs.g_rdf.topol) 904 new="${i/g_}";; 905 cg.inverse.gromacs.topol_out) 906 new="${i/_out}";; 907 cg.inverse.gromacs.traj_type) 908 new="";; 909 cg.inverse.convergence_check) 910 new="${i}.type";; 911 cg.inverse.convergence_check_options.limit) 912 new="cg.inverse.convergence_check.limit";; 913 cg.non-bonded.inverse.imc.reg) 914 new="cg.inverse.imc.<group>.reg";; 915 esac 916 [[ -n $new ]] && new="has been renamed to $new" || new="has been removed" 917 die "${FUNCNAME[0]}: The xml option $i $new\nPlease remove the obsolete options from the xmlfile" 918 done 919} 920export -f check_for_obsolete_xml_options 921 922command_not_found_handle() { #print and error message if a command or a function was not found 923 die "Command/function $1 not found (when calling from csg_call you might need to add --simprog option or set cg.inverse.program in the xml file)" 924} 925export -f command_not_found_handle 926 927#in bash4 this is not needed, but for older bash we add add a failback from most important simulation functions 928for i in simulation_finish checkpoint_exist get_simulation_setting; do 929 eval $i\(\) { command_not_found_handle $i\; } 930 eval export -f $i 931done 932unset i 933 934