From a64014e4ca1d28f85b32a0d00de81a30f53c3faf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Krup=C4=8D=C3=ADk?= <lukas.krupcik@vsb.cz>
Date: Thu, 19 Jul 2018 13:44:41 +0200
Subject: [PATCH] 	new file:   Ubuntu/16.04-OpenFOAM/README 	new
 file:   Ubuntu/16.04-OpenFOAM/Ubuntu-16.04-Bootstrap 	new file:  
 Ubuntu/16.04-OpenFOAM/app/check 	new file:  
 Ubuntu/16.04-OpenFOAM/app/monitor 	new file:  
 Ubuntu/16.04-OpenFOAM/app/startup 	new file:  
 Ubuntu/16.04-OpenFOAM/hpc_image 	new file:  
 Ubuntu/16.04-OpenFOAM/openfoam_hacks/aliases 	new file:  
 Ubuntu/16.04-OpenFOAM/openfoam_hacks/bashrc 	new file:  
 Ubuntu/16.04-OpenFOAM/openfoam_hacks/exec 	new file:  
 Ubuntu/16.04-OpenFOAM/recipe-openfoam.txt 	new file:  
 Ubuntu/16.04-OpenFOAM/singularity_image.tar 	new file:   install/CENTOS7 
 new file:   install/CENTOS7GPU 	new file:   install/CENTOS7MIC 
 modified:   install/install.sh 	deleted:    install/CENTOS74 
 deleted:    install/CENTOS74GPU 	deleted:    install/CENTOS74MIC

---
 Ubuntu/16.04-OpenFOAM/README                 |  74 +++++++
 Ubuntu/16.04-OpenFOAM/Ubuntu-16.04-Bootstrap |  93 ++++++++
 Ubuntu/16.04-OpenFOAM/app/check              | 183 ++++++++++++++++
 Ubuntu/16.04-OpenFOAM/app/monitor            |  62 ++++++
 Ubuntu/16.04-OpenFOAM/app/startup            | 106 +++++++++
 Ubuntu/16.04-OpenFOAM/hpc_image              |  71 ++++++
 Ubuntu/16.04-OpenFOAM/openfoam_hacks/aliases | 120 +++++++++++
 Ubuntu/16.04-OpenFOAM/openfoam_hacks/bashrc  | 215 +++++++++++++++++++
 Ubuntu/16.04-OpenFOAM/openfoam_hacks/exec    |   9 +
 Ubuntu/16.04-OpenFOAM/recipe-openfoam.txt    |  48 +++++
 Ubuntu/16.04-OpenFOAM/singularity_image.tar  | Bin 0 -> 40960 bytes
 install/{CENTOS74 => CENTOS7}                |   0
 install/{CENTOS74GPU => CENTOS7GPU}          |   0
 install/{CENTOS74MIC => CENTOS7MIC}          |   0
 install/install.sh                           |  10 +-
 15 files changed, 986 insertions(+), 5 deletions(-)
 create mode 100644 Ubuntu/16.04-OpenFOAM/README
 create mode 100644 Ubuntu/16.04-OpenFOAM/Ubuntu-16.04-Bootstrap
 create mode 100755 Ubuntu/16.04-OpenFOAM/app/check
 create mode 100755 Ubuntu/16.04-OpenFOAM/app/monitor
 create mode 100755 Ubuntu/16.04-OpenFOAM/app/startup
 create mode 100644 Ubuntu/16.04-OpenFOAM/hpc_image
 create mode 100755 Ubuntu/16.04-OpenFOAM/openfoam_hacks/aliases
 create mode 100755 Ubuntu/16.04-OpenFOAM/openfoam_hacks/bashrc
 create mode 100755 Ubuntu/16.04-OpenFOAM/openfoam_hacks/exec
 create mode 100644 Ubuntu/16.04-OpenFOAM/recipe-openfoam.txt
 create mode 100644 Ubuntu/16.04-OpenFOAM/singularity_image.tar
 rename install/{CENTOS74 => CENTOS7} (100%)
 rename install/{CENTOS74GPU => CENTOS7GPU} (100%)
 rename install/{CENTOS74MIC => CENTOS7MIC} (100%)

diff --git a/Ubuntu/16.04-OpenFOAM/README b/Ubuntu/16.04-OpenFOAM/README
new file mode 100644
index 0000000..b60452a
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/README
@@ -0,0 +1,74 @@
+A few instruction for building an openfoam image to be used on the CFG platform.
+Openfoam in this image can be used directly, by calling the solver, or through a python procedure useful to monitor the calculation on the CFG platform.
+
+Usually I build a sandbox writable image. When it's ok, I build a read-only image to be uploaded on CFG platform.
+
+openfoamdev/ is the sandbox image, to be used for development and debug
+openfoam.simg is a non-writable image deployable on CFG platform
+
+
+BUILDING OF THE IMAGE
+
+To build the writable image, use the recipe-openfoam.txt recipe as follows
+
+   sudo singularity build -s openfoamdev recipe-openfoam.txt
+   
+Files in folders app and openfoam_hacks are needed with this recipe.
+In the app folders there are files needed for checking and monitoring the calculation.
+They can be modified or not added.
+Files in openfoam_hacks are needed to allows openfoam to run in the container.
+
+
+TESTING OF THE IMAGE
+
+To test the container, the two mountpoints requested by the CGF platform are to be creted.
+Of course they can reside in a folder different than ~.
+
+  mkdir ~/scratch
+  mkdir ~/service
+  
+- Serial test (direct call of the solver). Specify the solver and the case folder (-case option)
+
+  singularity -B ~/service:service ~/scratch:/scratch exec --cleanenv openfoamdev ###SOLVER###
+
+- Parallel test (direct call of the solver). Specify the solver, the case folder (-case option) and the number of processors.
+Of course, modify the system/decomposeParDict file accordingly.
+
+  singularity -B ~/service:service ~/scratch:/scratch exec --cleanenv openfoamdev decomposePar
+  mpirun -np ###PROCS#### singularity -B ~/service:service ~/scratch:/scratch exec --cleanenv openfoamdev ###SOLVER### -parallel
+
+NOTE When executing directly a solver in parallel mode, this can be a truly MPI calculation.
+     Instead, when testing the python procedure, only processors on one single node can be used.
+
+- Python procedure test.
+When using the python procedure, the solver, the case folder and the number of processors are specified on command line.
+The decomposePar utility is executed, then the solver is started through an mpirun command and the the check utility is started.
+As stated before, in this way only processors on a single node can be used. This will hopefully change in future.
+A webpage is displayed in "~/service/status.html". The abort button can't work locally, but only on the CFG platform.
+Anyway, the interrupt procedure is implemented. See /app/notification file.
+
+  singularity -B ~/service:service ~/scratch:/scratch --cleanenv exec openfoamdev /app/startup -s SOLVER -f FOLDER -p NUMBER_OF_PROCESSORS
+
+The /app/check file is to be modified in order to display the desided webpage.
+The parsing procedure is taylored for interFoam solver and can fail when parsing output of different solvers, so please modify it and test it locally.
+
+
+DEPLOYMENT ON CFG PLATFORM
+
+To build to a non writable image:
+   sudo singularity build openfoam.simg recipe-openfoam.txt
+
+The hpc_image script can be used to upload and register the new image on the CFG platform, 
+or to unregister and delete. Specify username, password and project in the script before using it.
+To upload and register 
+  
+  ./hpc_image upload openfoam.simg
+
+To unregister and delete
+
+  ./hpc_image delete openfoam.simg
+
+
+
+
+
diff --git a/Ubuntu/16.04-OpenFOAM/Ubuntu-16.04-Bootstrap b/Ubuntu/16.04-OpenFOAM/Ubuntu-16.04-Bootstrap
new file mode 100644
index 0000000..6224f5d
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/Ubuntu-16.04-Bootstrap
@@ -0,0 +1,93 @@
+Bootstrap: docker
+From: ubuntu:16.04
+
+%post
+apt -y update && apt -y upgrade
+apt -y install python curl wget gcc g++ make vim mc libncurses-dev rsync tclsh lua5.2 liblua5.2 gfortran ssh
+
+curl -s -O https://bootstrap.pypa.io/get-pip.py
+python get-pip.py
+pip install setuptools python-graph-core python-graph-dot tabulate numpy scipy pandas wheel --upgrade
+
+wget https://github.com/luarocks/luarocks/archive/v2.4.2.tar.gz
+tar xvf v2.4.2.tar.gz
+cd luarocks-2.4.2
+./configure
+make build
+make install
+mkdir ~/.luarocks
+echo "fs_use_modules = false" > ~/.luarocks/config.lua
+luarocks install luaposix
+luarocks install luafilesystem
+LUAROCKS_PREFIX=/usr/local
+export LUA_PATH="$LUAROCKS_PREFIX/share/lua/5.1/?.lua;$LUAROCKS_PREFIX/share/lua/5.1/?/init.lua;;"
+export LUA_CPATH="$LUAROCKS_PREFIX/lib/lua/5.1/?.so;;"
+
+echo "# !/bin/bash" > /bin/logger
+echo "exit 0" >> /bin/logger
+chmod +x /bin/logger
+
+# Lmod
+wget https://github.com/TACC/Lmod/archive/7.7.7.tar.gz
+tar xvf 7.7.7.tar.gz
+cd Lmod-7.7.7
+./configure --prefix=/opt/apps
+make install
+
+# OpenMPI
+wget https://www.open-mpi.org/software/ompi/v2.1/downloads/openmpi-2.1.1.tar.gz
+tar xvf openmpi-2.1.1.tar.gz && cd openmpi-2.1.1
+./configure --prefix=/usr/local
+make -j 16
+make install
+
+# APPS and SCRATCH
+mkdir /apps
+mkdir /scratch
+
+cd /bin
+rm sh && ln -s /bin/bash sh
+
+
+### folders and files ###
+
+mkdir /service  
+mv /opt/startup /app
+mv /opt/check /app
+mv /opt/monitor /app
+
+### set-up python3 ###  
+
+apt install -y python3-pip python3-mpi4py
+pip3 install numpy matplotlib
+  
+### install openfoam ###
+
+apt install -y software-properties-common wget
+add-apt-repository "http://dl.openfoam.org/ubuntu"
+sh -c "wget -O - http://dl.openfoam.org/gpg.key | apt-key add -"
+apt update
+apt install -y openfoam5
+echo '. /opt/openfoam5/etc/bashrc' >>$SINGULARITY_ENVIRONMENT
+
+### openfoam hacks ###
+
+mv -f /opt/bashrc /opt/openfoam5/etc
+mv -f /opt/aliases /opt/openfoam5/etc/config.sh
+mv -f /opt/exec /.singularity.d/actions
+
+%environment
+source /opt/apps/lmod/7.7.7/init/profile
+clearMT
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
+export -f ml
+export -f module
+export -f clearMT
+
+%files
+app/startup opt/
+app/check   opt/
+app/monitor opt/
+openfoam_hacks/bashrc opt/
+openfoam_hacks/aliases opt/
+openfoam_hacks/exec opt/
diff --git a/Ubuntu/16.04-OpenFOAM/app/check b/Ubuntu/16.04-OpenFOAM/app/check
new file mode 100755
index 0000000..41d9499
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/app/check
@@ -0,0 +1,183 @@
+#!/usr/bin/python3
+
+'''
+   This file parses the solver log file and produces the webpage displayed
+   during the simulation. Also a webpage result is written after the end of the
+   simulation.
+   
+   The webpage displays just the residuals and the abort button. Of course it 
+   can be customized.
+   
+
+
+
+
+'''
+
+import time
+import sys
+from subprocess import check_output
+from os.path import isfile, basename
+from os import getcwd
+from shutil import copy
+import logging
+import base64
+
+import re
+import matplotlib
+matplotlib.use('Agg')
+import matplotlib.pyplot as plt
+
+
+
+PROGRESS_HEAD = '''<html>
+  <head>
+    <title>OPENFOAM job progress</title>
+    <script type="text/javascript">
+      function abort() {
+        notify_running_job("ABORT");
+      }
+    </script>
+  </head>'''
+  
+  
+PROGRESS_BODY='''<body style="margin: 20px; padding: 20px;">
+    <h3 align=center>Simulation {}</h3>
+    <p style="text-align:center;">
+<!--    Images to be embedded encoded in base64-->
+    <img src = "data:image/png;base64,{}" alt ="missing image">
+    </p>
+    <p style="text-align:center;">
+    <input type="button" value="Abort simulation" onclick="abort()">
+    </p>
+  </body>
+</html>
+'''
+
+'''
+  Regular expression needed to parse the log file
+'''
+_rx_dict = {
+    'time': re.compile(r'^Time = (?P<time>(\d+.\d+e[+-]\d+)|(\d+[.\d+]*))'),
+    'timestep': re.compile(r'^deltaT = (?P<timestep>(\d+.\d+e[+-]\d+)|(\d+[.\d+]*))'),
+    'residual': re.compile(r'''
+        ^\w+: \s+                                          # solver name
+        Solving \s for \s (?P<quantity>\w+(.\w+)*), \s     # field
+        Initial \s residual \s = \s                         
+        (?P<residual>(-?\d+.\d+e[+-]\d+)|(-?\d+[.\d+]*))   # residual value
+        ''',
+        re.VERBOSE),
+    'clock': re.compile(r'ExecutionTime = (?P<clock>\d+.\d+) s')
+    }
+    
+def _parse_line(line):
+    '''
+      Check if the line matches one of the regex in _rx_dict
+    '''
+    for key, rx in _rx_dict.items():
+        match = rx.search(line)
+        if match:
+            return key, match       
+    return None, None
+
+def give_nth_word(line,n):
+    try:
+        out = line.split()[n-1].strip(",")
+    except IndexError:
+        return ""
+    return out
+
+def parse_logfile(logfilename,data,offset=0):
+    '''
+      Parses the file LOGFILENAME starting from OFFSET line, putting requested 
+      data in list DATA and updating the OFFSET variable.
+      It is executed every 3 seconds (see main function).
+      Residuals, timestep, simulation time and clock time are parsed and 
+      appended to data, which is a list of list.
+    '''
+    time_data = {}
+    content = check_output(["cat",logfilename])
+    lines = content.decode("utf-8").splitlines(True)
+    for index,line in enumerate(lines):
+        if index > offset:
+            key, match = _parse_line(line)
+            if key == 'time':
+                time_data['time'] = float(match.group('time'))
+            if key == 'residual':
+                time_data[match.group('quantity')] = float(match.group('residual'))
+            if key == 'timestep':
+                time_data['timestep'] = float(match.group('timestep'))
+            if key == 'clock':
+                time_data['clock'] = float(match.group('clock'))
+                offset = index
+                data[0].append(time_data['time'])
+                data[1].append(time_data['timestep'])
+                data[2].append(time_data['Ux'])
+                data[3].append(time_data['Uy'])
+                data[4].append(time_data['Uz'])
+                data[5].append(time_data['p_rgh'])
+                data[6].append(time_data['clock'])
+    return offset
+
+def check_if_finished(logfilename):
+    with open(logfilename,'r') as logfile:
+        for line in logfile:
+            pass
+    if give_nth_word(line,1) == "Finalising":
+        return True
+    else:
+        return False
+
+def plot_images(data):
+    '''
+      Plot residual data and save it in a png file. This file will be embedded
+      in the webpage.
+    '''
+    try:
+        fig = plt.figure()
+        ax1 = fig.add_subplot(1,1,1)
+        ax1.set_xlabel('Time [s]')
+        ax1.set_ylabel('Residuals')
+        ax1.semilogy(data[0],data[2], label='ux')
+        ax1.semilogy(data[0],data[3], label='uy')
+        ax1.semilogy(data[0],data[4], label='uz')
+        ax1.semilogy(data[0],data[5], label='p')
+        leg = ax1.legend()
+        plt.savefig('/service/output.png')
+        plt.close()
+    except UserWarning:
+        return
+    return
+
+def main():
+    '''
+      Main function.
+      Every 3 seconds, che logfile is parsed, it is checked if the simulation is
+      finished, then the status webpage is updated.
+      In the end, the same webpage is written in the result file.
+    
+    '''
+    folder = sys.argv[1]
+    casename = basename((getcwd()))
+    logfilename = folder + '/' + casename + '.log'
+    statusfilename = '/service/status.html'
+    resultfilename = '/service/result.txt'
+    time.sleep(5)
+    finished = False
+    log_offset = 0
+    output_data = [[],[],[],[],[],[],[]]
+###    time, timestep, ux, uy, uz, pres, clock
+    while not finished:
+        time.sleep(3)
+        log_offset = parse_logfile(logfilename,output_data,log_offset)
+        finished = check_if_finished(logfilename)
+        plot_images(output_data)
+        with open('/service/output.png','rb') as imgfile:
+            b64image = base64.b64encode(imgfile.read()).decode()
+        with open(statusfilename,'w') as statfile:
+            statfile.write(str(PROGRESS_HEAD + PROGRESS_BODY.format(casename,b64image)))
+    with open(resultfilename,'w') as resfile:
+        resfile.write(str(PROGRESS_HEAD + PROGRESS_BODY.format(casename,b64image)))
+
+if __name__ == "__main__":
+    main()
diff --git a/Ubuntu/16.04-OpenFOAM/app/monitor b/Ubuntu/16.04-OpenFOAM/app/monitor
new file mode 100755
index 0000000..92943a2
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/app/monitor
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+
+
+'''
+  This file checks if something is written in the notification file
+  If the message ABORT is written, the controlDict file is modified in order to
+  stop the simulation.
+  Other answers to other command could be implemented, if needed.
+
+'''
+
+
+import os
+import sys
+import logging
+import time
+from subprocess import check_output
+
+logging.basicConfig(level=logging.DEBUG)
+logger = logging.getLogger("Notifications monitor")
+
+def modify_dict(dictionary,keyword,value):
+  spaces = " " * (16-len(keyword))
+  try:
+    with open(dictionary,"r") as old_filedict:
+      with open(dictionary+"new","w") as new_filedict:
+        for line in old_filedict:
+          keys = line.split()
+          if (len(keys) > 1) and not "//" in keys[0]:
+            if keyword == keys[0]:
+              line = keyword + spaces + str(value) + ";\n"
+          print(line,end='',file=new_filedict) 
+    os.remove(dictionary)
+    os.rename(dictionary+"new",dictionary)
+  except FileNotFoundError:
+    print("File " + dictionary + " not found.\n")
+    exit()
+  return 
+
+def main():
+
+    logger.info("Notification monitor started")
+    folder = sys.argv[1]
+    dictFile = folder + '/system/controlDict'
+    cached_stamp = 0
+
+    while True:
+        if os.path.exists('/service/notifications.txt'):
+            stamp = os.stat('/service/notifications.txt').st_mtime
+            if stamp != cached_stamp:
+                logger.info("New notifications received")
+                cached_stamp = stamp
+                command = check_output(["tail","-1","/service/notifications.txt"]).strip().decode()
+                if command == "ABORT":
+                    logger.info("Received abort command")
+                    modify_dict(dictFile,"stopAt","writeNow")
+                else:
+                    logger.info("Ignoring unknown command {}".format(command))
+        time.sleep(1)
+
+if __name__ == "__main__":
+    main()
diff --git a/Ubuntu/16.04-OpenFOAM/app/startup b/Ubuntu/16.04-OpenFOAM/app/startup
new file mode 100755
index 0000000..0c2a461
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/app/startup
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+
+
+'''
+  This is the principal file.
+  It parses the command line, in which requested openFoam solver, case folder 
+  and number of processors are specified.
+  It decomposes the case, calls the check and monitor scripts, then calls 
+  the solver with the correct parameters and, finally, reconstructs the case.
+  
+  cmdline syntax:
+  
+    startup -s SOLVER [-f FOLDER] -p PROCESSORS
+    
+  The execution is parallel, but since mpi is called inside the container, 
+  processes are spawn only on one node, so there can be at maximum 16 processes
+  on Anselm ann 24 on Salomon.
+ 
+  A truly parallel execution would probably require an mpi4py procedure.
+  
+  (Leonardo) It seems that subprocess commands don't work in conjunction with 
+             an mpi4py script. I have no idea on how to launch an MPI
+             openfoam solver from inside a MPI python script.
+
+'''
+
+from subprocess import Popen, run, DEVNULL
+from os import getcwd, chdir, rename, remove
+from os.path import basename, abspath
+import logging
+import argparse
+
+logging.basicConfig(level=logging.DEBUG)
+logger = logging.getLogger("Openfoam image logger")
+
+def command_line():
+    '''
+      Define and parses the command line arguments.
+      
+    '''
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-solver',action='store',dest='solver')
+    parser.add_argument('-folder', action='store', dest='folder')
+    parser.add_argument('-processor',action='store', type=int, dest='np')
+    args = vars(parser.parse_args())
+    solver = args['solver']
+    folder = args['folder']
+    np = args['np']
+    return solver,folder,np
+    
+def modify_dict(dictionary,keyword,value):
+  '''
+   Modify the VALUE of the desired KEYWORD in the desider DICTIONARY
+  
+  '''    
+  spaces = " " * (16-len(keyword))
+  try:
+    with open(dictionary,"r") as old_filedict:
+      with open(dictionary+"new","w") as new_filedict:
+        for line in old_filedict:
+          keys = line.split()
+          if (len(keys) > 1) and not "//" in keys[0]:
+            if keyword == keys[0]:
+              line = keyword + spaces + str(value) + ";\n"
+          print(line,end='',file=new_filedict) 
+    remove(dictionary)
+    rename(dictionary+"new",dictionary)
+  except FileNotFoundError:
+    print("File " + dictionary + " not found.\n")
+    exit()
+  return 
+
+def main():
+    solver,folder,np = command_line()
+    # folder check
+    if folder is not None:
+        try:
+            chdir(folder)
+        except FileNotFoundError:
+            logger.error("Folder {} not found".format(folder))
+            exit()
+    else:
+        folder = getcwd()
+    folder = abspath(getcwd())
+    casename = basename(getcwd())
+    logfilename = folder + '/' + casename + '.log'
+    statusfilename = '/service/status.html'
+
+    with open(logfilename,'w') as logfile:
+        logger.info("Running decomposePar utility")
+        run(["decomposePar","-force"],stdout=logfile)
+        check_proc = Popen(["/app/check",folder])
+        monitor_proc = Popen(["/app/monitor",folder])
+        logger.info("Running {} on {} processors".format(solver,str(np)))
+        run(["mpirun","-np",str(np),solver,"-parallel","-case",folder],stdout=logfile)
+        logger.info("Running reconstructPar")
+        run(["reconstructPar"],stdout=DEVNULL)
+
+    monitor_proc.terminate()
+### in case of abort command, set the controlDict file in the original state
+    ctrlDict = folder + '/system/controlDict'
+    modify_dict(ctrlDict,"stopAt","endTime")
+    logger.info("Calculation ended")
+
+if __name__ == "__main__":
+    main()
diff --git a/Ubuntu/16.04-OpenFOAM/hpc_image b/Ubuntu/16.04-OpenFOAM/hpc_image
new file mode 100644
index 0000000..7072c94
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/hpc_image
@@ -0,0 +1,71 @@
+#!/usr/bin/python3
+
+"""Ugly but working hard-coded test script for the HPC client"""
+""" 
+    For upload and register, hpc_image upload image_name
+    For unregister and delete, hpc_image delete image_name
+    
+    
+    N.B. Images are uploaded in /home/images folder in anselm cluster
+    N.B. Folder /home/images has to exist on the cluster
+    N.B. Username, password and project should be filled with needed values
+
+"""
+
+
+
+import os
+import filecmp
+import json
+
+import clfpy as cf
+import sys
+
+auth_url = "https://api.hetcomp.org/authManager/AuthManager?wsdl"
+hpc_url = "https://api.hetcomp.org/hpc_anselm/Images?wsdl"
+gss_url = "https://api.hetcomp.org/gss-0.1/FileUtilities?wsdl"
+username = ""
+password = ""
+project = ''
+
+
+print("Obtaining session token ...")
+auth = cf.AuthClient(auth_url)
+session_token = auth.get_session_token(username, project, password)
+
+gss = cf.GssClient(gss_url)
+hpc = cf.HpcImagesClient(hpc_url)
+
+
+
+def upload():
+    print("Uploading and registering a new image")
+    image_filepath = sys.argv[2]
+    gss_ID = "it4i_anselm://home/images/" + sys.argv[2]
+    print(gss.upload(gss_ID, session_token, image_filepath))
+    print(hpc.register_image(session_token, sys.argv[2], gss_ID))
+
+def delete():
+    print("Removing and deleting image")
+    print(hpc.delete_image(session_token, sys.argv[2]))  
+    gss_ID = "it4i_anselm://home/images/" + sys.argv[2]
+    print(gss.delete(gss_ID, session_token))
+
+
+def main():
+    if sys.argv[1] == "upload":
+        upload()
+    elif sys.argv[1] == "delete":
+        delete()
+        
+if __name__ == "__main__":
+    main()
+    
+
+
+
+
+
+
+
+
diff --git a/Ubuntu/16.04-OpenFOAM/openfoam_hacks/aliases b/Ubuntu/16.04-OpenFOAM/openfoam_hacks/aliases
new file mode 100755
index 0000000..098874b
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/openfoam_hacks/aliases
@@ -0,0 +1,120 @@
+#----------------------------------*-sh-*--------------------------------------
+# =========                 |
+# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#  \\    /   O peration     |
+#   \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+#    \\/     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/>.
+#
+# File
+#     etc/config.sh/aliases
+#
+# Description
+#     Aliases for working with OpenFOAM
+#     Sourced from OpenFOAM-<VERSION>/etc/bashrc and/or ~/.bashrc
+#
+#------------------------------------------------------------------------------
+
+# Change compiled version aliases
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+alias wmSet='. $WM_PROJECT_DIR/etc/bashrc'
+alias wm64='wmSet WM_ARCH_OPTION=64'
+alias wm32='wmSet WM_ARCH_OPTION=32'
+alias wmSP='wmSet WM_PRECISION_OPTION=SP'
+alias wmDP='wmSet WM_PRECISION_OPTION=DP'
+
+# Clear env
+alias wmUnset='. $WM_PROJECT_DIR/etc/config.sh/unset'
+
+# Toggle wmakeScheduler on/off
+#  - also need to set WM_HOSTS
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+alias wmSchedOn='export WM_SCHEDULER=$WM_PROJECT_DIR/wmake/wmakeScheduler'
+alias wmSchedOff='unset WM_SCHEDULER'
+
+# Change directory aliases
+# ~~~~~~~~~~~~~~~~~~~~~~~~
+alias foam='cd $WM_PROJECT_DIR'
+
+if [ -n "$WM_PROJECT_SITE" ]
+then
+    alias foamSite='cd $WM_PROJECT_SITE'
+else
+    alias foamSite='cd $WM_PROJECT_INST_DIR/site'
+fi
+
+alias src='cd $FOAM_SRC'
+alias lib='cd $FOAM_LIBBIN'
+alias app='cd $FOAM_APP'
+alias sol='cd $FOAM_SOLVERS'
+alias util='cd $FOAM_UTILITIES'
+alias tut='cd $FOAM_TUTORIALS'
+alias run='cd $FOAM_RUN'
+
+
+# Refresh the environment
+# ~~~~~~~~~~~~~~~~~~~~~~~
+
+
+
+# For backward-compatibility unalias wmRefresh if it is defined as an alias
+[ "$(type -t wmRefresh)" = "alias" ] && unalias wmRefresh || unset wmRefresh
+#if declare -f wmRefresh > /dev/null
+#then
+#    unset wmRefresh
+#else
+#    unalias wmRefresh 2> /dev/null
+#fi
+
+wmRefresh()
+{
+    wmProjectDir=$WM_PROJECT_DIR
+    foamSettings=$FOAM_SETTINGS
+    wmUnset
+    . $wmProjectDir/etc/bashrc $foamSettings
+}
+
+
+# Change OpenFOAM version
+# ~~~~~~~~~~~~~~~~~~~~~~~
+unset foamVersion
+foamVersion()
+{
+    if [ "$1" ]; then
+        foamInstDir=$FOAM_INST_DIR
+        wmUnset
+        . $foamInstDir/OpenFOAM-$1/etc/bashrc
+        foam
+        echo "Changed to OpenFOAM-$1" 1>&2
+    else
+        echo "OpenFOAM-$WM_PROJECT_VERSION" 1>&2
+    fi
+}
+
+
+# Change ParaView version
+# ~~~~~~~~~~~~~~~~~~~~~~~
+unset foamPV
+foamPV()
+{
+    . $WM_PROJECT_DIR/etc/config.sh/paraview ParaView_VERSION=$1
+    echo "paraview-$ParaView_VERSION  (major: $ParaView_MAJOR)" 1>&2
+}
+
+
+#------------------------------------------------------------------------------
diff --git a/Ubuntu/16.04-OpenFOAM/openfoam_hacks/bashrc b/Ubuntu/16.04-OpenFOAM/openfoam_hacks/bashrc
new file mode 100755
index 0000000..d69506a
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/openfoam_hacks/bashrc
@@ -0,0 +1,215 @@
+#----------------------------------*-sh-*--------------------------------------
+# =========                 |
+# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#  \\    /   O peration     |
+#   \\  /    A nd           | Copyright (C) 2011-2017 OpenFOAM Foundation
+#    \\/     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/>.
+#
+# File
+#     etc/bashrc
+#
+# Description
+#     Startup file for OpenFOAM
+#     Sourced from ~/.profile or ~/.bashrc
+#     Should be usable by any POSIX-compliant shell (eg, ksh)
+#
+#------------------------------------------------------------------------------
+
+export WM_PROJECT=OpenFOAM
+export WM_PROJECT_VERSION=5.0
+
+################################################################################
+# USER EDITABLE PART: Changes made here may be lost with the next upgrade
+#
+# FOAM_INST_DIR is the location of the OpenFOAM installation which defaults to
+# the directory containing this file if sourced by a bash shell.
+#
+# Please set to the appropriate path if the default is not correct.
+#
+[ "$BASH" -o "$ZSH_NAME" ]
+#export FOAM_INST_DIR=$(cd $(dirname ${BASH_SOURCE:-$0})/../.. && pwd -P) || \
+export FOAM_INST_DIR=/opt
+# export FOAM_INST_DIR=~$WM_PROJECT
+# export FOAM_INST_DIR=/opt/$WM_PROJECT
+# export FOAM_INST_DIR=/usr/local/$WM_PROJECT
+#
+# END OF (NORMAL) USER EDITABLE PART
+################################################################################
+
+# The default environment variables below can be overridden in a prefs.sh file
+# located in ~/.OpenFOAM/$WM_PROJECT_VERSION, ~/.OpenFOAM,
+# $FOAM_INST_DIR/site/$WM_PROJECT_VERSION or $FOAM_INST_DIR/site
+
+#- Compiler location:
+#    WM_COMPILER_TYPE= system | ThirdParty (OpenFOAM)
+export WM_COMPILER_TYPE=system
+
+#- Compiler:
+#    WM_COMPILER = Gcc | Gcc48 ... Gcc62 | Clang | Icc
+export WM_COMPILER=Gcc
+unset WM_COMPILER_ARCH WM_COMPILER_LIB_ARCH
+
+#- Memory addressing:
+#    On a 64bit OS this can be 32bit or 64bit
+#    On a 32bit OS addressing is 32bit and this option is not used
+#    WM_ARCH_OPTION = 32 | 64
+export WM_ARCH_OPTION=64
+
+#- Precision:
+#    WM_PRECISION_OPTION = DP | SP
+export WM_PRECISION_OPTION=DP
+
+#- Label size:
+#    WM_LABEL_SIZE = 32 | 64
+export WM_LABEL_SIZE=32
+
+#- Optimised, debug, profiling:
+#    WM_COMPILE_OPTION = Opt | Debug | Prof
+export WM_COMPILE_OPTION=Opt
+
+#- MPI implementation:
+#    WM_MPLIB = SYSTEMOPENMPI | OPENMPI | SYSTEMMPI | MPICH | MPICH-GM | HPMPI
+#               | MPI | FJMPI | QSMPI | SGIMPI | INTELMPI
+export WM_MPLIB=SYSTEMOPENMPI
+
+#- Operating System:
+#    WM_OSTYPE = POSIX | ???
+export WM_OSTYPE=POSIX
+
+#- Floating-point signal handling:
+#    set or unset
+export FOAM_SIGFPE=
+
+#- memory initialisation:
+#    set or unset
+#export FOAM_SETNAN=
+
+################################################################################
+
+# The old dirs to be cleaned from the environment variables
+foamOldDirs="$WM_PROJECT_DIR $WM_THIRD_PARTY_DIR \
+    $HOME/$WM_PROJECT/$USER $FOAM_USER_APPBIN $FOAM_USER_LIBBIN \
+    $WM_PROJECT_SITE $FOAM_SITE_APPBIN $FOAM_SITE_LIBBIN"
+
+# Location of installation
+# ~~~~~~~~~~~~~~~~~~~~~~~~
+export WM_PROJECT_INST_DIR=$FOAM_INST_DIR
+export WM_PROJECT_DIR=$WM_PROJECT_INST_DIR/openfoam5
+
+# Location of third-party software
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+export WM_THIRD_PARTY_DIR=$WM_PROJECT_INST_DIR/ThirdParty-$WM_PROJECT_VERSION
+
+# Location of site-specific templates etc
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# unset is equivalent to $WM_PROJECT_INST_DIR/site
+if [ -d "$WM_PROJECT_SITE" ]
+then
+    export WM_PROJECT_SITE
+else
+    unset WM_PROJECT_SITE
+fi
+
+# Location of user files
+# ~~~~~~~~~~~~~~~~~~~~~~
+export WM_PROJECT_USER_DIR=$HOME/$WM_PROJECT/$USER-$WM_PROJECT_VERSION
+
+# Source initialization functions
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+. $WM_PROJECT_DIR/etc/config.sh/functions
+
+# Add in preset user or site preferences:
+_foamSource `$WM_PROJECT_DIR/bin/foamEtcFile prefs.sh`
+
+# Evaluate command-line parameters and record settings for later
+# these can be used to set/unset values, or specify alternative pref files
+export FOAM_SETTINGS="$@"
+
+# _foamEval $@
+
+# Clean standard environment variables (PATH, LD_LIBRARY_PATH, MANPATH)
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+foamClean=$WM_PROJECT_DIR/bin/foamCleanPath
+
+#- Clean PATH
+cleaned=`$foamClean "$PATH" "$foamOldDirs"` && PATH="$cleaned"
+
+#- Clean LD_LIBRARY_PATH
+cleaned=`$foamClean "$LD_LIBRARY_PATH" "$foamOldDirs"` \
+    && LD_LIBRARY_PATH="$cleaned"
+
+#- Clean MANPATH
+cleaned=`$foamClean "$MANPATH" "$foamOldDirs"` && MANPATH="$cleaned"
+
+export PATH LD_LIBRARY_PATH MANPATH
+
+# Source project setup files
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~
+_foamSource $WM_PROJECT_DIR/etc/config.sh/settings
+_foamSource $WM_PROJECT_DIR/etc/config.sh/aliases
+
+
+# Source user setup files for optional packages
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+_foamSource `$WM_PROJECT_DIR/bin/foamEtcFile config.sh/mpi`
+_foamSource `$WM_PROJECT_DIR/bin/foamEtcFile config.sh/paraview`
+_foamSource `$WM_PROJECT_DIR/bin/foamEtcFile config.sh/ensight`
+_foamSource `$WM_PROJECT_DIR/bin/foamEtcFile config.sh/gperftools`
+
+if [ ! -z "$FOAMY_HEX_MESH" ]
+then
+    _foamSource `$WM_PROJECT_DIR/bin/foamEtcFile config.sh/CGAL`
+fi
+
+
+# Clean environment paths again. Only remove duplicates
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#- Clean PATH
+cleaned=`$foamClean "$PATH"` && PATH="$cleaned"
+
+#- Clean LD_LIBRARY_PATH
+cleaned=`$foamClean "$LD_LIBRARY_PATH"` && LD_LIBRARY_PATH="$cleaned"
+
+#- Clean MANPATH (trailing ':' to find system pages)
+cleaned=`$foamClean "$MANPATH"`: && MANPATH="$cleaned"
+
+export PATH LD_LIBRARY_PATH MANPATH
+
+#- Clean LD_PRELOAD
+if [ -n "$LD_PRELOAD" ]
+then
+    cleaned=`$foamClean "$LD_PRELOAD"` && LD_PRELOAD="$cleaned"
+    export LD_PRELOAD
+fi
+
+
+# Cleanup environment:
+# ~~~~~~~~~~~~~~~~~~~~
+unset cleaned foamClean foamOldDirs
+
+
+# Unload initialization functions:
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+. $WM_PROJECT_DIR/etc/config.sh/functions
+
+# Source the bash completion file:
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+[ "$BASH" ] && . $WM_PROJECT_DIR/etc/config.sh/bash_completion
+
+#------------------------------------------------------------------------------
diff --git a/Ubuntu/16.04-OpenFOAM/openfoam_hacks/exec b/Ubuntu/16.04-OpenFOAM/openfoam_hacks/exec
new file mode 100755
index 0000000..c24a5c6
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/openfoam_hacks/exec
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+for script in /.singularity.d/env/*.sh; do
+    if [ -f "$script" ]; then
+        . "$script"
+    fi
+done
+
+exec "$@"
diff --git a/Ubuntu/16.04-OpenFOAM/recipe-openfoam.txt b/Ubuntu/16.04-OpenFOAM/recipe-openfoam.txt
new file mode 100644
index 0000000..9e0c90d
--- /dev/null
+++ b/Ubuntu/16.04-OpenFOAM/recipe-openfoam.txt
@@ -0,0 +1,48 @@
+Bootstrap: docker
+From: ubuntu:17.10
+
+%post
+
+  ### folders and files ###
+
+  mkdir /scratch
+  mkdir /service  
+  mkdir /app
+  mv /opt/startup /app
+  mv /opt/check /app
+  mv /opt/monitor /app
+
+  ### update & upgrade ###
+  
+  apt update
+  apt upgrade -y
+  apt install -y nano ssh
+  
+  ### set-up python3 ###  
+
+  apt install -y python3-pip python3-mpi4py
+  pip3 install numpy matplotlib
+  
+  ### install openfoam ###
+
+  apt install -y software-properties-common wget
+  add-apt-repository "http://dl.openfoam.org/ubuntu"
+  sh -c "wget -O - http://dl.openfoam.org/gpg.key | apt-key add -"
+  apt update
+  apt install -y openfoam5
+  echo '. /opt/openfoam5/etc/bashrc' >>$SINGULARITY_ENVIRONMENT
+
+  ### openfoam hacks ###
+
+  mv -f /opt/bashrc /opt/openfoam5/etc
+  mv -f /opt/aliases /opt/openfoam5/etc/config.sh
+  mv -f /opt/exec /.singularity.d/actions
+
+  
+%files
+ app/startup opt/
+ app/check   opt/
+ app/monitor opt/
+ openfoam_hacks/bashrc opt/
+ openfoam_hacks/aliases opt/
+ openfoam_hacks/exec opt/
diff --git a/Ubuntu/16.04-OpenFOAM/singularity_image.tar b/Ubuntu/16.04-OpenFOAM/singularity_image.tar
new file mode 100644
index 0000000000000000000000000000000000000000..c129e103b5e985ad18399a39e24925cb77a1809d
GIT binary patch
literal 40960
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
Bu0a3*

literal 0
HcmV?d00001

diff --git a/install/CENTOS74 b/install/CENTOS7
similarity index 100%
rename from install/CENTOS74
rename to install/CENTOS7
diff --git a/install/CENTOS74GPU b/install/CENTOS7GPU
similarity index 100%
rename from install/CENTOS74GPU
rename to install/CENTOS7GPU
diff --git a/install/CENTOS74MIC b/install/CENTOS7MIC
similarity index 100%
rename from install/CENTOS74MIC
rename to install/CENTOS7MIC
diff --git a/install/install.sh b/install/install.sh
index d056fd2..ceb5281 100755
--- a/install/install.sh
+++ b/install/install.sh
@@ -9,7 +9,7 @@ if [[ $# -eq 0 ]]; then
 	 printf "\e[31m
  ./install.sh\e[0m   ... create and build singularity image
 	-\e[33mi\e[0m OS (\e[33mCentOS\e[0m, \e[33mDebian\e[0m, \e[33mUbuntu\e[0m)
-	-\e[33mv\e[0m Version OS (CentOS \e[33m6.9\e[0m or CentOS \e[33m7.4\e[0m, Debian \e[33m8.0\e[0m, Ubuntu \e[33m16.04\e[0m)
+	-\e[33mv\e[0m Version OS (CentOS \e[33m6.9\e[0m or CentOS \e[33m7.5\e[0m, Debian \e[33m8.0\e[0m, Ubuntu \e[33m16.04\e[0m)
 	-\e[33mo\e[0m OpenMPI module (default is \e[31mOpenMPI/2.1.1-GCC-6.3.0-2.27\e[0m)
 	-\e[33mt\e[0m \e[33mclassic\e[0m, \e[33mGPU\e[0m, \e[33mMIC\e[0m (default is \e[31mclassic\e[0m)
  Example:
@@ -147,9 +147,9 @@ else
 fi
 
 if [ -z "$VERSION" ]; then
-   echo -e "Please, specify OS version (-v CentOS \e[31m6.9\e[0m or CentOS \e[31m7.4\e[0m, Debian \e[31m8.0\e[0m, Ubuntu \e[31m16.04\e[0m)"
+   echo -e "Please, specify OS version (-v CentOS \e[31m6.9\e[0m or CentOS \e[31m7.5\e[0m, Debian \e[31m8.0\e[0m, Ubuntu \e[31m16.04\e[0m)"
    exit 1
-elif ([ "$IMAGE" == "CentOS" ] && [ "$VERSION" == "6.9" ]) || ([ "$IMAGE" == "CentOS" ] && [ "$VERSION" == "7.4" ]) || ([ "$IMAGE" == "Debian" ] && [ "$VERSION" == "8.0" ]) || ([ "$IMAGE" == "Ubuntu" ] && [ "$VERSION" == "16.04" ]); then
+elif ([ "$IMAGE" == "CentOS" ] && [ "$VERSION" == "6.9" ]) || ([ "$IMAGE" == "CentOS" ] && [ "$VERSION" == "7.5" ]) || ([ "$IMAGE" == "Debian" ] && [ "$VERSION" == "8.0" ]) || ([ "$IMAGE" == "Ubuntu" ] && [ "$VERSION" == "16.04" ]); then
    echo -e "Version ................................ [\e[32mOK\e[0m]"
 else
    echo -e "Version ................................ [\e[31mERROR\e[0m]"
@@ -196,9 +196,9 @@ if [ "$IMAGE" == "CentOS" ]; then
       fi
    else
      if [ "$TYPE" == "classic" ]; then
-        BOOTSTRAP="CENTOS74"
+        BOOTSTRAP="CENTOS7"
      else
-        BOOTSTRAP="CENTOS74$TYPE"
+        BOOTSTRAP="CENTOS7$TYPE"
      fi
    fi
 elif [ "$IMAGE" == "Debian" ]; then
-- 
GitLab