Skip to content

Commit 85c8310

Browse files
added script for rapid observation generation
1 parent 2053b2c commit 85c8310

3 files changed

Lines changed: 375 additions & 0 deletions

File tree

Dockerfile-amdados

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Building:
2+
# docker build --tag dev-allscale:compiler-env . -f Dockerfile-amdados
3+
# Removing intermediate images and inactive containers afterwards:
4+
# docker images -q --filter dangling=true | xargs docker rmi
5+
# docker rm $(docker ps -qa --no-trunc --filter "status=exited")
6+
# Extracting for sharing on another machine:
7+
# docker save dev-allscale:compiler-env > amdados-cpu-ubuntu.tar
8+
# Loading on another machine:
9+
# docker load < amdados-cpu-ubuntu.tar
10+
# Running with network, sharing of the directory "work" and
11+
# full access to physical memory:
12+
# docker run -it --volume /home/albert/work:/root/work \
13+
# --cap-add=NET_ADMIN --device=/dev/net/tun \
14+
# --privileged --volume /dev/shm:/dev/shm \
15+
# dev-images:tf-cpu-ubuntu /bin/bash
16+
17+
FROM debian:9.4
18+
19+
LABEL maintainer="Fearghal O'Donncha, feardonn@ie.ibm.com"
20+
21+
ENV LC_ALL=
22+
23+
# System package.
24+
RUN apt-get update && apt-get -y upgrade && \
25+
apt-get install -y --no-install-recommends \
26+
build-essential gdb gfortran cmake make automake \
27+
unzip bzip2 curl wget rsync tmux htop \
28+
libfreetype6-dev libpng12.* libzmq3-dev libjpeg-dev libtiff[0-9]*-dev librsvg2-dev \
29+
pkg-config software-properties-common \
30+
libcurl.*-openssl-dev libpcre++-dev libxml2-dev \
31+
git mc vim nano make cmake gdb openssh-client openssh-server \
32+
binutils binutils-dev sshfs exuberant-ctags valgrind \
33+
libblas-dev liblapack-dev liblapacke-dev \
34+
libarpack2-dev libarpack2++-dev libopenblas-dev \
35+
libatlas-base-dev libsuperlu.*-dev libopenblas-dev \
36+
python3 python3-dev python3-pip \
37+
python3-numpy python3-scipy python3-matplotlib python3-setuptools \
38+
&& \
39+
apt-get clean && apt-get autoremove && \
40+
apt-get update && \
41+
apt-get install -y --no-install-recommends groff && \
42+
rm -rf /var/lib/apt/lists/* \
43+
&& \
44+
ssh-keygen -t rsa -f ${HOME}/.ssh/id_rsa -q -P "" \
45+
&& \
46+
echo '' >> ~/.bashrc && \
47+
echo 'alias nano="nano -i -c --tabsize=4 --tabstospaces "' \
48+
>> ~/.bashrc && \
49+
echo 'alias py3="python3 "' \
50+
>> ~/.bashrc && \
51+
echo 'alias py3d="python3 -m pdb "' \
52+
>> ~/.bashrc && \
53+
echo 'PS1="\[\e[1;32m\]\u\[\e[1;36m\]@\[\e[1;31m\]\h\[\e[1;35m\]:\w\$\[\e[0;30m\] "' \
54+
>> ~/.bashrc
55+
56+
# Working directory.
57+
WORKDIR /root/work
58+
59+
# Execute this command on start-up.
60+
CMD ["/bin/bash"]
61+
62+
63+
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# -----------------------------------------------------------------------------
2+
# Author : Albert Akhriev, albert_akhriev@ie.ibm.com
3+
# Copyright : IBM Research Ireland, 2017-2018
4+
# -----------------------------------------------------------------------------
5+
6+
import sys, traceback, os, re, getopt, math, argparse, subprocess
7+
import numpy as np
8+
from timeit import default_timer as timer
9+
from Configuration import Configuration
10+
from Utility import *
11+
12+
# Full-path name of C++ executable.
13+
AMDADOS_EXE = "build/app/amdados"
14+
15+
16+
def Amdados2D(config_file, demo):
17+
""" Advection-diffusion PDE forward solver.
18+
"""
19+
# Initialize parameters, global indices and output directory.
20+
conf = Configuration(config_file)
21+
conf = InitDependentParams(conf)
22+
glo_idx = GlobalIndices(conf)
23+
if demo: conf.PrintParameters()
24+
MakePath(conf.output_dir + os.sep)
25+
26+
# Load sensor locations previously generated by C++ 'amdados' application:
27+
# './build/app/amdados --scenario sensors'
28+
# If does not exist, the file of sensor locations will be regenerated.
29+
sensors_filename = MakeFileName(conf, "sensors")
30+
if not os.path.exists(sensors_filename):
31+
print("")
32+
print("")
33+
print("WARNING: missed file of sensor locations: " + sensors_filename)
34+
print("generating a new one ...")
35+
assert os.path.exists(AMDADOS_EXE), (
36+
"Application 'amdados' must be built before running this script")
37+
subprocess.Popen([AMDADOS_EXE, "--scenario", "sensors",
38+
"--config", config_file])
39+
os.sync()
40+
print("")
41+
print("")
42+
sensor_idx = LoadSensorLocations(conf)
43+
44+
Nt = round(conf.Nt)
45+
writer = Writer(conf)
46+
for k in range(Nt):
47+
# Write the field entries at sensors into the file of observations from random number
48+
writer.WriteField(sensor_idx, k)
49+
50+
# Run forward simulation and record the "true" solutions into a file.
51+
# with open(MakeFileName(conf, "true_field"), "wb") as fid:
52+
# ForwardSolver(conf, glo_idx, sensor_idx, fid, demo)
53+
54+
55+
###############################################################################
56+
# Initialization.
57+
###############################################################################
58+
59+
def InitDependentParams(conf):
60+
""" Function initializes dependent parameters given
61+
the primary ones specified by user.
62+
"""
63+
# Ensure integer values.
64+
conf.nx = round(conf.num_subdomains_x * conf.subdomain_x)
65+
conf.ny = round(conf.num_subdomains_y * conf.subdomain_y)
66+
conf.integration_nsteps = round(conf.integration_nsteps)
67+
68+
# Diffusion coefficient must be positive float value.
69+
D = float(conf.diffusion_coef)
70+
assert D > 0
71+
72+
# Deduce space discretization steps.
73+
conf.problem_size = int(conf.nx * conf.ny)
74+
dx = float(conf.domain_size_x) / float(conf.nx-1)
75+
dy = float(conf.domain_size_y) / float(conf.ny-1)
76+
assert (dx > 0) and (dy > 0)
77+
conf.dx = dx
78+
conf.dy = dy
79+
80+
# Deduce the optimal time step from the stability criteria.
81+
tiny = np.finfo(float).tiny / np.finfo(float).eps**3
82+
dt_base = float(conf.integration_period) / float(conf.integration_nsteps)
83+
max_vx = float(conf.flow_model_max_vx)
84+
max_vy = float(conf.flow_model_max_vy)
85+
dt = min(dt_base, min( min(dx**2, dy**2)/(2.0*D + tiny),
86+
1.0/(abs(max_vx)/dx + abs(max_vy)/dy + tiny) ))
87+
assert(dt > 0)
88+
conf.dt = dt
89+
conf.Nt = round(math.ceil(float(conf.integration_period) / dt))
90+
91+
# Compute coefficients that will be used in the finite-difference scheme.
92+
conf.rho_x = float(D * dt / dx**2)
93+
conf.rho_y = float(D * dt / dy**2)
94+
95+
conf.v0x = float(2.0 * dx / dt)
96+
conf.v0y = float(2.0 * dy / dt)
97+
return conf
98+
99+
100+
def GlobalIndices(conf):
101+
""" Each nodal point gets a unique global index on the grid.
102+
Function initializes a 2D array of indices:
103+
index of (x,y) = glo_idx(x,y).
104+
"""
105+
glo_idx = np.arange(round(conf.problem_size)).reshape((conf.nx, conf.ny))
106+
return glo_idx
107+
108+
109+
def LoadSensorLocations(conf):
110+
""" Function loads sensor locations, i.e. indices of domain
111+
points occupied by sensors.
112+
"""
113+
print("Loading sensor locations ...")
114+
Nx = conf.nx
115+
Ny = conf.ny
116+
Np = round(math.ceil(Nx * Ny * conf.sensor_fraction))
117+
data = np.loadtxt(MakeFileName(conf, "sensors"), dtype=int)
118+
assert (data is not None) and (data.size > 0), "empty file of sensors"
119+
assert len(data.shape) == 2 and data.shape[1] == 2, "wrong layout"
120+
assert data.shape[0] <= Nx * Ny, "too many records"
121+
assert data.dtype == int, "type mismatch"
122+
assert np.all(data >= 0), "negative index"
123+
assert np.all(data[:,0] < Nx), "x-index is out of bound"
124+
assert np.all(data[:,1] < Ny), "y-index is out of bound"
125+
return data
126+
127+
###############################################################################
128+
# Utilities.
129+
###############################################################################
130+
131+
class Writer:
132+
""" Class for writing simulated solution at sensor locations in a text file.
133+
"""
134+
def __init__(self, conf):
135+
""" Constructor. TODO: could be useful to add a header to file:
136+
"""
137+
# N O T E: np.savetxt() expects (!?) file opened in binary format "wb".
138+
self.fid = open(MakeFileName(conf, "analytic"), "wb")
139+
140+
def __del__(self):
141+
""" This method is not a destructor, but a normal method that is always
142+
called before the garbage collector destroys the object.
143+
https://stackoverflow.com/questions/37852560/is-del-really-a-destructor
144+
"""
145+
if self.fid is not None:
146+
self.fid.flush()
147+
self.fid.close()
148+
self.fid = None
149+
150+
def WriteField(self, sensor_idx, discrete_time):
151+
""" Function appends the new field to the output file.
152+
Only values at sensor locations are written.
153+
column 1: discrete time;
154+
column 2: sensor abscissas (point indices) on the grid;
155+
column 3: sensor ordinates (point indices) on the grid;
156+
column 4: values at sensor locations.
157+
"""
158+
assert isinstance(discrete_time, int) and discrete_time >= 0
159+
assert len(sensor_idx.shape) == 2 and sensor_idx.shape[1] == 2
160+
161+
xind = sensor_idx[:, 0].astype(int)
162+
yind = sensor_idx[:, 1].astype(int)
163+
# For the purpose of performance analysis just set vals as random number
164+
vals = np.random.rand(len(xind)).astype(float)
165+
assert np.isnan(vals).any() == False, "NaN in observations"
166+
vals[np.fabs(vals) <= np.finfo(np.float64).eps]=0
167+
data = np.column_stack((xind, yind, vals))
168+
num = data.shape[0]
169+
np.savetxt(self.fid, np.array([discrete_time, num]), fmt="%d")
170+
np.savetxt(self.fid, data, fmt="%d %d %g");
171+
172+
###############################################################################
173+
# Entry point.
174+
###############################################################################
175+
if __name__ == "__main__":
176+
try:
177+
# CheckPythonVersion()
178+
parser = argparse.ArgumentParser()
179+
parser.add_argument("--config",
180+
type=str, default="amdados.conf",
181+
help="path to configuration file")
182+
parser.add_argument("--demo", nargs="?", const=True,
183+
type=bool, default=False,
184+
help="show live progress in a window")
185+
param = parser.parse_args()
186+
param.config = os.path.expanduser(param.config)
187+
print("Options:")
188+
print("Visualization: " + str(param.demo))
189+
print("Configuration files: " + param.config)
190+
print("")
191+
Amdados2D(param.config, param.demo)
192+
193+
except subprocess.CalledProcessError as error:
194+
traceback.print_exc()
195+
if error.output is not None:
196+
print("ERROR: " + str(error.output))
197+
else:
198+
print("CalledProcessError")
199+
except AssertionError as error:
200+
traceback.print_exc()
201+
print("ERROR: " + str(error.args))
202+
except ValueError as error:
203+
traceback.print_exc()
204+
print("ERROR: " + str(error.args))
205+
except Exception as error:
206+
traceback.print_exc()
207+
print("ERROR: " + str(error.args))
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# -----------------------------------------------------------------------------
2+
# Author : Albert Akhriev, albert_akhriev@ie.ibm.com
3+
# Copyright : IBM Research Ireland, 2017-2018
4+
# -----------------------------------------------------------------------------
5+
6+
""" This script runs several simulations with increasing problem size
7+
utilizing all available CPUs. It saves the execution time of each
8+
simulation in a file that can be used to plot the scalability profile.
9+
Essential parameters, listed in the first lines, include the set of
10+
problem sizes and integration period.
11+
Each simulation is twofold. First, we run the Python forward solver that
12+
generates the ground-truth and observations ("ObservationsGenerator.py").
13+
The Python code itself uses the C++ code running in the special mode for
14+
generating sensor locations (scenario "sensors"). Second, the C++ data
15+
assimilation application is launched (scenario "simulation") with
16+
observations generated by "ObservationsGenerator.py".
17+
The results of all the simulations are accumulated in the output
18+
directory and can be visualized later on by the script "Visualize.py".
19+
The configuration file "amdados.conf" is used in all the simulations with
20+
modification of three parameters: grid sizes (number of subdomains) in both
21+
dimensions and integration time. Other parameters remain intact. It is not
22+
recommended to tweak parameters unless their meaning is absolutely clear.
23+
If you had modified the parameters, please, consider to rerun this script
24+
because the results in the output directory a not valid any longer.
25+
The script was designed to fulfil the formal requirements of the
26+
Allscale project.
27+
"""
28+
print(__doc__)
29+
30+
from timeit import default_timer as timer
31+
import os, cmd
32+
from RandObservationsGenerator import InitDependentParams, Amdados2D
33+
from Utility import *
34+
35+
GridSizes = [(2,2),(4,4)]
36+
# Integration period in seconds.
37+
IntegrationPeriod = 100
38+
39+
# Path to the C++ executable.
40+
AMDADOS_EXE = "build/app/amdados"
41+
42+
43+
if __name__ == "__main__":
44+
try:
45+
# Read configuration file.
46+
conf = Configuration("amdados.conf")
47+
# Create the output directory, if it does not exist.
48+
if not os.path.isdir(conf.output_dir):
49+
os.mkdir(conf.output_dir)
50+
# Check existence of "amdados" application executable.
51+
assert os.path.isfile(AMDADOS_EXE), "amdados executable was not found"
52+
53+
# For all the grid sizes in the list ...
54+
exe_time_profile = np.zeros((len(GridSizes),2))
55+
for grid_no, grid in enumerate(GridSizes):
56+
assert grid[0] >= 2 and grid[1] >= 2, "the minimum grid size is 2x2"
57+
# Modify parameters given the current grid size.
58+
setattr(conf, "num_subdomains_x", int(grid[0]))
59+
setattr(conf, "num_subdomains_y", int(grid[1]))
60+
setattr(conf, "integration_period", int(IntegrationPeriod))
61+
InitDependentParams(conf)
62+
conf.PrintParameters()
63+
config_file = conf.WriteParameterFile("scalability_test.conf")
64+
os.sync()
65+
# Python simulator generates the ground-truth and observations.
66+
Amdados2D(config_file, False)
67+
68+
# Get the starting time.
69+
start_time = timer()
70+
71+
# Run C++ data assimilation application.
72+
print("##################################################")
73+
print("Simulation by 'amdados' application ...")
74+
print("silent if debugging & messaging were disabled")
75+
print("##################################################")
76+
print(AMDADOS_EXE, config_file)
77+
subprocess.call([AMDADOS_EXE, "--scenario", "simulation",
78+
"--config", config_file])
79+
os.sync()
80+
81+
# Get the execution time and corresponding (global) problem size
82+
# and save the current scalability profile into the file.
83+
problem_size = ( conf.num_subdomains_x * conf.subdomain_x *
84+
conf.num_subdomains_y * conf.subdomain_y )
85+
exe_time_profile[grid_no,0] = problem_size
86+
exe_time_profile[grid_no,1] = timer() - start_time
87+
np.savetxt(os.path.join(conf.output_dir, "scalability_performance.txt"),
88+
exe_time_profile)
89+
90+
except subprocess.CalledProcessError as error:
91+
traceback.print_exc()
92+
if error.output is not None:
93+
print("ERROR: " + str(error.output))
94+
else:
95+
print("CalledProcessError")
96+
except AssertionError as error:
97+
traceback.print_exc()
98+
print("ERROR: " + str(error.args))
99+
except ValueError as error:
100+
traceback.print_exc()
101+
print("ERROR: " + str(error.args))
102+
except Exception as error:
103+
traceback.print_exc()
104+
print("ERROR: " + str(error.args))
105+

0 commit comments

Comments
 (0)