SPH
BatchDialog.cpp
Go to the documentation of this file.
2 #include "gui/windows/Widgets.h"
3 #include "run/Config.h"
4 #include <wx/button.h>
5 #include <wx/menu.h>
6 #include <wx/msgdlg.h>
7 #include <wx/sizer.h>
8 #include <wx/spinctrl.h>
9 #include <wx/stattext.h>
10 #include <wx/textdlg.h>
11 
13 
14 void BatchManager::modifyHierarchy(const Size runIdx, JobNode& node) {
15  for (Size colIdx = 0; colIdx < cols.size(); ++colIdx) {
16  const std::string baseName = cols[colIdx].node->instanceName();
17 
18  // find the node in the hierarchy
19  SharedPtr<JobNode> modifiedNode;
20  node.enumerate([&baseName, &modifiedNode](SharedPtr<JobNode> node) {
21  const std::string name = node->instanceName();
22  const std::size_t n = name.find(" / ");
23  if (n == std::string::npos) {
24  throw InvalidSetup("Invalid name of cloned node: " + name);
25  }
26  const std::string clonedName = name.substr(n + 3);
27  if (baseName == clonedName) {
28  modifiedNode = node;
29  }
30  });
31 
32  if (modifiedNode) {
33  this->modifyNode(*modifiedNode, runIdx, colIdx);
34  } else {
35  throw InvalidSetup("Node '" + baseName + "' not found");
36  }
37  }
38 }
39 
42 private:
43  std::stringstream ss;
44 
45 public:
46  BatchValueVisitor(const std::string& newValue)
47  : ss(newValue) {}
48 
49  void operator()(Vector& v) {
50  ss >> v[X] >> v[Y] >> v[Z];
51  }
52 
53  void operator()(Interval& i) {
54  Float lower, upper;
55  ss >> lower >> upper;
56  i = Interval(lower, upper);
57  }
58 
59  void operator()(Path& path) {
60  path = Path(ss.str());
61  }
62 
63  void operator()(EnumWrapper& ew) {
64  const Optional<int> value = EnumMap::fromString(ss.str(), ew.index);
65  if (value) {
66  ew.value = value.value();
67  } else {
68  throw InvalidSetup("Value '" + ss.str() +
69  "' is invalid for this parameter. Possible values are:\n" +
71  }
72  }
73 
74  void operator()(ExtraEntry& extra) {
75  extra.fromString(ss.str());
76  }
77 
79  template <typename T>
80  void operator()(T& value) {
81  ss >> value;
82  }
83 };
84 
85 void BatchManager::modifyNode(JobNode& node, const Size runIdx, const Size paramIdx) {
86  const std::string newValue = getCell(paramIdx, runIdx);
87  VirtualSettings settings = node.getSettings();
88  IVirtualEntry::Value variant = settings.get(cols[paramIdx].key);
89  BatchValueVisitor visitor(newValue);
90  forValue(variant, visitor);
91  settings.set(cols[paramIdx].key, variant);
92 }
93 
95  SharedPtr<ConfigNode> root = config.getNode("batch");
96  const Size rowCnt = root->get<int>("runCount");
97  const Size colCnt = root->get<int>("paramCount");
98  this->resize(rowCnt, colCnt);
99 
100  SharedPtr<ConfigNode> paramNode = root->getChild("params");
101  for (Size i = 0; i < colCnt; ++i) {
102  const Optional<std::string> paramDesc = paramNode->tryGet<std::string>("param-" + std::to_string(i));
103  if (!paramDesc) {
104  continue;
105  }
106  const std::size_t sep = paramDesc->find("->");
107  if (sep == std::string::npos) {
108  continue;
109  }
110  cols[i].key = paramDesc->substr(sep + 2);
111  const std::string name = paramDesc->substr(0, sep);
112  auto iter = std::find_if(nodes.begin(), nodes.end(), [&name](const SharedPtr<JobNode>& node) {
113  return node->instanceName() == name;
114  });
115  if (iter != nodes.end()) {
116  cols[i].node = *iter;
117  }
118  }
119 
120  SharedPtr<ConfigNode> runNode = root->getChild("runs");
121  for (Size i = 0; i < rowCnt; ++i) {
122  rows[i] = runNode->get<std::string>("run-" + std::to_string(i));
123  }
124 
125  SharedPtr<ConfigNode> cellNode = root->getChild("cells");
126  for (Size j = 0; j < rowCnt; ++j) {
127  for (Size i = 0; i < colCnt; ++i) {
128  cells[Pixel(i, j)] =
129  cellNode->get<std::string>("cell-" + std::to_string(i) + "-" + std::to_string(j));
130  }
131  }
132 }
133 
134 void BatchManager::save(Config& config) {
135  SharedPtr<ConfigNode> root = config.addNode("batch");
136  root->set("runCount", rows.size());
137  root->set("paramCount", cols.size());
138 
139  SharedPtr<ConfigNode> paramNode = root->addChild("params");
140  for (Size i = 0; i < cols.size(); ++i) {
141  if (cols[i].node) {
142  paramNode->set("param-" + std::to_string(i), cols[i].node->instanceName() + "->" + cols[i].key);
143  }
144  }
145 
146  SharedPtr<ConfigNode> runNode = root->addChild("runs");
147  for (Size i = 0; i < rows.size(); ++i) {
148  runNode->set("run-" + std::to_string(i), getRunName(i));
149  }
150 
151  SharedPtr<ConfigNode> cellNode = root->addChild("cells");
152  for (Size j = 0; j < rows.size(); ++j) {
153  for (Size i = 0; i < cols.size(); ++i) {
154  cellNode->set("cell-" + std::to_string(i) + "-" + std::to_string(j), cells[Pixel(i, j)]);
155  }
156  }
157 }
158 
160 private:
161  wxArrayString& items;
162  Array<std::string>& keys;
163 
164 public:
165  AddParamProc(wxArrayString& items, Array<std::string>& keys)
166  : items(items)
167  , keys(keys) {
168  keys.clear();
169  }
170 
171  virtual void onCategory(const std::string& UNUSED(name)) const override {}
172 
173  virtual void onEntry(const std::string& key, IVirtualEntry& entry) const override {
174  keys.push(key);
175  items.Add(entry.getName());
176  }
177 };
178 
179 class ParamSelectDialog : public wxDialog {
180 private:
182  ComboBox* nodeBox;
183  ComboBox* paramBox;
184 
185  struct {
187  } cached;
188 
189 public:
190  ParamSelectDialog(wxWindow* parent, ArrayView<const SharedPtr<JobNode>> nodes)
191  : wxDialog(parent, wxID_ANY, "Select parameter")
192  , nodes(nodes) {
193  wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
194 
195  wxBoxSizer* nodeSizer = new wxBoxSizer(wxHORIZONTAL);
196  nodeSizer->Add(new wxStaticText(this, wxID_ANY, "Node:", wxDefaultPosition, wxSize(120, -1)));
197  nodeBox = new ComboBox(this, "", wxSize(200, -1));
198  wxArrayString items;
199  for (const SharedPtr<JobNode>& node : nodes) {
200  items.Add(node->instanceName());
201  }
202  nodeBox->Set(items);
203  nodeBox->SetSelection(0);
204  nodeSizer->Add(nodeBox);
205  sizer->Add(nodeSizer);
206 
207  wxBoxSizer* paramSizer = new wxBoxSizer(wxHORIZONTAL);
208  paramSizer->Add(new wxStaticText(this, wxID_ANY, "Parameter:", wxDefaultPosition, wxSize(120, -1)));
209  paramBox = new ComboBox(this, "", wxSize(200, -1));
210  paramSizer->Add(paramBox);
211  sizer->Add(paramSizer);
212 
213  nodeBox->Bind(wxEVT_COMBOBOX, [this, nodes](wxCommandEvent& UNUSED(evt)) {
214  const int idx = nodeBox->GetSelection();
215  SPH_ASSERT(idx < int(nodes.size()));
216  VirtualSettings settings = nodes[idx]->getSettings();
217  wxArrayString items;
218  settings.enumerate(AddParamProc(items, cached.keys));
219  paramBox->Set(items);
220  paramBox->SetSelection(0);
221  });
222 
223  wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
224  wxButton* okButton = new wxButton(this, wxID_ANY, "OK");
225  okButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) { this->EndModal(wxID_OK); });
226  buttonSizer->Add(okButton);
227  wxButton* cancelButton = new wxButton(this, wxID_ANY, "Cancel");
228  cancelButton->Bind(
229  wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) { this->EndModal(wxID_CANCEL); });
230  buttonSizer->Add(cancelButton);
231  sizer->Add(buttonSizer);
232 
233  this->SetSizer(sizer);
234  }
235 
237  return nodes[nodeBox->GetSelection()];
238  }
239 
240  std::string getKey() const {
241  return cached.keys[paramBox->GetSelection()];
242  }
243 
244  wxString getLabel() const {
245  return nodeBox->GetValue() + " - " + paramBox->GetValue();
246  }
247 };
248 
249 BatchDialog::BatchDialog(wxWindow* parent, const BatchManager& mgr, Array<SharedPtr<JobNode>>&& nodes)
250  : wxDialog(parent, wxID_ANY, "Batch run", wxDefaultPosition, wxSize(800, 530))
251  , manager(mgr.clone())
252  , nodes(std::move(nodes)) {
253 
254  wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
255 
256  wxBoxSizer* controlsSizer = new wxBoxSizer(wxHORIZONTAL);
257  controlsSizer->Add(new wxStaticText(this, wxID_ANY, "Run count:"));
258  wxSpinCtrl* runSpinner = new wxSpinCtrl(this, wxID_ANY);
259  runSpinner->SetValue(manager.getRunCount());
260  controlsSizer->Add(runSpinner);
261  controlsSizer->Add(new wxStaticText(this, wxID_ANY, "Parameter count:"));
262  wxSpinCtrl* paramSpinner = new wxSpinCtrl(this, wxID_ANY);
263  paramSpinner->SetValue(manager.getParamCount());
264  controlsSizer->Add(paramSpinner);
265 
266  sizer->Add(controlsSizer);
267  sizer->AddSpacer(10);
268 
269  grid = new wxGrid(this, wxID_ANY, wxDefaultPosition, wxSize(800, 450));
270  grid->SetDefaultColSize(200);
271  grid->SetTabBehaviour(wxGrid::Tab_Wrap);
272  grid->CreateGrid(manager.getRunCount(), manager.getParamCount());
273  grid->EnableEditing(true);
274 
275  sizer->Add(grid);
276 
277  wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
278  wxButton* okButton = new wxButton(this, wxID_ANY, "OK");
279  okButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) { this->EndModal(wxID_OK); });
280  buttonSizer->Add(okButton);
281  wxButton* closeButton = new wxButton(this, wxID_ANY, "Cancel");
282  closeButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) { this->EndModal(wxID_CANCEL); });
283  buttonSizer->Add(closeButton);
284  sizer->Add(buttonSizer);
285 
286  grid->Bind(wxEVT_GRID_LABEL_LEFT_DCLICK, [this](wxGridEvent& evt) {
287  const bool isRow = evt.GetCol() == -1;
288  if (isRow) {
289  wxTextEntryDialog* dialog = new wxTextEntryDialog(this, "Enter name of the run");
290  dialog->SetValue(grid->GetRowLabelValue(evt.GetRow()));
291  if (dialog->ShowModal() == wxID_OK) {
292  const wxString value = dialog->GetValue();
293  grid->SetRowLabelValue(evt.GetRow(), value);
294  manager.setRunName(evt.GetRow(), std::string(value));
295  }
296  } else {
297  ParamSelectDialog* dialog = new ParamSelectDialog(this, this->nodes);
298  // dialog->SetValue(grid->GetColLabelValue(evt.GetCol()));
299  if (dialog->ShowModal() == wxID_OK) {
300  grid->SetColLabelValue(evt.GetCol(), dialog->getLabel());
301  manager.setParam(evt.GetCol(), dialog->getNode(), dialog->getKey());
302  }
303  }
304  });
305  grid->Bind(wxEVT_GRID_LABEL_RIGHT_CLICK, [this](wxGridEvent& evt) {
306  if (evt.GetRow() == -1) {
307  return;
308  }
309 
310  wxMenu menu;
311  menu.Append(0, "Duplicate");
312  menu.Append(1, "Delete");
313 
314  const Size rowIdx = evt.GetRow();
315  menu.Bind(wxEVT_COMMAND_MENU_SELECTED, [this, rowIdx](wxCommandEvent& evt) {
316  switch (evt.GetId()) {
317  case 0:
318  manager.duplicateRun(rowIdx);
319  grid->InsertRows(rowIdx, 1);
320  break;
321  case 1:
322  manager.deleteRun(rowIdx);
323  grid->DeleteRows(rowIdx, 1);
324  break;
325  default:
326  NOT_IMPLEMENTED;
327  }
328  this->update();
329  });
330 
331  this->PopupMenu(&menu);
332  });
333 
334  grid->Bind(wxEVT_GRID_CELL_CHANGED, [this](wxGridEvent& evt) { //
335  const std::string value(grid->GetCellValue(evt.GetRow(), evt.GetCol()));
336  manager.setCell(evt.GetCol(), evt.GetRow(), value);
337  });
338 
339  runSpinner->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent& evt) {
340  const int newRunCount = max(evt.GetValue(), 1);
341  const int oldRunCount = grid->GetNumberRows();
342  if (newRunCount > oldRunCount) {
343  grid->InsertRows(oldRunCount, newRunCount - oldRunCount);
344  } else {
345  grid->DeleteRows(newRunCount, oldRunCount - newRunCount);
346  }
347  manager.resize(newRunCount, grid->GetNumberCols());
348  });
349  paramSpinner->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent& evt) {
350  const int newParamCount = max(evt.GetValue(), 1);
351  const int oldParamCount = grid->GetNumberCols();
352  if (newParamCount > oldParamCount) {
353  grid->InsertCols(oldParamCount, newParamCount - oldParamCount);
354  } else {
355  grid->DeleteCols(newParamCount, oldParamCount - newParamCount);
356  }
357  manager.resize(grid->GetNumberRows(), newParamCount);
358  });
359 
360  this->update();
361  this->SetSizer(sizer);
362  this->Layout();
363 }
364 
366 
367 void BatchDialog::update() {
368  const Size runCnt = manager.getRunCount();
369  const Size paramCnt = manager.getParamCount();
370  SPH_ASSERT(grid->GetNumberCols() == int(paramCnt));
371  SPH_ASSERT(grid->GetNumberRows() == int(runCnt));
372  for (Size j = 0; j < runCnt; ++j) {
373  grid->SetRowLabelValue(j, manager.getRunName(j));
374  }
375  for (Size i = 0; i < paramCnt; ++i) {
376  SharedPtr<JobNode> node = manager.getParamNode(i);
377  if (node) {
378  grid->SetColLabelValue(i, node->instanceName() + " - " + manager.getParamKey(i));
379  }
380  }
381  for (Size j = 0; j < runCnt; ++j) {
382  for (Size i = 0; i < paramCnt; ++i) {
383  grid->SetCellValue(j, i, manager.getCell(i, j));
384  }
385  }
386 }
387 
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
Interface for the configuration files storing job data.
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
constexpr INLINE T max(const T &f1, const T &f2)
Definition: MathBasic.h:20
INLINE Float root(const Float f)
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
decltype(auto) INLINE forValue(Variant< TArgs... > &variant, TFunctor &&functor)
Definition: Variant.h:428
@ Y
Definition: Vector.h:23
@ X
Definition: Vector.h:22
@ Z
Definition: Vector.h:24
AddParamProc(wxArrayString &items, Array< std::string > &keys)
virtual void onCategory(const std::string &UNUSED(name)) const override
virtual void onEntry(const std::string &key, IVirtualEntry &entry) const override
Called for every entry in the current category.
Object providing safe access to continuous memory of data.
Definition: ArrayView.h:17
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
void clear()
Removes all elements from the array, but does NOT release the memory.
Definition: Array.h:434
INLINE TCounter size() const noexcept
Definition: Array.h:193
BatchDialog(wxWindow *parent, const BatchManager &manager, Array< SharedPtr< JobNode >> &&nodes)
std::string getParamKey(const Size colIdx) const
Definition: BatchDialog.h:53
void save(Config &config)
void setCell(const Size colIdx, const Size rowIdx, const std::string &value)
Definition: BatchDialog.h:74
Size getParamCount() const
Definition: BatchDialog.h:49
void load(Config &config, ArrayView< const SharedPtr< JobNode >> nodes)
Definition: BatchDialog.cpp:94
std::string getRunName(const Size rowIdx) const
Definition: BatchDialog.h:41
Size getRunCount() const
Definition: BatchDialog.h:37
void resize(const Size rowCnt, const Size colCnt)
Definition: BatchDialog.h:78
void modifyHierarchy(const Size runIdx, JobNode &node)
Modifies the settings of the given node hierarchy.
Definition: BatchDialog.cpp:14
std::string getCell(const Size colIdx, const Size rowIdx) const
Definition: BatchDialog.h:61
void setRunName(const Size rowIdx, const std::string &name)
Definition: BatchDialog.h:65
SharedPtr< JobNode > getParamNode(const Size colIdx) const
Definition: BatchDialog.h:57
void setParam(const Size colIdx, const SharedPtr< JobNode > &node, const std::string &key)
Definition: BatchDialog.h:69
void operator()(Path &path)
Definition: BatchDialog.cpp:59
void operator()(Interval &i)
Definition: BatchDialog.cpp:53
void operator()(ExtraEntry &extra)
Definition: BatchDialog.cpp:74
void operator()(EnumWrapper &ew)
Definition: BatchDialog.cpp:63
void operator()(Vector &v)
Definition: BatchDialog.cpp:49
BatchValueVisitor(const std::string &newValue)
Definition: BatchDialog.cpp:46
void operator()(T &value)
Default overload.
Definition: BatchDialog.cpp:80
Provides functionality for reading and writing configuration files.
Definition: Config.h:272
SharedPtr< ConfigNode > addNode(const std::string &name)
Adds a new node to the config.
Definition: Config.cpp:81
SharedPtr< ConfigNode > getNode(const std::string &name)
Returns a node with given name.
Definition: Config.cpp:85
static Optional< TEnum > fromString(const std::string &value)
Definition: EnumMap.h:101
static std::string getDesc()
Definition: EnumMap.h:119
Copyable wrapper of a IExtraEntry.
void fromString(const std::string &s) const
Represents a virtual entry in the settings.
virtual std::string getName() const =0
Returns a descriptive name of the entry.
Object representing a 1D interval of real numbers.
Definition: Interval.h:17
Thrown when components of the run are mutually incompatible.
Definition: Exceptions.h:24
Building block of a simulation hierarchy.
Definition: Node.h:88
std::string instanceName() const
Returns the instance name of the job.
Definition: Node.cpp:14
void enumerate(Function< void(const SharedPtr< JobNode > &job)> func)
Enumerates all nodes in the hierarchy.
VirtualSettings getSettings() const
Returns settings object allowing to access and modify the state of the job.
Definition: Node.cpp:39
INLINE Type & value()
Returns the reference to the stored value.
Definition: Optional.h:172
ParamSelectDialog(wxWindow *parent, ArrayView< const SharedPtr< JobNode >> nodes)
SharedPtr< JobNode > getNode() const
Array< std::string > keys
wxString getLabel() const
std::string getKey() const
Object representing a path on a filesystem.
Definition: Path.h:17
INLINE RawPtr< T > get() const
Definition: SharedPtr.h:223
Variant, an implementation of type-safe union, similar to std::variant or boost::variant.
Definition: Variant.h:171
Interface allowing to enumerate all entries in the settings.
Holds a map of virtual entries, associated with a unique name.
void enumerate(const IEntryProc &proc)
Enumerates all entries stored in the settings.
IVirtualEntry::Value get(const std::string &key) const
Returns a value of an entry.
void set(const std::string &key, const IVirtualEntry::Value &value)
Modifies an existing entry in the settings.
Overload of std::swap for Sph::Array.
Definition: Array.h:578
Wrapper of an enum.
Definition: Settings.h:37
int value
Definition: Settings.h:38
EnumIndex index
Definition: Settings.h:40
Definition: Point.h:101