runBenchmark.py 16.2 KB
Newer Older
Gregor Olenik's avatar
Gregor Olenik committed
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
"""
    run ogl benchmarks

    Usage:
        runBenchmark.py [options]

    Options:
        -h --help           Show this screen
        -v --version        Print version and exit
        --folder=<folder>   Target folder  [default: Test].
        --report=<filename> Target file to store stats [default: report.csv].
greole's avatar
Ir (#1)    
greole committed
13
14
15
16
        --of                Generate default of cases [default: False].
        --ref               Generate ref cases [default: False].
        --cuda              Generate cuda cases [default: False].
        --omp               Generate omp cases [default: False].
17
        --omp_max_threads=<n>  Set the number of omp threads [default: 1].
greole's avatar
Ir (#1)    
greole committed
18
        --clean             Remove existing cases [default: False].
19
20
21
22
        --cg                Use CG matrix solver [default: False].
        --ir                Use Ginkgos IR matrix solver [default: False].
        --bicgstab          Use BiCGStab matrix solver [default: False].
        --smooth            Use OpenFOAMs smooth solver [default: False].
23
        --mpi_max_procs=<n>  Set the number of mpi processes [default: 1].
greole's avatar
Ir (#1)    
greole committed
24
25
        --small-cases       Include small cases [default: False].
        --large-cases       Include large cases [default: False].
26
        --very-large-cases  Include large cases [default: False].
greole's avatar
Ir (#1)    
greole committed
27
28
        --min_runs=<n>      Number of applications runs [default: 5]
        --run_time=<s>      Time to applications runs [default: 60]
Gregor Olenik's avatar
Gregor Olenik committed
29
30
31
32
33
34
35
"""
from docopt import docopt
from subprocess import check_output
from pathlib import Path
import os
import shutil
import datetime
greole's avatar
Ir (#1)    
greole committed
36
from itertools import product
Gregor Olenik's avatar
Gregor Olenik committed
37
38


greole's avatar
Ir (#1)    
greole committed
39
class Results:
Gregor Olenik's avatar
Gregor Olenik committed
40
41
42
    def __init__(self, fn):
        self.fn = Path(fn)
        self.columns = [
greole's avatar
Ir (#1)    
greole committed
43
44
45
46
47
            "domain",
            "executor",
            "solver",
            "number_of_iterations",
            "resolution",
48
            "processes",
greole's avatar
Ir (#1)    
greole committed
49
            "run_time",
50
            "success",
Gregor Olenik's avatar
Gregor Olenik committed
51
52
        ]
        self.current_col_vals = []
greole's avatar
Ir (#1)    
greole committed
53
54
        self.report_handle = open(self.fn, "a+", 1)
        self.report_handle.write(",".join(self.columns) + "\n")
Gregor Olenik's avatar
Gregor Olenik committed
55

56
57
58
    def set_case(
        self, domain, executor, solver, number_of_iterations, resolution, processes
    ):
Gregor Olenik's avatar
Gregor Olenik committed
59
        self.current_col_vals = [
greole's avatar
Ir (#1)    
greole committed
60
61
62
63
64
            domain,
            executor,
            solver,
            number_of_iterations,
            resolution,
65
            processes,
Gregor Olenik's avatar
Gregor Olenik committed
66
67
        ]

68
69
    def add(self, run, success):
        outp = self.current_col_vals + [run, success]
Gregor Olenik's avatar
Gregor Olenik committed
70
71
72
73
74
75
76
77
78
79
        outps = ",".join(map(str, outp))
        print(outps)
        self.report_handle.write(outps + "\n")

    def close_file(self):
        close(self.report_handle)


def sed(fn, in_reg_exp, out_reg_exp, inline=True):
    """ wrapper around sed """
greole's avatar
Ir (#1)    
greole committed
80
    ret = check_output(["sed", "-i", "s/" + in_reg_exp + "/" + out_reg_exp + "/g", fn])
Gregor Olenik's avatar
Gregor Olenik committed
81
82


greole's avatar
Ir (#1)    
greole committed
83
def clean_block_from_file(fn, block_starts, block_end, replace):
Gregor Olenik's avatar
Gregor Olenik committed
84
85
86
87
88
    with open(fn, "r") as f:
        lines = f.readlines()
    with open(fn, "w") as f:
        skip = False
        for line in lines:
89
            is_start = [block_start in line for block_start in block_starts]
greole's avatar
Ir (#1)    
greole committed
90
            if any(is_start):
Gregor Olenik's avatar
Gregor Olenik committed
91
92
93
94
95
96
97
98
99
100
101
102
103
                skip = True
            if skip == True and block_end in line:
                skip = False
                f.write(replace)
            if not skip:
                f.write(line)


def set_cells(blockMeshDict, old_cells, new_cells):
    """ """
    sed(blockMeshDict, old_cells, new_cells)


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def set_mesh_boundary_type_to_wall(blockMeshDict):
    """ """
    sed(blockMeshDict, "type[  ]*cyclic", "type wall")


def set_p_init_value(p):
    """ """
    sed(p, "type[  ]*cyclic;", "type zeroGradient;")


def set_U_init_value(U):
    """ """
    sed(U, "type[  ]*cyclic;", "type fixedValue;value uniform (0 0 0);")


Gregor Olenik's avatar
Gregor Olenik committed
119
120
121
122
123
124
125
126
127
128
129
130
131
def add_libOGL_so(controlDict):
    with open(controlDict, "a") as ctrlDict_handle:
        ctrlDict_handle.write('libs ("libOGL.so");')


def set_end_time(controlDict, endTime):
    sed(controlDict, "endTime[ ]*[0-9.]*", "endTime {}".format(endTime))


def set_deltaT(controlDict, deltaT):
    sed(controlDict, "deltaT[ ]*[0-9.]*", "deltaT {}".format(deltaT))


132
133
134
135
def set_writeInterval(controlDict):
    sed(controlDict, "writeInterval[ ]*[0-9.]*", "writeInterval 10.0")


Gregor Olenik's avatar
Gregor Olenik committed
136
def clear_solver_settings(fvSolution):
greole's avatar
Ir (#1)    
greole committed
137
    # sed(fvSolution, "p\\n[ ]*[{][^}]*[}]", "p{}")
138
    clean_block_from_file(fvSolution, ["   p\n", '"p.*"'], "  }\n", "p{}\n")
Gregor Olenik's avatar
Gregor Olenik committed
139
140
141
142
143
144
145
146


def ensure_path(path):
    print("creating", path)
    check_output(["mkdir", "-p", path])


class Case:
greole's avatar
Ir (#1)    
greole committed
147
148
149
150
151
152
153
154
155
156
157
158
159
    def __init__(
        self,
        test_base=None,
        solver="CG",
        executor=None,
        base_case=None,
        resolution=32,
        results=None,
        iterations=0,
        is_base_case=False,
        of_tutorial_domain="DNS",
        of_solver="dnsFoam",
        of_tutorial_case="boxTurb16",
160
        preconditioner="none",
161
        number_of_processes=1,
greole's avatar
Ir (#1)    
greole committed
162
163
    ):
        self.variable = None
164
        self.preconditioner = preconditioner
greole's avatar
Ir (#1)    
greole committed
165
        self.is_base_case = is_base_case
Gregor Olenik's avatar
Gregor Olenik committed
166
        self.test_base = test_base
greole's avatar
Ir (#1)    
greole committed
167
        self.of_base_case = "boxTurb16"
Gregor Olenik's avatar
Gregor Olenik committed
168
        self.fields = "p"
greole's avatar
Ir (#1)    
greole committed
169
        self.tolerance = "1e-06"
Gregor Olenik's avatar
Gregor Olenik committed
170
171
172
        self.resolution = resolution
        self.executor = executor
        self.solver = solver
greole's avatar
Ir (#1)    
greole committed
173
174
        self.iterations = iterations
        self.base_case_path_ = base_case
Gregor Olenik's avatar
Gregor Olenik committed
175
        self.results_accumulator = results
greole's avatar
Ir (#1)    
greole committed
176
177
178
179
        self.init_time = 0
        self.of_solver = of_solver
        self.of_tutorial_case = of_tutorial_case
        self.of_tutorial_domain = of_tutorial_domain
180
        self.number_of_processes = number_of_processes
greole's avatar
Ir (#1)    
greole committed
181
182
183
184

    @property
    def system_folder(self):
        return self.path / "system"
Gregor Olenik's avatar
Gregor Olenik committed
185

186
187
188
189
190
191
192
193
194
195
196
197
    @property
    def zero_folder(self):
        return self.path / "0"

    @property
    def init_p(self):
        return self.zero_folder / "p"

    @property
    def init_U(self):
        return self.zero_folder / "U.orig"

greole's avatar
Ir (#1)    
greole committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
    @property
    def controlDict(self):
        return self.system_folder / "controlDict"

    @property
    def blockMeshDict(self):
        return self.system_folder / "blockMeshDict"

    @property
    def fvSolution(self):
        return self.system_folder / "fvSolution"

    def create(self):
        ensure_path(self.parent_path)
        self.copy_base(self.base_case_path, self.parent_path)
        deltaT = 0.1 * 16 / self.resolution
        if self.is_base_case:
            new_cells = "{} {} {}".format(
                self.resolution, self.resolution, self.resolution
            )
            set_cells(self.blockMeshDict, "16 16 16", new_cells)
219
220
221
            set_mesh_boundary_type_to_wall(self.blockMeshDict)
            set_p_init_value(self.init_p)
            set_U_init_value(self.init_U)
greole's avatar
Ir (#1)    
greole committed
222
223
224
            add_libOGL_so(self.controlDict)
            set_end_time(self.controlDict, 10 * deltaT)
            set_deltaT(self.controlDict, deltaT)
225
            set_writeInterval(self.controlDict)
greole's avatar
Ir (#1)    
greole committed
226
227
228
229
230
231
232
233
234
235
236
            clear_solver_settings(self.fvSolution)
            print("Meshing", self.path)
            check_output(["blockMesh"], cwd=self.path)
            return

        self.set_matrix_solver(self.fvSolution)

    @property
    def base_case_path(self):
        if self.is_base_case:
            foam_tutorials = Path(os.environ["FOAM_TUTORIALS"])
237
238
239
240
241
242
            return (
                foam_tutorials
                / self.of_tutorial_domain
                / self.of_solver
                / self.of_tutorial_case
            )
greole's avatar
Ir (#1)    
greole committed
243
244
245
246
247
248
249
        return self.base_case_path_ / self.of_base_case

    @property
    def parent_path(self):
        return (
            Path(self.test_base)
            / Path(self.executor.local_path)
Gregor Olenik's avatar
Gregor Olenik committed
250
            / "{}-{}-{}".format(self.fields, self.solver, self.preconditioner)
greole's avatar
Ir (#1)    
greole committed
251
252
            / str(self.resolution)
        )
Gregor Olenik's avatar
Gregor Olenik committed
253
254
255

    @property
    def path(self):
greole's avatar
Ir (#1)    
greole committed
256
        return self.parent_path / str(self.of_tutorial_case)
Gregor Olenik's avatar
Gregor Olenik committed
257

258
259
    @property
    def log_path(self):
Gregor Olenik's avatar
Gregor Olenik committed
260
        return self.path / "log"
261

Gregor Olenik's avatar
Gregor Olenik committed
262
263
264
265
    def copy_base(self, src, dst):
        print("copying base case", src, dst)
        check_output(["cp", "-r", src, dst])

greole's avatar
Ir (#1)    
greole committed
266
267
268
    def set_matrix_solver(self, fn):
        print("setting solver", fn)
        matrix_solver = self.executor.prefix + self.solver
269
        # fmt: off
greole's avatar
Ir (#1)    
greole committed
270
        solver_str = (
271
            '"p.*"{\\n'
272
273
274
275
276
277
278
279
            + "solver {};\
\\ntolerance {};\
\\nrelTol 0.0;\
\\nsmoother none;\
\\npreconditioner {};\
\\nminIter {};\
\\nmaxIter 10000;\
\\nupdateSysMatrix no;\
280
\\nsort yes;\
281
\\nexecutor {};".format(
greole's avatar
Ir (#1)    
greole committed
282
283
                matrix_solver,
                self.tolerance,
284
                self.preconditioner,
greole's avatar
Ir (#1)    
greole committed
285
286
287
288
                self.iterations,
                self.executor.executor
            )
        )
289
        # fmt: on
Gregor Olenik's avatar
Gregor Olenik committed
290
291
        sed(fn, "p{}", solver_str)

greole's avatar
Ir (#1)    
greole committed
292
293
294
    def run(self, results_accumulator, min_runs, time_runs):
        if self.is_base_case:
            return
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311

        print("start runs")
        for processes in self.executor:
            print("start runs", processes)

            self.executor.prepare_enviroment(processes)

            self.results_accumulator.set_case(
                domain=self.executor.domain,
                executor=self.executor.executor,
                solver=self.solver,
                number_of_iterations=self.iterations,
                resolution=self.resolution,
                processes=processes,
            )
            accumulated_time = 0
            iters = 0
312
            ret = ""
313
314
315
            while accumulated_time < time_runs or iters < min_runs:
                iters += 1
                start = datetime.datetime.now()
316
317
                success = 0
                try:
Gregor Olenik's avatar
Gregor Olenik committed
318
                    ret = check_output([self.of_solver], cwd=self.path, timeout=15 * 60)
319
320
                    success = 1
                except:
321
                    break
322
                end = datetime.datetime.now()
Gregor Olenik's avatar
Gregor Olenik committed
323
                run_time = (end - start).total_seconds()  # - self.init_time
324
                self.results_accumulator.add(run_time, success)
325
326
                accumulated_time += run_time
            self.executor.clean_enviroment()
327
            try:
Gregor Olenik's avatar
Gregor Olenik committed
328
329
330
                with open(
                    self.log_path.with_suffix("." + str(processes)), "a+"
                ) as log_handle:
331
332
333
334
335
                    log_handle.write(ret.decode("utf-8"))
            except Exception as e:
                print(e)
                pass

336
        self.executor.current_num_processes = 1
Gregor Olenik's avatar
Gregor Olenik committed
337
338


339
340
def build_parameter_study(test_path, results, executor, setter, arguments):
    for (e, n) in product(executor, setter):
Gregor Olenik's avatar
Gregor Olenik committed
341
        for s in e.solvers:
342
343
344
345
346
347
            # check if solver supported by executor
            if not getattr(s, e.domain):
                print(s.name, "not supported by", e.domain)
                continue
            path = test_path / e.local_path / str(n.value)
            exist = os.path.isdir(path)
greole's avatar
Ir (#1)    
greole committed
348
            skip = False
349
350
351
352
353
354
355
            clean = arguments["--clean"]
            if exist and clean:
                shutil.rmtree(path)
                skip = False
            if exist and not clean:
                skip = True
            is_base_case = False
Gregor Olenik's avatar
Gregor Olenik committed
356
357
358
            base_case_path = (
                test_path / Path("base") / Path("p-" + s.name) / str(n.value)
            )
greole's avatar
Ir (#1)    
greole committed
359

360
361
362
            if e.domain == "base":
                print("is base case")
                is_base_case = True
greole's avatar
Ir (#1)    
greole committed
363

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
            if not skip:
                case = Case(
                    test_base=test_path,
                    solver=s.name,
                    executor=e,
                    base_case=base_case_path,
                    results=results,
                    is_base_case=is_base_case,
                )
                n.run(case)
                case.create()
                case.run(
                    results, int(arguments["--min_runs"]), int(arguments["--run_time"])
                )
            else:
                print("skipping")


def resolution_study(name, executor, arguments):
greole's avatar
Ir (#1)    
greole committed
383
384
385
386
387
388
389
390

    test_path = Path(arguments["--folder"]) / name

    results = Results(arguments["--report"])

    number_of_cells = []

    if arguments["--small-cases"]:
Gregor Olenik's avatar
Gregor Olenik committed
391
        number_of_cells += [8, 16]
Gregor Olenik's avatar
Gregor Olenik committed
392

greole's avatar
Ir (#1)    
greole committed
393
    if arguments["--large-cases"]:
Gregor Olenik's avatar
Gregor Olenik committed
394
        number_of_cells += [32, 64]
greole's avatar
Ir (#1)    
greole committed
395
396

    if arguments["--very-large-cases"]:
Gregor Olenik's avatar
Gregor Olenik committed
397
        number_of_cells += [128, 256]
Gregor Olenik's avatar
Gregor Olenik committed
398

greole's avatar
Ir (#1)    
greole committed
399
400
401
    n_setters = []
    for n in number_of_cells:
        n_setters.append(ValueSetter("resolution", n))
Gregor Olenik's avatar
Gregor Olenik committed
402

403
    build_parameter_study(test_path, results, executor, n_setters, arguments)
Gregor Olenik's avatar
Gregor Olenik committed
404

greole's avatar
Ir (#1)    
greole committed
405
406
407
408
409
410
411
412
413
414
415
416

class ValueSetter:
    def __init__(self, prop, value):
        self.prop = prop
        self.value = value

    def run(self, case):
        setattr(case, self.prop, self.value)
        setattr(case, "variable", self.value)


class Executor:
417
418
419
420
421
422
423
424
    def __init__(
        self,
        domain,
        solver_prefix,
        executor=None,
        cmd_prefix=None,
        max_number_processes=1,
        prepare_env=None,
425
        solvers=None,
426
    ):
greole's avatar
Ir (#1)    
greole committed
427
428
429
430
        self.domain = domain
        self.prefix = solver_prefix
        self.executor = executor
        self.cmd_prefix = cmd_prefix
431
432
433
        self.current_num_processes = 1
        self.max_number_processes = max_number_processes
        self.enviroment_handler = prepare_env
434
        self.solvers = solvers
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452

    def prepare_enviroment(self, processes):
        self.enviroment_handler.set_up(processes)

    def clean_enviroment(self):
        self.enviroment_handler.clean_up()

    def __iter__(self):
        return self

    def __next__(self):
        next_current_num_processes = 2 * self.current_num_processes
        ret = self.current_num_processes
        if ret <= self.max_number_processes:
            self.current_num_processes = next_current_num_processes
            return ret

        raise StopIteration
greole's avatar
Ir (#1)    
greole committed
453
454
455
456
457
458
459

    @property
    def local_path(self):
        path = self.domain
        if self.executor:
            path += self.executor
        return Path(path)
Gregor Olenik's avatar
Gregor Olenik committed
460
461


462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
class DefaultPrepareEnviroment:
    def __init__(self):
        pass

    def set_up(self, processes):
        pass

    def clean_up(self):
        pass


class PrepareOMPMaxThreads:
    def __init__(self):
        pass

    def set_up(self, processes):
        print(" use ", processes, " threads")
        os.environ["OMP_NUM_THREADS"] = str(processes)

    def clean_up(self):
        pass

484

Gregor Olenik's avatar
Gregor Olenik committed
485
class IR:
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
    def __init__(self):
        self.OF = False
        self.GKO = True
        self.base = True
        self.name = "IR"


class CG:
    def __init__(self):
        self.OF = True
        self.GKO = True
        self.base = True
        self.name = "CG"


class BiCGStab:
    def __init__(self):
        self.OF = True
        self.base = True
        self.GKO = True
        self.name = "BiCGStab"


class smoothSolver:
    def __init__(self):
        self.base = True
512
513
        self.OF = True
        self.GKO = False
514
515
        self.name = "smoothSolver"

516

Gregor Olenik's avatar
Gregor Olenik committed
517
518
if __name__ == "__main__":

greole's avatar
Ir (#1)    
greole committed
519
520
521
    arguments = docopt(__doc__, version="runBench 0.1")
    print(arguments)

522
    solvers = []
Gregor Olenik's avatar
Gregor Olenik committed
523

greole's avatar
Ir (#1)    
greole committed
524
    if arguments["--ir"]:
525
        solvers.append(IR())
Gregor Olenik's avatar
Gregor Olenik committed
526

greole's avatar
Ir (#1)    
greole committed
527
    if arguments["--cg"]:
528
        solvers.append(CG())
greole's avatar
Ir (#1)    
greole committed
529
530

    if arguments["--bicgstab"]:
531
532
533
534
        solvers.append(BiCGStab())

    if arguments["--smooth"]:
        solvers.append(smoothSolver())
greole's avatar
Ir (#1)    
greole committed
535
536

    preconditioner = []
537
    executor = [Executor("base", "", "", solvers=solvers)]
Gregor Olenik's avatar
Gregor Olenik committed
538
539

    if arguments["--cuda"]:
540
541
542
543
544
545
546
        executor.append(
            Executor(
                "GKO",
                "GKO",
                "cuda",
                max_number_processes=1,
                prepare_env=DefaultPrepareEnviroment(),
Gregor Olenik's avatar
Gregor Olenik committed
547
                solvers=solvers,
548
549
            )
        )
Gregor Olenik's avatar
Gregor Olenik committed
550
551

    if arguments["--of"]:
552
553
554
555
556
557
558
        executor.append(
            Executor(
                "OF",
                "P",
                "",
                max_number_processes=1,
                prepare_env=DefaultPrepareEnviroment(),
Gregor Olenik's avatar
Gregor Olenik committed
559
                solvers=solvers,
560
561
            )
        )
Gregor Olenik's avatar
Gregor Olenik committed
562
563

    if arguments["--ref"]:
564
565
566
567
568
569
570
        executor.append(
            Executor(
                "GKO",
                "GKO",
                "ref",
                max_number_processes=1,
                prepare_env=DefaultPrepareEnviroment(),
Gregor Olenik's avatar
Gregor Olenik committed
571
                solvers=solvers,
572
573
            )
        )
Gregor Olenik's avatar
Gregor Olenik committed
574
575

    if arguments["--omp"]:
576
577
578
579
580
581
582
583
584
        max_omp_threads = int(arguments["--omp_max_threads"])
        print("max omp threads ", max_omp_threads)
        executor.append(
            Executor(
                "GKO",
                "GKO",
                "omp",
                prepare_env=PrepareOMPMaxThreads(),
                max_number_processes=max_omp_threads,
Gregor Olenik's avatar
Gregor Olenik committed
585
                solvers=solvers,
586
587
            )
        )
Gregor Olenik's avatar
Gregor Olenik committed
588

589
    resolution_study("number_of_cells", executor, arguments)