CreateConfig.cpp 6.61 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//======================================================================================================================
//
//  This file is part of waLBerla. waLBerla 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.
//
//  waLBerla 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 waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
//
Jean-Noël Grad's avatar
Jean-Noël Grad committed
16
//! \file CreateConfig.cpp
17
18
//! \ingroup python
//! \author Martin Bauer <martin.bauer@fau.de>
19
//! \author Markus Holzer <markus.holzer@fau.de>
20
21
22
23
24
//
//======================================================================================================================

#include "CreateConfig.h"

25
#include "core/StringUtility.h"
26
27
28
29
30
#include "core/config/Config.h"
#include "core/config/Create.h"
#include "core/logging/Logging.h"

#ifdef WALBERLA_BUILD_WITH_PYTHON
31
#   include "python_coupling/helper/ConfigFromDict.h"
32

33
34
#   include "PythonCallback.h"
#   include <pybind11/pybind11.h>
35

36
37
38
39
40
namespace walberla
{
namespace python_coupling
{
namespace py = pybind11;
41

42
43
44
45
46
shared_ptr< Config > createConfigFromPythonScript(const std::string& scriptFile,
                                                         const std::string& pythonFunctionName,
                                                         const std::vector< std::string >& argv)
{
   importModuleOrFile(scriptFile, argv);
47

48
   PythonCallback pythonCallback(pythonFunctionName);
49

50
   pythonCallback();
51

52
53
   using py::dict;
   using py::object;
54

55
56
   object returnValue = pythonCallback.data().dict()["returnValue"];
   if (returnValue.is(object())) return shared_ptr< Config >();
57

58
59
60
61
62
   bool isDict = py::isinstance< dict >(returnValue);
   if (!isDict) { WALBERLA_ABORT("Python configuration did not return a dictionary object."); }
   dict returnDict = dict(returnValue);
   return configFromPythonDict(returnDict);
}
63

64
65
66
67
68
//===================================================================================================================
//
//  Config Generators and iterators
//
//===================================================================================================================
69

70
71
72
class PythonMultipleConfigGenerator : public config::ConfigGenerator
{
 public:
Markus Holzer's avatar
Markus Holzer committed
73
74
   PythonMultipleConfigGenerator(py::object ScenarioConfigGenerator) // NOLINT
      : ScenarioConfigGenerator_(ScenarioConfigGenerator)            // NOLINT
75
   {}
76

77
   shared_ptr< Config > next() override
78
   {
79
      shared_ptr< Config > config = make_shared< Config >();
Markus Holzer's avatar
Markus Holzer committed
80
81
82
83
84
85
86
87
      try
      {
         py::dict configDict = ScenarioConfigGenerator_.attr("__next__")();
         configFromPythonDict(config->getWritableGlobalBlock(), configDict);
         return config;
      }
      catch (py::error_already_set&)
      {
88
         return shared_ptr<Config>();
Markus Holzer's avatar
Markus Holzer committed
89
      }
90
   }
91

92
 private:
Markus Holzer's avatar
Markus Holzer committed
93
   py::object ScenarioConfigGenerator_;
94
};
95

96
97
98
99
class PythonSingleConfigGenerator : public config::ConfigGenerator
{
 public:
   PythonSingleConfigGenerator(const shared_ptr< Config >& config) : config_(config) {}
100

101
   shared_ptr< Config > next() override
102
   {
103
104
105
106
      auto res = config_;
      config_.reset();
      return res;
   }
107

108
109
110
 private:
   shared_ptr< Config > config_;
};
111

112
113
114
115
116
117
118
config::Iterator createConfigIteratorFromPythonScript(const std::string& scriptFile,
                                                             const std::string& pythonFunctionName,
                                                             const std::vector< std::string >& argv)
{
   importModuleOrFile(scriptFile, argv);
   PythonCallback pythonCallback(pythonFunctionName);
   pythonCallback();
119

120
121
   py::object returnValue = pythonCallback.data().dict()["returnValue"];
   bool isDict            = py::isinstance< py::dict >(returnValue);
122

123
124
   shared_ptr< config::ConfigGenerator > generator;
   if (isDict)
125
   {
126
127
128
129
130
131
132
133
      auto config            = make_shared< Config >();
      py::dict extractedDict = py::cast< py::dict >(returnValue);
      configFromPythonDict(config->getWritableGlobalBlock(), extractedDict);
      generator = make_shared< PythonSingleConfigGenerator >(config);
   }
   else
   {
      try
134
      {
135
136
137
138
139
         generator = make_shared< PythonMultipleConfigGenerator >(returnValue);
      } catch (py::error_already_set&)
      {
         std::string message = std::string("Error while running Python function ") + pythonFunctionName;
         WALBERLA_ABORT_NO_DEBUG_INFO(message);
140
141
142
      }
   }

143
144
   return config::Iterator(generator);
}
145
146
147
148
149
150

} // namespace python_coupling
} // namespace walberla

#else

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
namespace walberla
{
namespace python_coupling
{
shared_ptr< Config > createConfigFromPythonScript(const std::string&, const std::string&,
                                                  const std::vector< std::string >&)
{
   WALBERLA_ABORT("Tried to run with Python config but waLBerla was built without Python support.");
   return shared_ptr< Config >();
}

config::Iterator createConfigIteratorFromPythonScript(const std::string&, const std::string&,
                                                      const std::vector< std::string >&)
{
   WALBERLA_ABORT("Tried to run with Python config but waLBerla was built without Python support.");
   return config::Iterator();
}
168
169
170
171
172
173

} // namespace python_coupling
} // namespace walberla

#endif

174
175
176
177
178
179
180
namespace walberla
{
namespace python_coupling
{
shared_ptr< Config > createConfig(int argc, char** argv)
{
   if (argc < 2) throw std::runtime_error(config::usageString(argv[0]));
181

182
183
   shared_ptr< Config > config;
   std::string filename(argv[1]);
184

185
   auto argVec = std::vector< std::string >(argv + 1, argv + argc);
186

187
188
   if (string_ends_with(filename, ".py")) { config = createConfigFromPythonScript(filename, "config", argVec); }
   else
189
   {
190
191
192
      config = make_shared< Config >();
      config::createFromTextFile(*config, filename);
   }
193

194
   config::substituteCommandLineArgs(*config, argc, argv);
195

196
197
   return config;
}
198

199
200
201
config::Iterator configBegin(int argc, char** argv)
{
   if (argc < 2) throw std::runtime_error(config::usageString(argv[0]));
202

203
204
205
206
207
   std::string filename(argv[1]);
   if (string_ends_with(filename, ".py"))
   {
      auto argVec = std::vector< std::string >(argv + 1, argv + argc);
      return createConfigIteratorFromPythonScript(filename, "config", argVec);
208
   }
209
   else
210
   {
211
      return config::begin(argc, argv);
212
   }
213
}
214
215
216

} // namespace python_coupling
} // namespace walberla