SPH
GridPage.cpp
Go to the documentation of this file.
1 #include "gui/windows/GridPage.h"
2 #include "gui/MainLoop.h"
3 #include "gui/Utils.h"
4 #include "io/Path.h"
5 #include "quantities/Quantity.h"
6 #include "system/Factory.h"
7 #include "thread/Scheduler.h"
8 #include "windows/Widgets.h"
9 #include <fstream>
10 #include <numeric>
11 #include <wx/button.h>
12 #include <wx/checkbox.h>
13 #include <wx/msgdlg.h>
14 #include <wx/sizer.h>
15 #include <wx/spinctrl.h>
16 #include <wx/statbox.h>
17 #include <wx/stattext.h>
18 
20 
28 enum class CheckFlag {
29  PARTICLE_COUNT = 1 << 0,
30  MASS_FRACTION = 1 << 1,
31  DIAMETER = 1 << 2,
32  VELOCITY_DIFFERENCE = 1 << 3,
33  PERIOD = 1 << 4,
34  RATIO_CB = 1 << 5,
35  RATIO_BA = 1 << 6,
36  SPHERICITY = 1 << 7,
37  MOONS = 1 << 8,
38 };
39 const Size CHECK_COUNT = 9;
40 
41 GridPage::GridPage(wxWindow* parent, const wxSize size, const Storage& storage)
42  : wxPanel(parent, wxID_ANY, wxDefaultPosition, size)
43  , storage(storage) {
44 
45  wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
46 
47  wxBoxSizer* countSizer = new wxBoxSizer(wxHORIZONTAL);
48  countSizer->Add(new wxStaticText(this, wxID_ANY, "Number of fragments"));
49  countSpinner = new wxSpinCtrl(this, wxID_ANY);
50  countSpinner->SetValue(4);
51  countSizer->Add(countSpinner);
52  sizer->Add(countSizer);
53 
54  wxGridSizer* boxSizer = new wxGridSizer(4, 2, 2);
55  boxSizer->Add(new wxCheckBox(this, int(CheckFlag::PARTICLE_COUNT), "Particle count"));
56  boxSizer->Add(new wxCheckBox(this, int(CheckFlag::MASS_FRACTION), "Mass fraction"));
57  boxSizer->Add(new wxCheckBox(this, int(CheckFlag::DIAMETER), "Diameter [km]"));
58  boxSizer->Add(new wxCheckBox(this, int(CheckFlag::VELOCITY_DIFFERENCE), "Velocity difference [m/s]"));
59  boxSizer->Add(new wxCheckBox(this, int(CheckFlag::PERIOD), "Period [h]"));
60  boxSizer->Add(new wxCheckBox(this, int(CheckFlag::RATIO_CB), "Ratio c/b"));
61  boxSizer->Add(new wxCheckBox(this, int(CheckFlag::RATIO_BA), "Ratio b/a"));
62  boxSizer->Add(new wxCheckBox(this, int(CheckFlag::SPHERICITY), "Sphericity"));
63  sizer->Add(boxSizer);
64 
65  wxStaticBox* moonGroup = new wxStaticBox(this, wxID_ANY, "Moons", wxDefaultPosition, wxSize(-1, 60));
66  wxBoxSizer* moonSizer = new wxBoxSizer(wxHORIZONTAL);
67  wxCheckBox* moonBox = new wxCheckBox(moonGroup, int(CheckFlag::MOONS), "Moon counts");
68  moonSizer->Add(moonBox);
69  moonSizer->AddSpacer(30);
70  moonSizer->Add(new wxStaticText(moonGroup, wxID_ANY, "Mass ratio limit"));
71  FloatTextCtrl* limitSpinner = new FloatTextCtrl(moonGroup, 0.1f);
72  moonSizer->Add(limitSpinner);
73  limitSpinner->Enable(false);
74  moonGroup->SetSizer(moonSizer);
75  moonSizer->AddSpacer(30);
76  moonSizer->Add(new wxStaticText(moonGroup, wxID_ANY, "Pericenter limit"));
77  FloatTextCtrl* radiiSpinner = new FloatTextCtrl(moonGroup, 2.f);
78  moonSizer->Add(radiiSpinner);
79  radiiSpinner->Enable(false);
80  moonGroup->SetSizer(moonSizer);
81  sizer->Add(moonGroup);
82  moonBox->Bind(wxEVT_CHECKBOX, [moonBox, limitSpinner, radiiSpinner](wxCommandEvent&) {
83  limitSpinner->Enable(moonBox->GetValue());
84  radiiSpinner->Enable(moonBox->GetValue());
85  });
86 
87  wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
88  wxButton* computeButton = new wxButton(this, wxID_ANY, "Compute");
89  buttonSizer->Add(computeButton);
90 
91  wxButton* saveButton = new wxButton(this, wxID_ANY, "Save to file");
92  saveButton->Enable(false);
93  buttonSizer->Add(saveButton);
94  sizer->Add(buttonSizer);
95 
96  this->SetSizer(sizer);
97  this->Layout();
98 
99  computeButton->Bind(wxEVT_BUTTON,
100  [this, size, sizer, saveButton, limitSpinner, radiiSpinner](wxCommandEvent& UNUSED(evt)) {
101  const Size checkCount = this->getCheckedCount();
102  if (checkCount == 0) {
103  wxMessageBox("No parameters selected", "Fail", wxOK | wxCENTRE);
104  return;
105  }
106  if (grid != nullptr) {
107  sizer->Detach(grid);
108  this->RemoveChild(grid);
109  }
110  grid = new wxGrid(this, wxID_ANY, wxDefaultPosition, size);
111  grid->EnableEditing(false);
112  sizer->Add(grid);
113  grid->CreateGrid(countSpinner->GetValue(), checkCount);
114  this->Layout();
115  saveButton->Enable(true);
116 
117  Config config;
118  config.moonLimit = limitSpinner->getValue();
119  config.radiiLimit = radiiSpinner->getValue();
120  this->update(this->storage, config);
121  });
122 
123  saveButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) {
124  Optional<Path> path = doSaveFileDialog("Save to file", { { "Text file", "txt" } });
125  if (path) {
126  std::ofstream ofs(path->native());
127  ofs << "#";
128  for (int col = 0; col < grid->GetNumberCols(); ++col) {
129  const wxString label = grid->GetColLabelValue(col);
130  ofs << std::string(max(1, 26 - int(label.size())), ' ') << label;
131  }
132  ofs << "\n";
133  for (int row = 0; row < grid->GetNumberRows(); ++row) {
134  ofs << " ";
135  for (int col = 0; col < grid->GetNumberCols(); ++col) {
136  ofs << std::setw(25) << grid->GetCellValue(row, col) << " ";
137  }
138  ofs << "\n";
139  }
140  }
141  });
142 }
143 
145  if (thread.joinable()) {
146  thread.join();
147  }
148 }
149 
151 private:
152  const Storage& storage;
153  Array<Size> indices;
154  Size maxIdx;
155 
156  mutable struct {
160  } lazy;
161 
162 public:
163  explicit ComponentGetter(const Storage& storage)
164  : storage(storage) {
165  const Flags<Post::ComponentFlag> flags =
167  maxIdx = Post::findComponents(storage, 2._f, flags, indices);
168  }
169 
170  Storage getComponent(const Size idx) {
171  Storage component = storage.clone(VisitorEnum::ALL_BUFFERS);
172  Array<Size> toRemove;
173  for (Size i = 0; i < indices.size(); ++i) {
174  if (indices[i] != idx) {
175  toRemove.push(i);
176  }
177  }
178  component.remove(toRemove, Storage::IndicesFlag::INDICES_SORTED);
179  return component;
180  }
181 
183  if (lazy.masses.empty()) {
184  this->computeIntegrals();
185  }
186  return lazy.masses;
187  }
188 
190  if (lazy.positions.empty()) {
191  this->computeIntegrals();
192  }
193  return lazy.positions;
194  }
195 
197  if (lazy.velocities.empty()) {
198  this->computeIntegrals();
199  }
200  return lazy.velocities;
201  }
202 
203 private:
204  void computeIntegrals() const {
205  lazy.masses.resizeAndSet(maxIdx, 0._f);
206  lazy.positions.resizeAndSet(maxIdx, Vector(0._f));
207  lazy.velocities.resizeAndSet(maxIdx, Vector(0._f));
208 
209  Array<Float> radii(maxIdx);
210  radii.fill(0._f);
211 
215  for (Size i = 0; i < indices.size(); ++i) {
216  lazy.masses[indices[i]] += m[i];
217  lazy.positions[indices[i]] += m[i] * r[i];
218  lazy.velocities[indices[i]] += m[i] * v[i];
219  radii[indices[i]] += pow<3>(r[i][H]);
220  }
221  for (Size k = 0; k < maxIdx; ++k) {
222  lazy.positions[k] /= lazy.masses[k];
223  lazy.positions[k][H] = cbrt(radii[k]);
224  lazy.velocities[k] /= lazy.masses[k];
225  }
226  }
227 };
228 
229 static Pair<Float> getMassAndDiameter(const Storage& storage) {
230  if (!storage.has(QuantityId::DENSITY)) {
231  return { 0._f, 0._f }; // some fallback?
232  }
233  ArrayView<const Float> m, rho;
235 
236  Float mass = 0._f;
237  Float diameter = 0._f;
238  for (Size i = 0; i < m.size(); ++i) {
239  mass += m[i];
240  diameter += m[i] / rho[i];
241  }
242  diameter = cbrt(3._f * diameter / (4._f * PI)) * 2._f;
243  return { mass, diameter };
244 }
245 
246 static Pair<Float> getSemiaxisRatios(const Storage& storage) {
249  const SymmetricTensor I = Post::getInertiaTensor(m, r);
250  const Eigen e = eigenDecomposition(I);
251  const Float A = e.values[0];
252  const Float B = e.values[1];
253  const Float C = e.values[2];
254  const Float a = sqrt(B + C - A);
255  const Float b = sqrt(A + C - B);
256  const Float c = sqrt(A + B - C);
257  SPH_ASSERT(a > 0._f && b > 0._f && c > 0._f, a, b, c);
258  return { c / b, b / a };
259 }
260 
261 static Float getSphericity(const Storage& storage) {
263  return Post::getSphericity(*scheduler, storage, 0.02_f);
264 }
265 
266 static Float getVelocityDifference(const Storage& s1, const Storage& s2) {
269 
270  Float mtot1 = 0._f;
271  Vector p1(0._f);
272  for (Size i = 0; i < m1.size(); ++i) {
273  mtot1 += m1[i];
274  p1 += m1[i] * v1[i];
275  }
276 
279 
280  Float mtot2 = 0._f;
281  Vector p2(0._f);
282  for (Size i = 0; i < m2.size(); ++i) {
283  mtot2 += m2[i];
284  p2 += m2[i] * v2[i];
285  }
286 
287  return getLength(p1 / mtot1 - p2 / mtot2);
288 }
289 
290 static Float getPeriod(const Storage& storage) {
292  ArrayView<const Vector> r, v, dv;
293  tie(r, v, dv) = storage.getAll<Vector>(QuantityId::POSITION);
294  const Float omega = getLength(Post::getAngularFrequency(m, r, v));
295  if (omega == 0._f) {
296  return 0._f;
297  } else {
298  return 2._f * PI / (3600._f * omega);
299  }
300 }
301 
302 static Size getMoons(ArrayView<const Float> m,
305  const Size idx,
306  const Float limit,
307  const Float radius) {
308  return Post::findMoonCount(m, r, v, idx, radius, limit);
309 }
310 
311 void GridPage::update(const Storage& storage, const Config& config) {
312  if (thread.joinable()) {
313  wxMessageBox("Computation in progress", "Fail", wxOK | wxCENTRE);
314  return;
315  }
316  Size colIdx = 0;
317  Flags<CheckFlag> checks;
318  for (Size i = 0; i < CHECK_COUNT; ++i) {
319  const CheckFlag flag = CheckFlag(1 << i);
320  wxCheckBox* box = this->getCheck(flag);
321  if (box->GetValue()) {
322  grid->SetColLabelValue(colIdx++, box->GetLabel());
323  checks.set(flag);
324  }
325  }
326  grid->AutoSize();
327 
328  const Size fragmentCnt = countSpinner->GetValue();
329 
330  thread = std::thread([this, &storage, fragmentCnt, checks, config] {
331  try {
332  this->updateAsync(storage, fragmentCnt, checks, config);
333  } catch (const std::exception& e) {
334  executeOnMainThread([message = std::string(e.what())] {
335  wxMessageBox("Failed to compute fragment parameters.\n\n" + message, "Fail", wxOK | wxCENTRE);
336  });
337  }
338  });
339 }
340 
341 void GridPage::updateAsync(const Storage& storage,
342  const Size fragmentCnt,
343  const Flags<CheckFlag> checks,
344  const Config& config) {
346  const Float totalMass = std::accumulate(m.begin(), m.end(), 0._f);
347 
348  ComponentGetter getter(storage);
349 
350  Storage lr;
351  for (Size i = 0; i < fragmentCnt; ++i) {
352  const Storage fragment = getter.getComponent(i);
353  if (fragment.getParticleCnt() < 2) {
354  break;
355  }
356  if (i == 0) {
357  lr = fragment.clone(VisitorEnum::ALL_BUFFERS);
358  }
359 
360  // order must match the values of CheckFlag
361  Size colIdx = 0;
362  if (checks.has(CheckFlag::PARTICLE_COUNT)) {
363  this->updateCell(i, colIdx++, fragment.getParticleCnt());
364  }
365 
367  const Pair<Float> massAndDiameter = getMassAndDiameter(fragment);
368  if (checks.has(CheckFlag::MASS_FRACTION)) {
369  this->updateCell(i, colIdx++, massAndDiameter[0] / totalMass);
370  }
371  if (checks.has(CheckFlag::DIAMETER)) {
372  this->updateCell(i, colIdx++, massAndDiameter[1] / 1.e3_f);
373  }
374  }
375 
376  if (checks.has(CheckFlag::VELOCITY_DIFFERENCE)) {
377  this->updateCell(i, colIdx++, getVelocityDifference(fragment, lr));
378  }
379 
380  if (checks.has(CheckFlag::PERIOD)) {
381  const Float period = getPeriod(fragment);
382  this->updateCell(i, colIdx++, period);
383  }
384 
386  const Pair<Float> ratios = getSemiaxisRatios(fragment);
387  if (checks.has(CheckFlag::RATIO_CB)) {
388  this->updateCell(i, colIdx++, ratios[0]);
389  }
390  if (checks.has(CheckFlag::RATIO_BA)) {
391  this->updateCell(i, colIdx++, ratios[1]);
392  }
393  }
394 
395  if (checks.has(CheckFlag::SPHERICITY)) {
396  this->updateCell(i, colIdx++, getSphericity(fragment));
397  }
398 
399  if (checks.has(CheckFlag::MOONS)) {
400  ArrayView<const Float> masses = getter.getMasses();
401  ArrayView<const Vector> positions = getter.getPositions();
402  ArrayView<const Vector> velocities = getter.getVelocities();
403  const Size count =
404  getMoons(masses, positions, velocities, i, config.moonLimit, config.radiiLimit);
405  this->updateCell(i, colIdx++, count);
406  }
407 
408  executeOnMainThread([this] { grid->AutoSize(); });
409  }
410 
411  executeOnMainThread([this] { thread.join(); });
412 }
413 
414 template <typename T>
415 void GridPage::updateCell(const Size rowIdx, const Size colIdx, const T& value) {
416  executeOnMainThread([this, rowIdx, colIdx, value] { //
417  grid->SetCellValue(rowIdx, colIdx, std::to_string(value));
418  });
419 }
420 
421 wxCheckBox* GridPage::getCheck(const CheckFlag check) const {
422  wxCheckBox* box = dynamic_cast<wxCheckBox*>(this->FindWindow(int(check)));
423  SPH_ASSERT(box);
424  return box;
425 }
426 
427 Size GridPage::getCheckedCount() const {
428  Size count = 0;
429  const StaticArray<CheckFlag, CHECK_COUNT> allIds = {
439  };
440  SPH_ASSERT(allIds.size() == CHECK_COUNT);
441 
442  for (CheckFlag id : allIds) {
443  count += int(this->getCheck(id)->GetValue());
444  }
445  return count;
446 }
447 
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
const float radius
Definition: CurveDialog.cpp:18
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
const Size CHECK_COUNT
Definition: GridPage.cpp:39
CheckFlag
Definition: GridPage.cpp:28
@ VELOCITY_DIFFERENCE
NAMESPACE_SPH_BEGIN void executeOnMainThread(const Function< void()> &function)
Posts a callback to be executed on main thread.
Definition: MainLoop.cpp:8
Posting events to be executed on main thread.
constexpr INLINE T max(const T &f1, const T &f2)
Definition: MathBasic.h:20
INLINE Float cbrt(const Float f)
Returns a cubed root of a value.
Definition: MathUtils.h:84
INLINE T sqrt(const T f)
Return a squared root of a value.
Definition: MathUtils.h:78
constexpr INLINE Float pow< 3 >(const Float v)
Definition: MathUtils.h:132
constexpr Float PI
Mathematical constants.
Definition: MathUtils.h:361
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
Object representing a path on a filesystem, similar to std::filesystem::path in c++17.
@ POSITION
Positions (velocities, accelerations) of particles, always a vector quantity,.
@ DENSITY
Density, always a scalar quantity.
@ MASS
Paricles masses, always a scalar quantity.
Holder of quantity values and their temporal derivatives.
Interface for executing tasks (potentially) asynchronously.
StaticArray< T0 &, sizeof...(TArgs)+1 > tie(T0 &t0, TArgs &... rest)
Creates a static array from a list of l-value references.
Definition: StaticArray.h:281
Eigen eigenDecomposition(const SymmetricTensor &t)
Computes eigenvectors and corresponding eigenvalues of symmetric matrix.
Optional< Path > doSaveFileDialog(const std::string &title, Array< FileFormat > &&formats)
Definition: Utils.cpp:56
Random utility functions for drawing stuff to DC.
INLINE Float getLength(const Vector &v)
Returns the length of the vector. Enabled only for vectors of floating-point precision.
Definition: Vector.h:579
BasicVector< Float > Vector
Definition: Vector.h:539
@ H
Definition: Vector.h:25
INLINE TCounter size() const
Definition: ArrayView.h:101
INLINE Iterator< StorageType > begin()
Definition: ArrayView.h:55
INLINE Iterator< StorageType > end()
Definition: ArrayView.h:63
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
INLINE TCounter size() const noexcept
Definition: Array.h:193
Storage getComponent(const Size idx)
Definition: GridPage.cpp:170
Array< Float > masses
Definition: GridPage.cpp:157
ComponentGetter(const Storage &storage)
Definition: GridPage.cpp:163
Array< Vector > positions
Definition: GridPage.cpp:158
ArrayView< const Vector > getVelocities() const
Definition: GridPage.cpp:196
Array< Vector > velocities
Definition: GridPage.cpp:159
ArrayView< const Float > getMasses() const
Definition: GridPage.cpp:182
ArrayView< const Vector > getPositions() const
Definition: GridPage.cpp:189
Provides functionality for reading and writing configuration files.
Definition: Config.h:272
constexpr INLINE bool has(const TEnum flag) const
Checks if the object has a given flag.
Definition: Flags.h:77
constexpr INLINE bool hasAny(const TEnum flag, const TArgs... others) const
Checks if the object has any of given flags.
Definition: Flags.h:83
INLINE void set(const TEnum flag)
Adds a single flag into the object. All previously stored flags are kept unchanged.
Definition: Flags.h:94
double getValue() const
Definition: Widgets.h:21
GridPage(wxWindow *parent, const wxSize size, const Storage &storage)
Definition: GridPage.cpp:41
Wrapper of type value of which may or may not be present.
Definition: Optional.h:23
static const Settings & getDefaults()
\brief Returns a reference to object containing default values of all settings.
Array with fixed number of allocated elements.
Definition: StaticArray.h:19
INLINE TCounter size() const
Returns the current size of the array (number of constructed elements).
Definition: StaticArray.h:147
Container storing all quantities used within the simulations.
Definition: Storage.h:230
StaticArray< Array< TValue > &, 3 > getAll(const QuantityId key)
Retrieves quantity buffers from the storage, given its key and value type.
Definition: Storage.cpp:163
Array< TValue > & getDt(const QuantityId key)
Retrieves a quantity derivative from the storage, given its key and value type.
Definition: Storage.cpp:217
Size getParticleCnt() const
Returns the number of particles.
Definition: Storage.cpp:445
auto getValues(const QuantityId first, const QuantityId second, const TArgs... others)
Retrieves an array of quantities from the key.
Definition: Storage.h:359
@ INDICES_SORTED
Use if the given array is already sorted (optimization)
void remove(ArrayView< const Size > idxs, const Flags< IndicesFlag > flags=EMPTY_FLAGS)
Removes specified particles from the storage.
Definition: Storage.cpp:756
bool has(const QuantityId key) const
Checks if the storage contains quantity with given key.
Definition: Storage.cpp:130
Storage clone(const Flags< VisitorEnum > buffers) const
Clones specified buffers of the storage.
Definition: Storage.cpp:563
Array< TValue > & getValue(const QuantityId key)
Retrieves a quantity values from the storage, given its key and value type.
Definition: Storage.cpp:191
Symmetric tensor of 2nd order.
Creating code components based on values from settings.
@ VELOCITY_DIFFERENCE
Signal speed given by relative velocity projected to the positive vector, as in Valdarnini (2018),...
SharedPtr< IScheduler > getScheduler(const RunSettings &settings=RunSettings::getDefaults())
Definition: Factory.cpp:178
Vector getAngularFrequency(ArrayView< const Float > m, ArrayView< const Vector > r, ArrayView< const Vector > v, const Vector &r0, const Vector &v0, ArrayView< const Size > idxs=nullptr)
Computes the immediate vector of angular frequency of a rigid body.
Definition: Analysis.cpp:514
@ OVERLAP
Specifies that overlapping particles belong into the same component.
Size findComponents(const Storage &storage, const Float particleRadius, const Flags< ComponentFlag > flags, Array< Size > &indices)
Finds and marks connected components (a.k.a. separated bodies) in the array of vertices.
Definition: Analysis.cpp:86
SymmetricTensor getInertiaTensor(ArrayView< const Float > m, ArrayView< const Vector > r, const Vector &r0, ArrayView< const Size > idxs=nullptr)
Computes the total inertia tensor of particles with respect to given center.
Definition: Analysis.cpp:484
Float getSphericity(IScheduler &scheduler, const Storage &strorage, const Float resolution=0.05_f)
Computes the sphericity coefficient of a body.
Definition: Analysis.cpp:550
Size findMoonCount(ArrayView< const Float > m, ArrayView< const Vector > r, ArrayView< const Vector > v, const Size i, const Float radius=1._f, const Float limit=0._f)
Find the number of moons of given body.
Definition: Analysis.cpp:415
Vector values
Eigenvalues.