1 /// Just a cluster of auxiliary tools, used for analysis of the result.
2 ///
3 /// Currently fixed for my machine, sorry :(
4
5 #include "io/FileManager.h"
6 #include "io/FileSystem.h"
7 #include "io/Logger.h"
8 #include "io/Output.h"
9 #include "objects/Exceptions.h"
10 #include "objects/containers/ArrayRef.h"
11 #include "physics/Functions.h"
12 #include "post/Analysis.h"
13 #include "post/StatisticTests.h"
14 #include "quantities/Quantity.h"
15 #include "quantities/Storage.h"
16 #include "quantities/Utility.h"
17 #include "sph/initial/Initial.h"
18 #include "system/Factory.h"
19 #include "system/Process.h"
20 #include "system/Statistics.h"
21 #include "thread/Pool.h"
22 #include <fstream>
23 #include <iostream>
24 #include <mutex>
25
26 using namespace Sph;
27
parsePkdgravOutput(const Path & path)28 static Expected<Storage> parsePkdgravOutput(const Path& path) {
29 Storage storage;
30 Statistics stats;
31 PkdgravInput io;
32 Outcome result = io.load(path, storage, stats);
33 if (!result) {
34 return makeUnexpected<Storage>(result.error());
35 } else {
36 return storage;
37 }
38 }
39
pkdgravToSfd(const Path & filePath,const Path & sfdPath)40 int pkdgravToSfd(const Path& filePath, const Path& sfdPath) {
41 std::cout << "Processing pkdgrav file ... " << std::endl;
42 Expected<Storage> storage = parsePkdgravOutput(filePath);
43 if (!storage) {
44 std::wcout << "Invalid file: " << storage.error() << std::endl;
45 return 0;
46 }
47 Post::HistogramParams params;
48 Array<Post::HistPoint> sfd = Post::getCumulativeHistogram(
49 storage.value(), Post::HistogramId::EQUIVALENT_MASS_RADII, Post::HistogramSource::PARTICLES, params);
50 FileLogger logRadiiSfd(sfdPath, EMPTY_FLAGS);
51 for (Post::HistPoint& p : sfd) {
52 logRadiiSfd.write(p.value, " ", p.count);
53 }
54 return 0;
55 }
56
pkdgravToOmega(const Path & filePath,const Path & omegaPath)57 int pkdgravToOmega(const Path& filePath, const Path& omegaPath) {
58 std::cout << "Processing pkdgrav file ... " << std::endl;
59 Expected<Storage> storage = parsePkdgravOutput(filePath);
60 if (!storage) {
61 std::wcout << "Invalid file: " << storage.error() << std::endl;
62 return 0;
63 }
64 /*Post::HistogramParams params;
65 params.source = Post::HistogramParams::Source::PARTICLES;
66 params.id = Post::HistogramId::ANGULAR_VELOCITIES;
67 params.binCnt = 50;
68 params.validator = [](const Float value) { return value > 0._f; };*/
69
70 // Array<Post::SfdPoint> sfd = Post::getDifferentialSfd(storage.value(), params);
71 FileLogger logOmegaSfd(omegaPath, EMPTY_FLAGS);
72 /*for (Post::SfdPoint& p : sfd) {
73 logOmegaSfd.write(p.value, " ", p.count);
74 }*/
75 ArrayView<Vector> omega = storage->getValue<Vector>(QuantityId::ANGULAR_FREQUENCY);
76 std::sort(
77 omega.begin(), omega.end(), [](Vector& v1, Vector& v2) { return getLength(v1) > getLength(v2); });
78 for (Vector v : omega) {
79 if (getLength(v) != 0._f) {
80 logOmegaSfd.write(getLength(v));
81 }
82 }
83 return 0;
84 }
85
pkdgravToMoons(const Path & filePath,const Float limit)86 int pkdgravToMoons(const Path& filePath, const Float limit) {
87 std::cout << "Processing pkdgrav file ... " << std::endl;
88 Expected<Storage> storage = parsePkdgravOutput(filePath);
89 if (!storage) {
90 std::cout << "Invalid file: " << storage.error() << std::endl;
91 return 0;
92 }
93 /// \todo use correct radius here, we assume that very close ecounters will eventually collide
94 Array<Post::MoonEnum> moons = Post::findMoons(storage.value(), 1.2_f, limit);
95 Size moonCnt = std::count(moons.begin(), moons.end(), Post::MoonEnum::MOON);
96 std::cout << "Moon count = " << moonCnt << std::endl;
97 return 0;
98 }
99
ssfToSfd(const Post::HistogramSource source,const Path & filePath,const Path & sfdPath)100 int ssfToSfd(const Post::HistogramSource source, const Path& filePath, const Path& sfdPath) {
101 std::cout << "Processing SPH file ... " << std::endl;
102 AutoPtr<IInput> input = Factory::getInput(filePath);
103 Storage storage;
104 Statistics stats;
105 Outcome outcome = input->load(filePath, storage, stats);
106 if (!outcome) {
107 std::cout << "Cannot load particle data, " << outcome.error() << std::endl;
108 return 0;
109 }
110
111 Post::HistogramParams params;
112 Array<Post::HistPoint> sfd =
113 Post::getCumulativeHistogram(storage, Post::HistogramId::EQUIVALENT_MASS_RADII, source, params);
114 FileLogger logSfd(sfdPath, EMPTY_FLAGS);
115 for (Post::HistPoint& p : sfd) {
116 logSfd.write(p.value, " ", p.count);
117 }
118 return 0;
119 }
120
ssfToOmega(const Path & filePath,const Path & omegaPath,const Path & omegaDPath,const Path & omegaDirPath)121 int ssfToOmega(const Path& filePath,
122 const Path& omegaPath,
123 const Path& omegaDPath,
124 const Path& omegaDirPath) {
125 std::cout << "Processing SPH file ... " << std::endl;
126 BinaryInput input;
127 Storage storage;
128 Statistics stats;
129 Outcome outcome = input.load(filePath, storage, stats);
130 if (!outcome) {
131 std::cout << "Cannot load particle data, " << outcome.error() << std::endl;
132 return 0;
133 }
134
135 Post::HistogramParams params;
136 params.range = Interval(0._f, 13._f);
137 params.binCnt = 12;
138
139 ArrayView<const Vector> w = storage.getValue<Vector>(QuantityId::ANGULAR_FREQUENCY);
140 ArrayView<const Float> m = storage.getValue<Float>(QuantityId::MASS);
141 // const Float massCutoff = 1._f / 300000._f;
142 const Float m_total = std::accumulate(m.begin(), m.end(), 0._f);
143 params.validator = [w](const Size i) { //
144 return getSqrLength(w[i]) > 0._f; //= m_total * massCutoff;
145 };
146
147 params.centerBins = false;
148
149 Array<Post::HistPoint> sfd = Post::getDifferentialHistogram(
150 storage, Post::HistogramId::ROTATIONAL_FREQUENCY, Post::HistogramSource::PARTICLES, params);
151
152 FileLogger logOmega(omegaPath, EMPTY_FLAGS);
153 for (Post::HistPoint& p : sfd) {
154 logOmega.write(p.value, " ", p.count); // / sum);
155 }
156
157 params.range = Interval();
158 Array<Post::HistPoint> dirs = Post::getDifferentialHistogram(
159 storage, Post::HistogramId::ROTATIONAL_AXIS, Post::HistogramSource::PARTICLES, params);
160
161 FileLogger logOmegaDir(omegaDirPath, EMPTY_FLAGS);
162 for (Post::HistPoint& p : dirs) {
163 logOmegaDir.write(p.value, " ", p.count);
164 }
165
166 // print omega-D relation
167 Array<Float> h(storage.getParticleCnt());
168 /// ArrayView<const Float> rho = storage.getValue<Float>(QuantityId::DENSITY);
169 ArrayView<const Vector> r = storage.getValue<Vector>(QuantityId::POSITION);
170 for (Size i = 0; i < m.size(); ++i) {
171 h[i] = r[i][H]; // root<3>(3.f * m[i] / (rho[i] * 4.f * PI));
172 }
173
174
175 FileLogger logOmegaD(omegaDPath, EMPTY_FLAGS);
176 for (Size i = 0; i < m.size(); ++i) {
177 if (m[i] > 3._f * params.massCutoff * m_total) {
178 // in m vs. rev/day
179 logOmegaD.write(2._f * h[i], " ", getLength(w[i]) * 3600._f * 24._f / (2._f * PI));
180 }
181 }
182
183 return 0;
184 }
185
186
ssfToVelocity(const Path & filePath,const Path & outPath)187 int ssfToVelocity(const Path& filePath, const Path& outPath) {
188 std::cout << "Processing SPH file ... " << std::endl;
189 AutoPtr<IInput> input = Factory::getInput(filePath);
190 Storage storage;
191 Statistics stats;
192 Outcome outcome = input->load(filePath, storage, stats);
193 if (!outcome) {
194 std::cout << "Cannot load particle data, " << outcome.error() << std::endl;
195 return -1;
196 }
197
198 // convert to system with center at LR
199 Array<Size> idxs = Post::findLargestComponent(storage, 2._f, EMPTY_FLAGS);
200 ArrayView<const Float> m = storage.getValue<Float>(QuantityId::MASS);
201 ArrayView<Vector> r, v, dv;
202 tie(r, v, dv) = storage.getAll<Vector>(QuantityId::POSITION);
203 Vector r0(0._f);
204 Vector v0(0._f);
205 Float m0 = 0._f;
206 for (Size i : idxs) {
207 m0 += m[i];
208 r0 += m[i] * r[i];
209 v0 += m[i] * v[i];
210 }
211 r0 /= m0;
212 v0 /= m0;
213
214 for (Size i = 0; i < r.size(); ++i) {
215 r[i] -= r0;
216 v[i] -= v0;
217 }
218
219 Post::HistogramParams params;
220 params.binCnt = 2000;
221 Array<Post::HistPoint> hist = Post::getDifferentialHistogram(
222 storage, Post::HistogramId::VELOCITIES, Post::HistogramSource::COMPONENTS, params);
223
224 FileLogger logSfd(outPath, EMPTY_FLAGS);
225 for (Post::HistPoint& p : hist) {
226 logSfd.write(p.value, " ", p.count);
227 }
228
229 return 0;
230 }
231
ssfToVelDir(const Path & filePath,const Path & outPath)232 void ssfToVelDir(const Path& filePath, const Path& outPath) {
233 std::cout << "Processing SPH file ... " << std::endl;
234 BinaryInput input;
235 Storage storage;
236 Statistics stats;
237 Outcome outcome = input.load(filePath, storage, stats);
238 if (!outcome) {
239 std::cout << "Cannot load particle data, " << outcome.error() << std::endl;
240 return;
241 }
242
243 // convert to system with center at LR
244 Array<Size> idxs = Post::findLargestComponent(storage, 2._f, EMPTY_FLAGS);
245 ArrayView<const Float> m = storage.getValue<Float>(QuantityId::MASS);
246 ArrayView<Vector> r, v, dv;
247 tie(r, v, dv) = storage.getAll<Vector>(QuantityId::POSITION);
248 Vector r0(0._f);
249 Vector v0(0._f);
250 Float m0 = 0._f;
251 for (Size i : idxs) {
252 m0 += m[i];
253 r0 += m[i] * r[i];
254 v0 += m[i] * v[i];
255 }
256 r0 /= m0;
257 v0 /= m0;
258
259 for (Size i = 0; i < r.size(); ++i) {
260 r[i] -= r0;
261 v[i] -= v0;
262 }
263
264 // compute velocity directions (in x-y plane)
265 Array<Float> dirs;
266 for (Size i = 0; i < v.size(); ++i) {
267 Float dir = Sph::atan2(v[i][Y], v[i][X]);
268 if (dir < 0._f) {
269 dir += 2._f * PI;
270 }
271 dirs.push(dir * RAD_TO_DEG);
272 }
273
274 Post::HistogramParams params;
275 params.range = Interval(0._f, 360._f);
276 params.binCnt = 72; // 5 deg bins
277 Array<Post::HistPoint> hist = Post::getDifferentialHistogram(dirs, params);
278 FileLogger logSfd(outPath, EMPTY_FLAGS);
279 for (Post::HistPoint& p : hist) {
280 logSfd.write(p.value, " ", p.count);
281 }
282 }
283
284 struct HarrisAsteroid {
285 Optional<Size> number;
286 String name;
287 Optional<Float> radius;
288 Optional<Float> period;
289 };
290
loadHarris(std::wifstream & ifs)291 static Array<HarrisAsteroid> loadHarris(std::wifstream& ifs) {
292 Array<HarrisAsteroid> harris;
293 while (ifs) {
294 String dummy;
295 String number;
296 ifs >> number >> dummy;
297
298 String name;
299 ifs >> name;
300 for (int i = 0; i < 6; ++i) {
301 ifs >> dummy;
302 }
303
304 String radius;
305 ifs >> radius;
306 for (int i = 0; i < 5; ++i) {
307 ifs >> dummy;
308 }
309
310 String period;
311 ifs >> period;
312 for (int i = 0; i < 10; ++i) {
313 ifs >> dummy;
314 }
315
316 harris.push(HarrisAsteroid{
317 fromString<Size>(number),
318 name,
319 fromString<Float>(radius),
320 fromString<Float>(period),
321 });
322 }
323 return harris;
324 }
325
326 struct FamilyAsteroid {
327 Optional<Size> number;
328 Optional<String> name;
329 };
330
loadFamilies(std::wifstream & ifs)331 static Array<FamilyAsteroid> loadFamilies(std::wifstream& ifs) {
332 SPH_ASSERT(ifs);
333 Array<FamilyAsteroid> asteroids;
334 std::wstring line;
335 bool firstLine = true;
336 int format = 1;
337 while (std::getline(ifs, line)) {
338 if (line.empty() || line[0] == '#') {
339 // comment line
340 if (firstLine) {
341 // this is the other format of the file, with asteroid names, etc.
342 format = 2;
343 }
344 firstLine = false;
345 continue;
346 }
347 firstLine = false;
348 std::wstringstream ss(line);
349 // both formats start with asteroid number
350 String number;
351 ss >> number;
352 if (format == 2) {
353 String name;
354 ss >> name;
355 asteroids.push(FamilyAsteroid{ fromString<Size>(number), name });
356 } else {
357 asteroids.push(FamilyAsteroid{ fromString<Size>(number), NOTHING });
358 }
359 // check that we have at least one information
360 SPH_ASSERT(asteroids.back().name || asteroids.back().number);
361 }
362 return asteroids;
363 }
364
findInHarris(const FamilyAsteroid & ast1,ArrayView<const HarrisAsteroid> catalog)365 static Optional<HarrisAsteroid> findInHarris(const FamilyAsteroid& ast1,
366 ArrayView<const HarrisAsteroid> catalog) {
367 auto iter = std::find_if(catalog.begin(), catalog.end(), [&ast1](const HarrisAsteroid& ast2) { //
368 // search primarily by number
369 if (ast1.number && ast2.number && ast1.number.value() == ast2.number.value()) {
370 return true;
371 }
372 // if we don't have the number, search by name
373 if (ast1.name && ast1.name.value() == ast2.name) {
374 return true;
375 }
376 // either don't match or we don't have the information
377 return false;
378 });
379 if (iter == catalog.end()) {
380 return NOTHING;
381 } else {
382 if (iter->period && iter->radius) {
383 return *iter;
384 } else {
385 return NOTHING;
386 }
387 }
388 }
389
printDvsOmega(const Path & familyData,const Path & outputPath,ArrayView<const HarrisAsteroid> catalog,Array<PlotPoint> & outPoints)390 static Optional<Post::KsResult> printDvsOmega(const Path& familyData,
391 const Path& outputPath,
392 ArrayView<const HarrisAsteroid> catalog,
393 Array<PlotPoint>& outPoints) {
394
395 std::wifstream ifs(familyData.native());
396 SPH_ASSERT(ifs);
397 Array<FamilyAsteroid> family = loadFamilies(ifs);
398 Array<HarrisAsteroid> found;
399 Interval rangePeriod;
400 Interval rangeR;
401 for (FamilyAsteroid& famAst : family) {
402 if (auto harAst = findInHarris(famAst, catalog)) {
403 found.push(harAst.value());
404 rangePeriod.extend(harAst->period.value());
405 rangeR.extend(harAst->radius.value());
406 }
407 }
408 if (found.size() < 10) {
409 // too few data to do any statistics
410 return NOTHING;
411 }
412
413 FileSystem::createDirectory(outputPath.parentPath());
414 std::wofstream ofs(outputPath.native());
415 SPH_ASSERT(ofs);
416
417 auto compare = [](HarrisAsteroid& ast1, HarrisAsteroid& ast2) {
418 return ast1.radius.value() < ast2.radius.value();
419 };
420 Iterator<HarrisAsteroid> largestRemnant = std::max_element(found.begin(), found.end(), compare);
421
422 auto periodToOmega = [](const Float P) { return 1._f / (P / 24._f); };
423
424 Array<PlotPoint> points;
425 for (HarrisAsteroid& ast : found) {
426 const Float omega = periodToOmega(ast.period.value());
427 if (&ast != &*largestRemnant) {
428 String printedName = ast.name;
429 if (ast.number) {
430 printedName = "(" + toString(ast.number.value()) + ") " + printedName;
431 }
432 ofs << ast.radius.value() << " " << omega << " " << printedName << std::endl;
433 }
434 points.push(PlotPoint(ast.radius.value(), omega));
435 }
436 Interval rangeOmega;
437 rangeOmega.extend(periodToOmega(rangePeriod.lower()));
438 rangeOmega.extend(periodToOmega(rangePeriod.upper()));
439
440 Post::KsFunction uniformCdf = Post::getUniformKsFunction(rangeR, rangeOmega);
441 Post::KsResult result = Post::kolmogorovSmirnovTest(points, uniformCdf);
442
443
444 if (points.size() > 36) {
445 const Path histPath = Path("histogram") / outputPath.fileName();
446 FileSystem::createDirectory(histPath.parentPath());
447 std::wofstream histofs(histPath.native());
448 Array<Float> values;
449 for (PlotPoint& p : points) {
450 values.push(p.y);
451 }
452 Post::HistogramParams params;
453 params.range = Interval(0._f, 13._f);
454 Array<Post::HistPoint> histogram = Post::getDifferentialHistogram(values, params);
455 for (Post::HistPoint p : histogram) {
456 histofs << p.value << " " << p.count << std::endl;
457 }
458 }
459
460 outPoints = std::move(points);
461 return result;
462 }
463
elliptize(const String & s,const Size maxSize)464 static String elliptize(const String& s, const Size maxSize) {
465 SPH_ASSERT(maxSize >= 5);
466 if (s.size() < maxSize) {
467 return s;
468 }
469 const Size cutSize = (maxSize - 3) / 2;
470 return s.substr(0, cutSize) + "..." + s.substr(s.size() - cutSize);
471 }
472
processHarrisFile()473 void processHarrisFile() {
474 Path harrisPath("/home/pavel/projects/astro/asteroids/grant3/harris.out");
475 std::wifstream ifs(harrisPath.native());
476 Array<HarrisAsteroid> harris = loadHarris(ifs);
477 ifs.close();
478
479 Size below3 = 0, below7 = 0, below12 = 0, total = 0;
480 Float avgPeriod = 0._f;
481 Size count = 0;
482 for (auto& h : harris) {
483 if (!h.period) {
484 continue;
485 }
486
487 if (h.period.value() < 3) {
488 below3++;
489 }
490 if (h.period.value() < 7) {
491 below7++;
492 }
493 if (h.period.value() < 12) {
494 below12++;
495 }
496
497 if (h.radius.valueOr(0._f) > 100._f) {
498 avgPeriod += h.period.value();
499 count++;
500 }
501 total++;
502 }
503 std::cout << "Below 3h: " << (100.f * below3) / total << "%" << std::endl;
504 std::cout << "Below 7h: " << (100.f * below7) / total << "%" << std::endl;
505 std::cout << "Below 12h: " << (100.f * below12) / total << "%" << std::endl;
506
507 if (count > 0) {
508 std::cout << "Average period for R>100km: " << avgPeriod / count << std::endl;
509 } else {
510 SPH_ASSERT(false);
511 }
512
513 Array<PlotPoint> points;
514 printDvsOmega(
515 Path("/home/pavel/projects/astro/asteroids/families.txt"), Path("LR_D_omega.txt"), harris, points);
516
517 const Path parentPath("/home/pavel/projects/astro/asteroids/families");
518 Array<Path> paths = FileSystem::getFilesInDirectory(parentPath);
519 std::mutex printMutex;
520 std::mutex plotMutex;
521 UniquePathManager uniquePaths;
522 std::ofstream kss("KS.txt");
523 kss << "# name D_ks probability" << std::endl;
524
525 std::ofstream alls("D_omega_all.txt");
526
527 ThreadPool& pool = *ThreadPool::getGlobalInstance();
528 parallelFor(pool, 0, paths.size(), [&](const Size index) {
529 {
530 std::unique_lock<std::mutex> lock(printMutex);
531 std::wcout << paths[index].string() << std::endl;
532 }
533 const Path name = paths[index].fileName();
534 const Path targetPath = Path("D_omega") / Path(name.string() + ".txt");
535 const Path ksPath = Path("KS") / Path(name.string() + ".txt");
536 Array<PlotPoint> points;
537 Optional<Post::KsResult> ks = printDvsOmega(parentPath / paths[index], targetPath, harris, points);
538
539 std::unique_lock<std::mutex> lock(plotMutex);
540 if (!FileSystem::pathExists(targetPath) || FileSystem::fileSize(targetPath) == 0) {
541 return;
542 }
543 if (ks) {
544 kss << std::left << std::setw(35) << elliptize(Path(name).removeExtension().string(), 30)
545 << std::setw(15) << ks->D << std::setw(15) << ks->prob << std::endl;
546 }
547 for (PlotPoint p : points) {
548 alls << p.x << " " << p.y << " " << index << std::endl;
549 }
550
551 FileSystem::copyFile(targetPath, Path("family.txt"));
552 // make plot
553 Process gnuplot(Path("/bin/gnuplot"), { "doplot.plt" });
554 gnuplot.wait();
555 SPH_ASSERT(FileSystem::pathExists(Path("plot.png")));
556 FileSystem::copyFile(Path("plot.png"), uniquePaths.getPath(Path(targetPath).replaceExtension("png")));
557
558 if (points.size() > 25) {
559 const Path histPath = Path("histogram") / targetPath.fileName();
560 FileSystem::copyFile(histPath, Path("hist.txt"));
561 Process gnuplot2(Path("/bin/gnuplot"), { "dohistogram.plt" });
562 gnuplot2.wait();
563 SPH_ASSERT(FileSystem::pathExists(Path("hist.png")));
564 FileSystem::copyFile(
565 Path("hist.png"), uniquePaths.getPath(Path(histPath).replaceExtension("png")));
566 }
567 });
568
569 FileSystem::copyFile(Path("D_omega_all.txt"), Path("family.txt"));
570 Process gnuplot(Path("/bin/gnuplot"), { "doplot_all.plt" });
571 gnuplot.wait();
572 }
573
maxwellBoltzmann(const Float x,const Float a)574 static Float maxwellBoltzmann(const Float x, const Float a) {
575 return sqrt(2._f / PI) * sqr(x) * exp(-sqr(x) / (2._f * sqr(a))) / pow<3>(a);
576 }
577
sampleMaxwellBoltzmann(UniformRng & rng,const Float a)578 static Float sampleMaxwellBoltzmann(UniformRng& rng, const Float a) {
579 while (true) {
580 const Float x = rng() * a * 10._f;
581 const Float y = rng() / a;
582
583 if (maxwellBoltzmann(x, a) > y) {
584 return x;
585 }
586 }
587 }
588
makeSwift(const Path & filePath)589 void makeSwift(const Path& filePath) {
590 // for Hygiea
591 /*const Float a = 3.14178_f;
592 const Float e = 0.135631_f;
593 const Float I = asin(0.0889622);
594 const Float W = 64.621768_f * DEG_TO_RAD;
595 const Float w = 128.543611_f * DEG_TO_RAD;
596 const Float u = 0._f;
597
598 const Float X = (cos(W) * cos(w) - sin(W) * cos(I) * sin(w)) * a * (cos(u) - e) -
599 (cos(W) * sin(w) + sin(W) * cos(I) * cos(w)) * a * sqrt(1 - sqr(e)) * sin(u);
600 const Float Y = (sin(W) * cos(w) + cos(W) * cos(I) * sin(w)) * a * (cos(u) - e) +
601 (-sin(W) * sin(w) + cos(W) * cos(I) * cos(w)) * a * sqrt(1 - sqr(e)) * sin(u);
602 const Float Z = sin(I) * sin(w) * a * (cos(u) - e) + sin(I) * cos(w) * a * sqrt(1 - sqr(e)) * sin(u);
603
604 const Vector r = Vector(X, Y, Z);
605
606 const Float npart = 1500;
607
608 BinaryInput input;
609 Storage storage;
610 Statistics stats;
611 if (!input.load(filePath, storage, stats)) {
612 std::cout << "Cannot parse ssf file" << std::endl;
613 }
614
615 ArrayView<const Vector> v = storage.getDt<Vector>(QuantityId::POSITION);
616
617 FileLogger logger(Path("tp.in"));
618 UniformRng rng;
619 logger.write(npart);
620 for (Size i = 0; i < npart; ++i) {
621 logger.write(r / Constants::au);
622 const Size idx = clamp<Size>(Size(rng() * v.size()), 0, storage.getParticleCnt() - 1);
623 logger.write(v[idx] / Constants::au * Constants::year);
624 logger.write("0");
625 logger.write("0.0");
626 }*/
627 std::wifstream ifs(filePath.native());
628 std::wstring line;
629
630 Array<Float> rs;
631 while (std::getline(ifs, line)) {
632 const Float d = std::stof(line);
633 rs.push(d / 2 * 1000);
634 }
635
636 std::ofstream yarko("yarko.in");
637 yarko << rs.size() << std::endl;
638 for (Float r : rs) {
639 yarko << r << " 2860.0 1500.0 0.0010 680.0 0.10 0.90" << std::endl;
640 }
641
642 std::ofstream spin("spin.in");
643 spin << rs.size() << "\n-1\n1\n";
644 UniformRng rng;
645 for (Size i = 0; i < rs.size(); ++i) {
646 const Float phi = rng() * 2._f * PI;
647 const Float cosTheta = rng() * 2._f - 1._f;
648 const Float theta = Sph::acos(cosTheta);
649 spin << sphericalToCartesian(1._f, theta, phi) << " " << sampleMaxwellBoltzmann(rng, 0.0001_f)
650 << std::endl;
651 }
652
653 std::ofstream yorp("yorp.in");
654 for (Size i = 1; i <= rs.size(); ++i) {
655 yorp << i << " " << clamp(int(rng() * 200), 0, 199) << std::endl;
656 }
657 }
658
origComponents(const Path & lastDumpPath,const Path & firstDumpPath,const Path & colorizedDumpPath)659 void origComponents(const Path& lastDumpPath, const Path& firstDumpPath, const Path& colorizedDumpPath) {
660 BinaryInput input;
661 Storage lastDump, firstDump;
662 Statistics stats;
663 Outcome res1 = input.load(lastDumpPath, lastDump, stats);
664 Outcome res2 = input.load(firstDumpPath, firstDump, stats);
665 if (!res1 || !res2) {
666 throw IoError((res1 || res2).error());
667 }
668
669 // use last dump to find components
670 Array<Size> components;
671 Post::findComponents(
672 lastDump, 2._f, Post::ComponentFlag::ESCAPE_VELOCITY | Post::ComponentFlag::SORT_BY_MASS, components);
673
674 // "colorize" the flag quantity using the components
675 SPH_ASSERT(firstDump.getParticleCnt() == components.size());
676 firstDump.getValue<Size>(QuantityId::FLAG) = components.clone();
677
678 // save as new file
679 BinaryOutput output(colorizedDumpPath);
680 output.dump(firstDump, stats);
681 }
682
extractLr(const Path & inputPath,const Path & outputPath)683 void extractLr(const Path& inputPath, const Path& outputPath) {
684 BinaryInput input;
685 Storage storage;
686 Statistics stats;
687 Outcome res = input.load(inputPath, storage, stats);
688 if (!res) {
689 throw IoError(res.error());
690 }
691
692 // allow using this for storage without masses --> add ad hoc mass if it's missing
693 if (!storage.has(QuantityId::MASS)) {
694 storage.insert<Float>(QuantityId::MASS, OrderEnum::ZERO, 1._f);
695 }
696
697 Array<Size> components;
698 const Size componentCnt =
699 Post::findComponents(storage, 1.5_f, Post::ComponentFlag::SORT_BY_MASS, components);
700 std::cout << "Component cnt = " << componentCnt << std::endl;
701
702 Array<Size> toRemove;
703 for (Size i = 0; i < components.size(); ++i) {
704 if (components[i] != 0) {
705 // not LR
706 toRemove.push(i);
707 }
708 }
709 storage.remove(toRemove, Storage::IndicesFlag::INDICES_SORTED);
710
711 moveToCenterOfMassFrame(storage);
712
713 BinaryOutput output(outputPath);
714 output.dump(storage, stats);
715
716 if (storage.has(QuantityId::DENSITY)) {
717 ArrayView<const Float> m = storage.getValue<Float>(QuantityId::MASS);
718 ArrayView<const Float> rho = storage.getValue<Float>(QuantityId::DENSITY);
719
720 Float volume = 0._f;
721 for (Size i = 0; i < m.size(); ++i) {
722 volume += m[i] / rho[i];
723 }
724
725 std::cout << "eq. diameter = " << Sph::cbrt(3._f * volume / (4._f * PI)) * 2._f / 1000._f << "km"
726 << std::endl;
727 }
728
729 ArrayView<const Float> m = storage.getValue<Float>(QuantityId::MASS);
730 ArrayView<Vector> r = storage.getValue<Vector>(QuantityId::POSITION);
731 ArrayView<Vector> v = storage.getDt<Vector>(QuantityId::POSITION);
732 const Float omega = getLength(Post::getAngularFrequency(m, r, v));
733 std::cout << "period = " << 2._f * PI / omega / 3600._f << "h" << std::endl;
734
735 SymmetricTensor I = Post::getInertiaTensor(m, r);
736 SPH_ASSERT(isReal(I), I);
737 Eigen e = eigenDecomposition(I);
738 /*std::cout << "I = " << I << std::endl;
739 std::cout << "matrix = " << e.vectors << std::endl;
740 std::cout << "values = " << e.values << std::endl;*/
741 const Float A = e.values[2];
742 const Float B = e.values[1];
743 const Float C = e.values[0];
744 const Float a = sqrt(B + C - A);
745 const Float b = sqrt(A + C - B);
746 const Float c = sqrt(A + B - C);
747 SPH_ASSERT(a > 0._f && b > 0._f && c > 0._f, a, b, c);
748 std::cout << "a/b = " << a / b << std::endl;
749 std::cout << "b/c = " << b / c << std::endl;
750 }
751
printHelp()752 void printHelp() {
753 std::cout << "Expected usage: post mode [parameters]" << std::endl
754 << " where 'mode' is one of:" << std::endl
755 << " - pkdgravToSfd - computes the cumulative SFD from pkdgrav output file" << std::endl
756 << " - pkdgravToOmega - computes the spin rate distribution from pkdgrav output file"
757 << std::endl
758 << " - pkdgravToMoons - finds satellites of the largest remnant (fragment) from pkdgrav "
759 "output file"
760 << std::endl
761 << "- ssfToSfd - computes the cumulative SFD from SPH output file" << std::endl
762 << "- ssfToVelocity - computes the velocity distribution from SPH output file" << std::endl
763 << "- harris - TODO" << std::endl
764 << "- stats - prints ejected mass and the period of the largest remnant" << std::endl
765 << "- swift - makes yarko.in, yorp.in and spin.in input file for swift" << std::endl;
766 }
767
main(int argc,char ** argv)768 int main(int argc, char** argv) {
769 if (argc < 2) {
770 printHelp();
771 return 0;
772 }
773 try {
774
775 String mode = String::fromAscii(argv[1]);
776 if (mode == "pkdgravToSfd") {
777 if (argc < 4) {
778 std::cout << "Expected parameters: post pkdgravToSfd ss.50000.bt sfd.txt";
779 return 0;
780 }
781 return pkdgravToSfd(Path(String::fromAscii(argv[2])), Path(String::fromAscii(argv[3])));
782 } else if (mode == "pkdgravToOmega") {
783 if (argc < 4) {
784 std::cout << "Expected parameters: post pkdgravToOmega ss.50000.bt omega.txt";
785 return 0;
786 }
787 return pkdgravToOmega(Path(String::fromAscii(argv[2])), Path(String::fromAscii(argv[3])));
788 } else if (mode == "pkdgravToMoons") {
789 if (argc < 4) {
790 std::cout << "Expected parameters: post pkdgravToMoons ss.50000.bt 0.1";
791 return 0;
792 }
793 const Float limit = std::atof(argv[3]);
794 return pkdgravToMoons(Path(String::fromAscii(argv[2])), limit);
795 } else if (mode == "ssfToSfd") {
796 if (argc < 4) {
797 std::cout << "Expected parameters: post ssfToSfd [--components] output.ssf sfd.txt";
798 return 0;
799 }
800 if (String(String::fromAscii(argv[2])) == "--components") {
801 return ssfToSfd(Post::HistogramSource::COMPONENTS,
802 Path(String::fromAscii(argv[3])),
803 Path(String::fromAscii(argv[4])));
804 } else {
805 return ssfToSfd(Post::HistogramSource::PARTICLES,
806 Path(String::fromAscii(argv[2])),
807 Path(String::fromAscii(argv[3])));
808 }
809 } else if (mode == "ssfToVelocity") {
810 return ssfToVelocity(Path(String::fromAscii(argv[2])), Path(String::fromAscii(argv[3])));
811 } else if (mode == "ssfToOmega") {
812 if (argc < 6) {
813 std::cout << "Expected parameters: post ssfToOmega output.ssf omega.txt omega_D.txt "
814 "omega_dir.txt";
815 return 0;
816 }
817 return ssfToOmega(Path(String::fromAscii(argv[2])),
818 Path(String::fromAscii(argv[3])),
819 Path(String::fromAscii(argv[4])),
820 Path(String::fromAscii(argv[5])));
821 } else if (mode == "ssfToVelDir") {
822 if (argc < 4) {
823 std::cout << "Expected parameters: post ssfToVelDir output.ssf veldir.txt" << std::endl;
824 return 0;
825 }
826 ssfToVelDir(Path(String::fromAscii(argv[2])), Path(String::fromAscii(argv[3])));
827 } else if (mode == "harris") {
828 processHarrisFile();
829 } else if (mode == "swift") {
830 if (argc < 3) {
831 std::cout << "Expected parameters: post maketp D.dat";
832 return 0;
833 }
834 makeSwift(Path(String::fromAscii(argv[2])));
835 } else if (mode == "origComponents") {
836 if (argc < 5) {
837 std::cout << "Expected parameters: post origComponents lastDump.ssf firstDump.ssf "
838 "colorizedDump.ssf"
839 << std::endl;
840 }
841 origComponents(Path(String::fromAscii(argv[2])),
842 Path(String::fromAscii(argv[3])),
843 Path(String::fromAscii(argv[4])));
844 } else if (mode == "extractLr") {
845 if (argc < 4) {
846 std::cout << "Expected parameters: post extractLr input.ssf lr.ssf" << std::endl;
847 }
848 extractLr(Path(String::fromAscii(argv[2])), Path(String::fromAscii(argv[3])));
849 } else {
850 printHelp();
851 return 0;
852 }
853
854 } catch (const std::exception& e) {
855 std::cout << "ERROR: " << e.what() << std::endl;
856 return -1;
857 }
858 }
859