diff --git a/bin/foamJob b/bin/foamJob
index 7b8143c2a8f63cbcd23cd4fae7e6006f7b2f0649..2b5f4f67b2644cad2e252c59603c69ca1512df74 100755
--- a/bin/foamJob
+++ b/bin/foamJob
@@ -3,7 +3,7 @@
 # =========                 |
 # \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
 #  \\    /   O peration     |
-#   \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+#   \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
 #    \\/     M anipulation  |
 #-------------------------------------------------------------------------------
 #                           | Copyright (C) 2011-2015 OpenFOAM Foundation
@@ -32,6 +32,9 @@
 #     Redirects the output to 'log' in the case directory.
 #
 #------------------------------------------------------------------------------
+# If dispatching via foamExec
+foamExec="$WM_PROJECT_DIR/bin/tools/foamExec"
+
 usage() {
     exec 1>&2
     while [ "$#" -ge 1 ]; do echo "$1"; shift; done
@@ -40,80 +43,30 @@ usage() {
 Usage: ${0##*/} [OPTION] <application> ...
 options:
   -case <dir>       specify alternative case directory, default is the cwd
-  -parallel         parallel run of processors
-  -screen           also sends output to screen
-  -append           append to log file instead of overwriting it
+  -parallel         run in parallel (with mpirun)
+  -screen           also send output to screen
+  -append           append to existing log file instead of overwriting it
+  -log=FILE         specify the log file
+  -log-app          Use log.{appName} for the log file
+  -no-check         run without fewer checks (eg, processor dirs etc)
+  -no-log           run without log file
   -wait             wait for execution to complete (when not using -screen)
-  -version=VER      specify an alternative OpenFOAM version
   -help             print the usage
 
-* run an OpenFOAM job in background.
-  Redirects the output to 'log' in the case directory
+Run an OpenFOAM job in background, redirecting output to a 'log' file
+in the case directory
 
 USAGE
     exit 1
 }
 
-# Echo strings that have single quotes
-echoArgs() {
-    addSpace=""
-
-    for stringItem in "$@"
-    do
-        echo -n "${addSpace}"
-
-        if [ "${stringItem##* }" = "$stringItem" ]
-        then
-            echo -n "$stringItem"
-            addSpace=" "
-        else
-            echo -n "'$stringItem'"
-            addSpace=" "
-        fi
-    done
-
-    unset stringItem addSpace
-}
-
-
-# Replacement for possibly buggy 'which'
-findExec() {
-    case "$1" in
-    */*)
-        if [ -x "$1" ]
-        then
-            echo "$1"
-            return 0
-        fi
-        ;;
-    esac
-
-    oldIFS=$IFS
-    IFS=':'
-    for d in $PATH
-    do
-        # echo "testing: $d/$1" 1>&2
-        if [ -x "$d/$1" -a ! -d "$d/$1" ]
-        then
-            # echo "Found exec: $d/$1" 1>&2
-            IFS=$oldIFS
-            echo "$d/$1"
-            return 0
-        fi
-     done
-     IFS=$oldIFS
-     echo ""
-     return 1
-}
-
-
+#------------------------------------------------------------------------------
+# Parse options
 
-# Main script
-#~~~~~~~~~~~~
-unset parallelOpt screenOpt waitOpt
-unset projectVersion
+logFile="log"
+optCheck=true
+unset optParallel optScreen optWait logMode
 
-# Parse options
 while [ "$#" -gt 0 ]
 do
     case "$1" in
@@ -122,27 +75,41 @@ do
         ;;
     -case)
         [ "$#" -ge 2 ] || usage "'$1' option requires an argument"
-        cd "$2" 2>/dev/null || usage "directory does not exist:  '$2'"
+        cd "$2" 2>/dev/null || usage "directory does not exist: '$2'"
         shift
         ;;
     -p | -parallel)
-        parallelOpt=true
+        optParallel=true
         ;;
     -s | -screen)
-        screenOpt=true
+        optScreen=true
         ;;
     -a | -append)
-        appendOpt=true
+        logMode=append
         ;;
     -w | -wait)
-        waitOpt=true
+        optWait=true
+        ;;
+    -no-check*)
+        unset optCheck
+        ;;
+    -no-log)
+        logMode=none
+        logFile=log     # Consistency if -append toggles it back on
+        ;;
+    -log=*)
+        logFile="${1##*=}"
+        [ -n "$logFile" ] || logFile="log"
+        ;;
+    -log-app)
+        logFile="{LOG_APPNAME}"  # Tag for log.appName output
         ;;
     -version=*)
-        projectVersion="${1#*=}"    # for foamExec
+        echo "Ignoring version option" 1>&2
         ;;
     -v | -version)
         [ "$#" -ge 2 ] || usage "'$1' option requires an argument"
-        projectVersion="$2"         # for foamExec
+        echo "Ignoring version option" 1>&2
         shift
         ;;
     --)
@@ -166,65 +133,74 @@ done
 # The requested application
 appName="$1"
 
-# Use foamExec for a specified version
-# Also need foamExec for remote (parallel) runs
-if [ -n "$projectVersion" -o "$parallelOpt" = true ]
+# Does application even exist?
+APPLICATION="$(command -v "$appName")" || \
+    usage "Application '$appName' not found"
+
+if [ "$logFile" = "{LOG_APPNAME}" ]
 then
-    # When possible, determine if application even exists
-    if [ -z "$projectVersion" ]
-    then
-        findExec "$appName" >/dev/null || usage "Application '$appName' not found"
-    fi
+    logFile="log.${appName##*/}"
+fi
 
-    # Use foamExec for dispatching
-    APPLICATION=$(findExec foamExec) || usage "'foamExec' not found"
 
-    [ -n "$projectVersion" ] && APPLICATION="$APPLICATION -version $projectVersion"
+# Need foamExec for remote (parallel) runs
+if [ "$optParallel" = true ]
+then
+    # Use foamExec for dispatching
+    [ -x "$foamExec" ] || usage "File not found: $foamExec"
 
+    APPLICATION="$foamExec"
 else
-    APPLICATION=$(findExec "$appName") || usage "Application '$appName' not found"
-    echo "Application : $appName"
+    # Drop first argument in favour of fully qualified APPLICATION
     shift
 fi
 
 
-if [ "$parallelOpt" = true ]
-then
-    # parallel
-    # ~~~~~~~~
+# Stringify args, adding single quotes for args with spaces
+echoArgs()
+{
+    unset stringifiedArgs
+
+    for stringItem in "$@"
+    do
+        case "$stringItem" in (*' '*) stringItem="'$stringItem'" ;; esac
+        stringifiedArgs="${stringifiedArgs}${stringifiedArgs:+ }${stringItem}"
+    done
+    echo "$stringifiedArgs"
+}
+
 
+if [ "$optParallel" = true ]
+then
     #
-    # Check if the case decomposed
+    # Parallel
     #
-    if [ -r "processor0" -o -r "processors" ]
+    dict="system/decomposeParDict"
+
+    [ -r "$dict" ] || {
+        echo "No $dict found, which is required for parallel running."
+        exit 1
+    }
+
+    nProcs="$(foamDictionary -entry numberOfSubdomains -value $dict 2>/dev/null)"
+
+    # Check if case is decomposed
+    if [ "$optCheck" = true ]
     then
-        nprocs="$(foamDictionary -entry numberOfSubdomains -value system/decomposeParDict 2>/dev/null)"
-    else
-        echo "Case is not currently decomposed"
-        if [ -r system/decomposeParDict ]
-        then
-            echo "system/decomposeParDict exists"
-            echo "Try decomposing with \"foamJob decomposePar\""
+        [ -r "processor0" ] || [ -r "processors" ] || {
+            echo "Case is not currently decomposed"
+            echo "Try decomposing first with \"foamJob decomposePar\""
             exit 1
-        else
-            echo "Cannot find system/decomposeParDict file required to decompose the case for parallel running."
-            echo "Please consult the User Guide for details of parallel running"
-            exit 1
-        fi
+        }
     fi
 
-    #
-    # Find mpirun
-    #
-    mpirun=$(findExec mpirun) || usage "'mpirun' not found"
-    mpiopts="-np $nprocs"
+    # Locate mpirun
+    mpirun=$(command -v mpirun) || usage "'mpirun' not found"
+    mpiopts="-n $nProcs"
 
-    #
     # Check if the machine ready to run parallel
-    #
-    echo "Parallel processing using $WM_MPLIB with $nprocs processors"
     case "$WM_MPLIB" in
-    *OPENMPI)
+    *OPENMPI*)
         # Add hostfile info
         for hostfile in \
             hostfile \
@@ -233,7 +209,7 @@ then
             system/machines \
             ;
         do
-            if [ -r $hostfile ]
+            if [ -r "$hostfile" ]
             then
                 mpiopts="$mpiopts -hostfile $hostfile"
                 break
@@ -242,59 +218,107 @@ then
 
         # Send FOAM_SETTINGS to parallel processes, so that the proper
         # definitions are sent as well.
-        mpiopts="$mpiopts -x FOAM_SETTINGS"
+        if [ -n "$FOAM_SETTINGS" ]
+        then
+            mpiopts="$mpiopts -x FOAM_SETTINGS"
+        fi
         ;;
     esac
 
     #
     # Run (in parallel)
     #
-    if [ "$screenOpt" = true ]
+    echo "Application : $appName ($nProcs processes)"
+    if [ "$logMode" != "none" ]
     then
-        [ "$appendOpt" = true ] && teeOpts=" -a"
-        echo "Executing: $mpirun $mpiopts $APPLICATION $(echoArgs "$@") -parallel | tee $teeOpts log"
-        $mpirun $mpiopts $APPLICATION "$@" -parallel | tee $teeOpts log
+    echo "Output      : $logFile"
+    fi
+    echo "Executing   : $mpirun $mpiopts $APPLICATION $(echoArgs "$@") -parallel"
+    if [ "$optScreen" = true ]
+    then
+        case "$logMode" in
+        none)
+            "$mpirun" $mpiopts "$APPLICATION" "$@" -parallel
+            ;;
+        append)
+            "$mpirun" $mpiopts "$APPLICATION" "$@" -parallel | tee -a "$logFile"
+            ;;
+        *)
+            "$mpirun" $mpiopts "$APPLICATION" "$@" -parallel | tee "$logFile"
+            ;;
+        esac
     else
-        if [ "$appendOpt" = true ]
-        then
-            echo "Executing: $mpirun $mpiopts $APPLICATION $(echoArgs "$@") -parallel >> log 2>&1"
-            $mpirun $mpiopts $APPLICATION "$@" -parallel >> log 2>&1 &
-        else
-            echo "Executing: $mpirun $mpiopts $APPLICATION $(echoArgs "$@") -parallel > log 2>&1"
-            $mpirun $mpiopts $APPLICATION "$@" -parallel > log 2>&1 &
-        fi
+        case "$logMode" in
+        none)
+            "$mpirun" $mpiopts "$APPLICATION" "$@" -parallel > /dev/null 2>&1 &
+            ;;
+        append)
+            "$mpirun" $mpiopts "$APPLICATION" "$@" -parallel >> "$logFile" 2>&1 &
+            ;;
+        *)
+            "$mpirun" $mpiopts "$APPLICATION" "$@" -parallel > "$logFile" 2>&1 &
+            ;;
+        esac
 
         pid=$!
-        if [ "$waitOpt" = true ]
+        if [ "$optWait" = true ]
         then
-            wait $pid
+            echo "Waiting for process $pid to finish"
+            wait "$pid"
+            echo "Process $pid finished"
+        else
+            echo "Process id  : $pid"
         fi
     fi
 
 else
     #
-    # Run (on single processor)
+    # Serial
     #
-    if [ "$screenOpt" = true ]
+    echo "Application : $appName ($nProcs processes)"
+    if [ "$logMode" != "none" ]
+    then
+    echo "Output      : $logFile"
+    fi
+    echo "Executing   : $APPLICATION $(echoArgs "$@")"
+    if [ "$optScreen" = true ]
     then
-        [ "$appendOpt" = true ] && teeOpts=" -a"
-        echo "Executing: $APPLICATION $(echoArgs "$@") | tee $teeOpts log &"
-        $APPLICATION "$@" | tee $teeOpts log &
-        wait $!
+        case "$logMode" in
+        none)
+            "$APPLICATION" "$@" &
+            ;;
+        append)
+            "$APPLICATION" "$@" | tee -a "$logFile" &
+            ;;
+        *)
+            "$APPLICATION" "$@" | tee "$logFile" &
+            ;;
+        esac
+
+        pid=$!
+        echo "Process id  : $pid"
+        wait "$pid"
     else
-        if [ "$appendOpt" = true ]
-        then
-            echo "Executing: $APPLICATION $(echoArgs "$@") >> log 2>&1 &"
-            $APPLICATION "$@" >> log 2>&1 &
-        else
-            echo "Executing: $APPLICATION $(echoArgs "$@") > log 2>&1 &"
-            $APPLICATION "$@" > log 2>&1 &
-        fi
+        case "$logMode" in
+        none)
+            "$APPLICATION" "$@" > /dev/null 2>&1 &
+            ;;
+        append)
+            "$APPLICATION" "$@" >> "$logFile" 2>&1 &
+            ;;
+        *)
+            "$APPLICATION" "$@" > "$logFile" 2>&1 &
+            ;;
+        esac
 
         pid=$!
-        if [ "$waitOpt" = true ]
+        if [ "$optWait" = true ]
         then
-            wait $pid
+            echo "Waiting for process $pid to finish"
+            wait "$pid"
+            echo "Process $pid finished"
+        else
+            echo "Process id  : $pid"
         fi
     fi
 fi
diff --git a/bin/tools/foamExec b/bin/tools/foamExec
new file mode 100755
index 0000000000000000000000000000000000000000..80c266a9cb8fd6e1fee221aefbf4969ee099f5e7
--- /dev/null
+++ b/bin/tools/foamExec
@@ -0,0 +1,120 @@
+#!/bin/bash
+#------------------------------------------------------------------------------
+# =========                 |
+# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#  \\    /   O peration     |
+#   \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+#    \\/     M anipulation  |
+#-------------------------------------------------------------------------------
+# License
+#     This file is part of OpenFOAM.
+#
+#     OpenFOAM is free software: you can redistribute it and/or modify it
+#     under the terms of the GNU General Public License as published by
+#     the Free Software Foundation, either version 3 of the License, or
+#     (at your option) any later version.
+#
+#     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+#     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+#     for more details.
+#
+#     You should have received a copy of the GNU General Public License
+#     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Script
+#     foamExec <application> ...
+#
+# Description
+#     Runs an application (with arguments) after first sourcing the OpenFOAM
+#     etc/bashrc file from the project directory
+#
+#     This script must exist in $WM_PROJECT_DIR/bin/tools
+#
+#     Can useful for parallel runs. For example,
+#
+#     mpirun -n <nProcs> \
+#         projectDir/bin/tools/foamExec <simpleFoam> ... -parallel
+#
+#------------------------------------------------------------------------------
+usage() {
+    exec 1>&2
+    while [ "$#" -ge 1 ]; do echo "$1"; shift; done
+    cat<<USAGE
+
+Usage: ${0##*/} [OPTION] <application> ...
+
+options:
+  -help             Print the usage
+
+Runs an application (with arguments) after first sourcing the OpenFOAM
+etc/bashrc file from the project directory
+
+USAGE
+    exit 1
+}
+
+# Report error and exit
+die()
+{
+    exec 1>&2
+    echo
+    echo "Error encountered:"
+    while [ "$#" -ge 1 ]; do echo "    $1"; shift; done
+    echo
+    echo "See '$0 -help' for usage"
+    echo
+    exit 1
+}
+
+#-------------------------------------------------------------------------------
+toolsDir="${0%/*}"                                  # The bin/tools dir
+projectDir="${toolsDir%/bin/tools}"                 # Project dir
+
+# Parse options
+while [ "$#" -gt 0 ]
+do
+    case "$1" in
+    -h | -help*)
+        usage
+        ;;
+    --)
+        shift
+        break
+        ;;
+    -*)
+        die "unknown option: '$1'"
+        ;;
+    *)
+        break
+        ;;
+    esac
+    shift
+done
+
+#-------------------------------------------------------------------------------
+
+[ "$#" -ge 1 ] || die "No application specified"
+
+[ -d "$projectDir" ] || {
+    echo "Error: no project dir: $projectDir" 1>&2
+    exit 2
+}
+
+[ -f "$projectDir/etc/bashrc" ] || {
+    echo "Error: file not found: $projectDir/etc/bashrc" 1>&2
+    exit 2
+}
+
+
+# Source bashrc within a function to preserve command-line arguments
+# - this will not have aliases, but working non-interactively anyhow
+sourceBashrc()
+{
+    . "$projectDir/etc/bashrc" $FOAM_SETTINGS
+}
+
+sourceBashrc
+exec "$@"
+
+#------------------------------------------------------------------------------