exaoutput.py 11.8 KB
Newer Older
1 2 3

import os
from pathlib import Path
4
from tempfile import gettempdir
5
import subprocess
6 7 8 9 10
from collections import OrderedDict
from pylatexenc.latexencode import utf8tolatex, utf82latex


def remove_ensuremaths():
11
    """remove ensuremath wrappers in utf82latex before translating back from unicode to latex"""
12 13 14 15 16 17
    for key, value in utf82latex.items():
        if value.startswith('\\ensuremath{'):
            utf82latex[key] = value.replace('\\ensuremath{', '', 1)[:-1]


class ExaOutput:
18 19 20
    """generates configuration files for exastencils,
        but only if simdata is given"""
    def __init__(self, simdata=None, username="user", probname=None):
21
        remove_ensuremaths()
22
        self.exastencils_path = Path.home().joinpath("./exastencils")
23

24
        self.username = username
25
        self.l1_string = ""
26 27 28

        if probname is None:
            self.probname = self.username
29 30 31
        else:
            self.probname = probname

32 33
        #output parameters which should also be made adaptable at some point
        self.platform = OrderedDict([
34 35 36 37 38
                            ("targetOS", "Linux"),
                           ("targetCompiler", "GCC"),
                           ("targetCompilerVersion", 5),
                           ("targetCompilerVersionMinor", 4),
                           ("simd_instructionSet", "AVX")
39 40
                           ])
        self.settings = OrderedDict([
41 42 43 44 45 46 47 48
            ("user", self.username),
            ("configName", self.probname),
            ("basePathPrefix", "./" + self.probname),
            ("l1file", "$configName$.exa1"),
            ("debugL1File", "../Debug/$configName$_debug.exa1"),
            ("debugL2File", "../Debug/$configName$_debug.exa2"),
            ("debugL3File", "../Debug/$configName$_debug.exa3"),
            ("debugL4File", "../Debug/$configName$_debug.exa4"),
49 50
            ("htmlLogFile", "../Debug/$configName$_log.html"),
            ("outputPath", "../generated/$configName$/"),
51 52
            ("produceHtmlLog", True),
            ("timeStrategies", True),
53 54
            ("buildfileGenerators", "{ \"MakefileGenerator\" }")
        ])
55
        self.tmppath = Path(gettempdir())
56 57
        self.dirpath = self.exastencils_path.joinpath(self.probname)
        self.filespath = self.dirpath.joinpath(self.probname)
58 59 60 61 62 63 64 65 66 67
        ff = [self.dirpath]  # , self.tmppath.joinpath("generated"), self.tmppath.joinpath("Debug")]
        for f in ff:
            if not os.path.exists(str(f)):
                try:
                    os.makedirs(str(f))
                except OSError as exc:# Guard against race condition
                    if exc.errno != errno.EEXIST:
                        raise
            # # and link it into the exastencils directory
            # os.symlink(str(f), str(self.exastencils_path.joinpath(os.path.basename(os.path.normpath(f)))))
68 69 70 71 72 73 74 75 76
        if simdata is not None:
            self.create_settings()
    #        self.create_platform()
            self.create_knowledge()
            self.create_l1(simdata)
            self.create_examples_list_file()
    #        self.create_l2(simdata)
    #        self.create_l3()
    #        self.create_l4()
77 78

    def create_l1(self, simdata):
79
        l1path = str(self.filespath.with_suffix('.exa1'))
80
        domain_name = utf8tolatex(simdata["domain"]["name"], non_ascii_only=True, brackets=False)
81
        op = utf8tolatex(self.replace_cdot(simdata["pdes"]["pdes"][-1]["op"]), non_ascii_only=True, brackets=False)
82 83
        bc_rhs = self.replace_cdot(self.replace_boundary_x(simdata["bcs"]["bcs"][-1]["rhsstring_expanded"])) #TODO expand
        pde_rhs = self.replace_x(self.replace_cdot(simdata["pdes"]["pdes"][-1]["rhsstring_expanded"]))
84 85
        unknowns = [*simdata["unknowns"]]
        first_unknown = unknowns[0]
Theresa Pollinger's avatar
Theresa Pollinger committed
86
        self.l1_string = str(
87 88
                "/// inline knowledge \n"
                "Knowledge { \n"
Theresa Pollinger's avatar
Theresa Pollinger committed
89
                "  dimensionality = " + str(simdata["num_dimensions"]) + " \n"
90 91 92 93 94 95 96
                " \n"
                "  minLevel       = 5 \n"
                "  maxLevel       = 15 \n"
                "} \n"
                " \n"
                "/// problem specification \n"
                " \n"
Theresa Pollinger's avatar
Theresa Pollinger committed
97
                "Domain \Omega = ( " + str(simdata["domain"]["from"]) + ", " + str(simdata["domain"]["to"]) + " ) \n"
98
                " \n"
Theresa Pollinger's avatar
Theresa Pollinger committed
99 100
                "Field f@finest \in \Omega = " + pde_rhs + " \n"
                "Field " + first_unknown + " \in \Omega = 0.0 \n"
101
                " \n"
102 103
                "Field " + first_unknown + "@finest \in \partial \Omega = " + bc_rhs + " \n" #"sin ( 0.5 * PI * vf_boundaryCoord_x ) \n" #TODO expand
                "Field " + first_unknown + "@(all but finest) \in \partial \Omega = 0.0 \n"
104
                " \n"
Theresa Pollinger's avatar
Theresa Pollinger committed
105
                "Operator op = " + op + " // alt: - \partial_{xx} \n"
106
                " \n"
107 108
                "Equation " + first_unknown + "Eq@finest           op * " + first_unknown + " == f \n" #insert pde
                "Equation " + first_unknown + "Eq@(all but finest) op * " + first_unknown + " == 0.0 \n"
109 110 111 112 113
                " \n"
                "/// configuration of inter-layer transformations \n"
                " \n"
                "DiscretizationHints { // alt: Discretize, L2Hint(s) \n"
                "  f on Node \n"
114
                "  " + first_unknown + " on Node \n"
115 116 117
                " \n"
                "  op on \Omega \n"
                " \n"
118
                "  " + first_unknown + "Eq \n"
119 120 121 122 123 124
                " \n"
                "  // paramters \n"
                "  discr_type = \"" + simdata["sim"]["type"] + "\" \n"
                "} \n"
                " \n"
                "SolverHints { // alt: Solve, L3Hint(s) \n"
125
                "  generate solver for " + first_unknown + " in " + first_unknown + "Eq \n"
126 127 128 129 130 131 132 133
                " \n"
                "  // parameters \n"
                "  solver_targetResReduction = 1e-6 \n"
                "} \n"
                " \n"
                "ApplicationHints { // alt L4Hint(s) \n"
                "  // parameters \n"
                "  l4_genDefaultApplication = true \n"
134
                "  l4_defAppl_FieldToPrint = \"" + first_unknown + "\" \n" #TODO
Theresa Pollinger's avatar
Theresa Pollinger committed
135 136 137
                "} \n")
        with open(l1path, 'w') as l1:
            l1.write(self.l1_string)
138 139

    def replace_x(self, string):
140 141 142 143
        return string.replace("x", "vf_nodePosition_x@current")

    def replace_cdot(self, string):
        return string.replace("⋅", "*")
144 145 146 147 148

    def replace_boundary_x(self, string):
        return string.replace("x", "vf_boundaryCoord_x")

    def create_l2(self, simdata):
149
        l2path = str(self.filespath.with_suffix('.exa2'))
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
        with open(l2path,'w') as l2:
            l2.write(
                "Domain global< " + simdata["domain"]["from"] + " to " + simdata["domain"]["to"] + " > \n" # TODO domain goes here
                "\n"
                "Field Solution with Real on Node of global = 0.0 \n" #TODO codomain goes here
                "Field Solution@finest on boundary = vf_boundaryCoord_x ** 2 \n" # TODO BCs go here
                "Field Solution@(all but finest) on boundary = 0.0 \n"
                "\n"
                "Field RHS with Real on Node of global = 0.0 \n" #TODO RHS goes here
                #"Operator Laplace from kron ( Laplace_1D, Laplace_1D ) \n"
                "Operator Laplace_1D from Stencil { \n"
                "   [ 0] => 2.0 / ( vf_gridWidth_x ** 2 ) \n"
                "   [-1] => -1.0 / ( vf_gridWidth_x ** 2 ) \n"
                "   [ 1] => -1.0 / ( vf_gridWidth_x ** 2 ) \n"
                "\n"
                "Equation solEq@finest { \n"
                "    Laplace_1D * Solution == RHS \n"
                "} \n"
                "\n"
                "Equation solEq@(all but finest) { \n"
                "    Laplace_1D * Solution == 0.0 \n"
                "} \n"
            )

    def create_l3(self):
175 176
        l3path = str(self.filespath.with_suffix('.exa3'))
        with open(l3path, 'w') as l3:
177 178 179 180 181
            l3.write(
                "generate solver for Solution in solEq \n"
            )

    def create_l4(self):
182
        l4path = str(self.filespath.with_suffix('.exa4'))
183
        with open(l4path, 'w') as l4:
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
            l4.write(
                "Function Application ( ) : Unit { \n"
                "   startTimer ( 'setup' ) \n"
                "\n"
                "   initGlobals ( ) \n"
                "   initDomain ( ) \n"
                "   initFieldsWithZero ( ) \n"
                "   initGeometry ( ) \n"
                "   InitFields ( ) \n"
                "\n"
                "   stopTimer ( 'setup' ) \n"
                "\n"
                "   startTimer ( 'solve' ) \n"
                "\n"
                "   Solve@finest ( ) \n"
                "\n"
                "   stopTimer ( 'solve' ) \n"
                "\n"
                "   printAllTimers ( ) \n"
                "\n"
                "   destroyGlobals ( ) \n"
                "} \n"
            )

    def key_val(self, key, val):
        return key.ljust(30) + ' = ' + val + '\n'

    def format_key_val(self, key, val):
212 213 214 215
        val_repr = repr(val).replace('\'', '\"')
        if isinstance(val, bool):
            val_repr = val_repr.lower()
        return self.key_val(key, val_repr)
216 217 218 219 220

    def format_key(self, key, dict):
        return self.format_key_val(key, dict[key])

    def create_settings(self):
221 222
        settingspath = str(self.filespath) + '.settings'
        with open(settingspath, 'w') as settingsfile:
223
            for key in self.settings:
224 225 226 227
                if key == "buildfileGenerators":
                    settingsfile.write(self.key_val(key, self.settings[key]))
                else:
                    settingsfile.write(self.format_key(key, self.settings))
228 229

    def create_platform(self):
230
        platformpath = str(self.filespath) + '.platform'
231
        with open(platformpath, 'w') as platformfile:
232 233 234 235
            for key in self.platform:
                platformfile.write(self.format_key(key, self.platform))

    def create_knowledge(self):
236
        knowledgepath = str(self.filespath) + '.knowledge'
237
        with open(knowledgepath, 'w') as knowledgefile:
238 239 240 241 242 243 244
#            for key in self.knowledge:
#                knowledgefile.write(self.format_key(key, self.knowledge))
            knowledgefile.write(
                "// omp parallelization on exactly one fragment in one block \n"
                "import '../lib/domain_onePatch.knowledge' \n"
                "import '../lib/parallelization_pureOmp.knowledge'"
            )
245 246 247 248 249 250 251 252 253

    def create_examples_list_file(self):
        examples_path = str(self.exastencils_path.joinpath("examples").with_suffix('.sh'))
        with open(examples_path, 'w') as shell_file:
            shell_file.write(
                "#!/usr/bin/env bash \n"\
                "\n"
                "\n"
                "configList=\"\" \n"\
254
                "configList+=\"{}/{} \" \n".format(self.probname, self.probname)) #(self.dirpath, self.probname))
255

256 257 258
class ExaRunner:
    """A class to run exastencils using the files generated by an Exaoutput class, and to get the results"""

259 260 261 262 263
    from functools import lru_cache

    def __init__(self, exaout):
        self.exaout = exaout

264
    def run_exastencils(self):
265
        # print(str(os.path.abspath(self.exaout.exastencils_path)))
266 267 268 269 270 271
        p = subprocess.run(["./generate_compile_and_run_list.sh"], cwd=str(self.exaout.exastencils_path),
                           stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        out = p.stdout
        if p.returncode != 0:
            print(out)

272
    @lru_cache()
273
    def load_data(self, data_name="u"):  # TODO more dimensions
274
        import pandas as pd
275 276
        data_path = self.exaout.exastencils_path.joinpath("generated").joinpath(self.exaout.probname)\
                                                .joinpath(data_name).with_suffix(".dat")
277 278
        df = pd.read_csv(data_path, sep=' ', index_col=0)
        try:
279
            df.columns = [data_name]
280
        except ValueError:  # length mismatch because additional column of nans was read
281
            df.columns = [data_name, 'nan']
282
        return df