SPH
Impact.cpp
Go to the documentation of this file.
1 
3 #include "Sph.h"
4 #include "run/Node.h"
6 #include "run/jobs/IoJobs.h"
10 
11 using namespace Sph;
12 
13 static Array<ArgDesc> params{
14  { "tr", "target-radius", ArgEnum::FLOAT, "Radius of the target [m]" },
15  { "tp", "target-period", ArgEnum::FLOAT, "Rotational period of the target [h]" },
16  { "ir", "impactor-radius", ArgEnum::FLOAT, "Radius of the impactor [m]" },
17  { "q",
18  "impact-energy",
20  "Relative impact energy Q/Q_D^*. This option can be only used together with -tr and -v, it is "
21  "incompatible with -ir." },
22  { "v", "impact-speed", ArgEnum::FLOAT, "Impact speed [km/s]" },
23  { "phi", "impact-angle", ArgEnum::FLOAT, "Impact angle [deg]" },
24  { "n", "particle-count", ArgEnum::INT, "Number of particles in the target" },
25  { "st", "stabilization-time", ArgEnum::FLOAT, "Duration of the stabilization phase [s]" },
26  { "ft", "fragmentation-time", ArgEnum::FLOAT, "Duration of the fragmentation phase [s]" },
27  { "rt", "reaccumulation-time", ArgEnum::FLOAT, "Duration of the reaccumulation phase [s]" },
28  { "i", "resume-from", ArgEnum::STRING, "Resume simulation from given state file" },
29  { "o",
30  "output-dir",
32  "Directory containing configuration files and run output files. If not specified, it is determined "
33  "from other arguments. If no arguments are specified, the current working directory is used." },
34 };
35 
36 static void printBanner(ILogger& logger) {
37  logger.write("*******************************************************************************");
38  logger.write("******************************* OpenSPH Impact ********************************");
39  logger.write("*******************************************************************************");
40 }
41 
42 static void printNoConfigsMsg(ILogger& logger, const Path& outputDir) {
43  logger.write("");
44  logger.write("No configuration files found, the program will generate default configuration ");
45  logger.write("files and save them to directory '" + outputDir.native() + "'");
46  logger.write("");
47  logger.write("To start a simulation, re-run this program; it will load the generated files. ");
48  logger.write("You can also specify parameters of the simulation as command-line arguments. ");
49  logger.write("Note that these arguments will override parameters loaded from configuration ");
50  logger.write("files. For more information, execute the program with -h (or --help) argument.");
51  logger.write("");
52 }
53 
54 template <typename TEnum>
55 static Settings<TEnum> loadSettings(const Path& path,
56  const Settings<TEnum>& defaults,
57  ILogger& logger,
58  bool& doDryRun) {
59  Settings<TEnum> settings = defaults;
60  const bool result = settings.tryLoadFileOrSaveCurrent(path);
61  if (result) {
62  logger.write("Loaded configuration file '" + path.native() + "'");
63 
64  // at least one configuration file exists, run the simulation
65  doDryRun = false;
66  } else {
67  logger.write("No file '" + path.native() + "' found, it has been created with default parameters");
68  }
69  return settings;
70 }
71 
72 struct RunParams {
79 
83 
86 
87 
89  std::string getOutputPath() const {
90  if (outputPath) {
91  return outputPath.value();
92  }
93 
94  std::stringstream ss;
95  ss << "sph_";
96  if (targetRadius) {
97  ss << round(targetRadius.value()) << "m_";
98  }
99  if (impactorRadius) {
100  ss << round(impactorRadius.value()) << "m_";
101  }
102  if (targetPeriod) {
103  ss << round(60._f * targetPeriod.value()) << "min_";
104  }
105  if (impactSpeed) {
106  ss << round(impactSpeed.value() / 1.e3_f) << "kms_";
107  }
108  if (impactAngle) {
109  ss << round(impactAngle.value()) << "deg_";
110  }
111  if (particleCnt) {
112  ss << particleCnt.value() << "p_";
113  }
114 
115  std::string name = ss.str();
116  // drop the last "_";
117  name.pop_back();
118  return name;
119  }
120 };
121 
122 class RunFactory {
123 private:
124  StringLogger logger;
125  RunParams params;
126  Path outputDir;
127  bool doDryRun;
128  std::string paramsMsg;
129 
130 public:
131  RunFactory(const RunParams& params)
132  : params(params) {
133  doDryRun = true;
134  outputDir = Path(params.getOutputPath());
135  }
136 
138  if (params.resumePath) {
139  BinaryInput input;
140  Expected<BinaryInput::Info> info = input.getInfo(Path(params.resumePath.value()));
141  if (!info) {
142  throw Exception("Cannot resume simulation from file '" + params.resumePath.value() + "'.\n" +
143  info.error());
144  }
145 
146  logger.write("Resuming simulation from file '" + params.resumePath.value() + "'");
147  if (info->runType == RunTypeEnum::SPH) {
148  return resumeFragmentation();
149  } else if (info->runType == RunTypeEnum::NBODY) {
150  return resumeReaccumulation();
151  } else {
152  throw Exception("Cannot resume from this file.");
153  }
154 
155  } else {
156  logger.write("Starting new simulation");
157  return makeNewSimulation();
158  }
159  }
160 
161  bool isDryRun() const {
162  return doDryRun;
163  }
164 
165  std::string getBannerMsg() const {
166  return logger.toString() + "\n" + paramsMsg;
167  }
168 
169  Path getOutputDir() const {
170  return outputDir;
171  }
172 
173 private:
174  Float defaultSphTime(const Optional<Float> runTime, const Optional<Float> radius, const Float mult) {
175  // by default, use 1h for 100km body
176  return runTime.valueOr(mult * 3600._f * radius.valueOr(5.e4_f) / 5.e4_f);
177  }
178 
179  void overrideRunTime(RunSettings& settings, const Float endTime) {
180  settings.set(RunSettingsId::RUN_END_TIME, endTime)
181  .set(RunSettingsId::RUN_OUTPUT_INTERVAL, endTime / 10._f);
182  }
183 
184  SharedPtr<JobNode> makeCollisionSetup() {
185  // target IC
186  BodySettings targetDefaults;
187  targetDefaults.set(BodySettingsId::BODY_RADIUS, params.targetRadius.valueOr(50.e3_f))
188  .set(BodySettingsId::PARTICLE_COUNT, params.particleCnt.valueOr(10000));
189  if (params.targetPeriod) {
190  targetDefaults.set(BodySettingsId::BODY_SPIN_RATE, 24._f / params.targetPeriod.value());
191  }
192  BodySettings targetBody =
193  loadSettings<BodySettingsId>(outputDir / Path("target.cnf"), targetDefaults, logger, doDryRun);
194  SharedPtr<JobNode> targetIc = makeNode<MonolithicBodyIc>("target body", targetBody);
195 
196  // impactor IC
197  BodySettings impactorDefaults;
198  impactorDefaults.set(BodySettingsId::BODY_RADIUS, params.targetRadius.valueOr(10.e3_f))
201  impactorDefaults.unset(BodySettingsId::PARTICLE_COUNT); // never used
202  BodySettings impactorBody = loadSettings<BodySettingsId>(
203  outputDir / Path("impactor.cnf"), impactorDefaults, logger, doDryRun);
204  SharedPtr<JobNode> impactorIc = makeNode<ImpactorIc>("impactor body", impactorBody);
205  targetIc->connect(impactorIc, "target");
206 
207  // target stabilization
208  RunSettings stabDefaults = SphStabilizationJob::getDefaultSettings("stabilization");
209  stabDefaults.set(RunSettingsId::RUN_OUTPUT_PATH, outputDir.native());
210  overrideRunTime(stabDefaults, defaultSphTime(params.stabTime, params.targetRadius, 0.2_f));
211 
212  RunSettings stabRun =
213  loadSettings<RunSettingsId>(outputDir / Path("stab.cnf"), stabDefaults, logger, doDryRun);
214  SharedPtr<JobNode> stabTarget = makeNode<SphStabilizationJob>("stabilization", stabRun);
215  targetIc->connect(stabTarget, "particles");
216 
217  // collision setup
218  CollisionGeometrySettings geometryDefaults;
219  geometryDefaults.set(CollisionGeometrySettingsId::IMPACT_SPEED, params.impactSpeed.valueOr(5.e3_f))
220  .set(CollisionGeometrySettingsId::IMPACT_ANGLE, params.impactAngle.valueOr(45._f));
221  CollisionGeometrySettings geometry = loadSettings<CollisionGeometrySettingsId>(
222  outputDir / Path("geometry.cnf"), geometryDefaults, logger, doDryRun);
223  SharedPtr<JobNode> setup = makeNode<CollisionGeometrySetup>("geometry", geometry);
224  stabTarget->connect(setup, "target");
225  impactorIc->connect(setup, "impactor");
226 
227  printRunSettings(targetBody, impactorBody, geometry);
228 
229  return setup;
230  }
231 
232  SharedPtr<JobNode> makeFragmentation() {
233  RunSettings fragDefaults = SphJob::getDefaultSettings("fragmentation");
234  fragDefaults.set(RunSettingsId::RUN_OUTPUT_PATH, outputDir.native());
235  overrideRunTime(fragDefaults, defaultSphTime(params.fragTime, params.targetRadius, 1._f));
236  RunSettings fragRun =
237  loadSettings<RunSettingsId>(outputDir / Path("frag.cnf"), fragDefaults, logger, doDryRun);
238  SharedPtr<JobNode> frag = makeNode<SphJob>("fragmentation", fragRun);
239  return frag;
240  }
241 
242  SharedPtr<JobNode> makeReaccumulation() {
243  RunSettings reacDefaults = NBodyJob::getDefaultSettings("reaccumulation");
244  reacDefaults.set(RunSettingsId::RUN_OUTPUT_PATH, outputDir.native());
245  overrideRunTime(reacDefaults, params.reacTime.valueOr(3600._f * 24._f * 10._f));
246  RunSettings reacRun =
247  loadSettings<RunSettingsId>(outputDir / Path("reac.cnf"), reacDefaults, logger, doDryRun);
248  SharedPtr<JobNode> reac = makeNode<NBodyJob>("reaccumulation", reacRun);
249  return reac;
250  }
251 
252  SharedPtr<JobNode> makeNewSimulation() {
253  SharedPtr<JobNode> setup = makeCollisionSetup();
254  SharedPtr<JobNode> frag = makeFragmentation();
255  setup->connect(frag, "particles");
256 
257  SharedPtr<JobNode> handoff = makeNode<SmoothedToSolidHandoff>("handoff"); // has no parameters
258  frag->connect(handoff, "particles");
259 
260  SharedPtr<JobNode> reac = makeReaccumulation();
261  handoff->connect(reac, "particles");
262  return reac;
263  }
264 
265  SharedPtr<JobNode> resumeFragmentation() {
266  SharedPtr<JobNode> loadFile = makeNode<LoadFileJob>(Path(params.resumePath.value()));
267 
268  SharedPtr<JobNode> frag = makeFragmentation();
269  loadFile->connect(frag, "particles");
270 
271  SharedPtr<JobNode> handoff = makeNode<SmoothedToSolidHandoff>("handoff");
272  frag->connect(handoff, "particles");
273 
274  SharedPtr<JobNode> reac = makeReaccumulation();
275  handoff->connect(reac, "particles");
276  return reac;
277  }
278 
279  SharedPtr<JobNode> resumeReaccumulation() {
280  SharedPtr<JobNode> loadFile = makeNode<LoadFileJob>(Path(params.resumePath.value()));
281 
282  SharedPtr<JobNode> reac = makeReaccumulation();
283  loadFile->connect(reac, "particles");
284  return reac;
285  }
286 
287  void printRunSettings(const BodySettings& targetBody,
288  const BodySettings& impactorBody,
289  const CollisionGeometrySettings& geometry) {
290  const Float targetRadius = targetBody.get<Float>(BodySettingsId::BODY_RADIUS);
291  const Float impactorRadius = impactorBody.get<Float>(BodySettingsId::BODY_RADIUS);
292  const Float impactSpeed = geometry.get<Float>(CollisionGeometrySettingsId::IMPACT_SPEED);
293  const Float impactAngle = geometry.get<Float>(CollisionGeometrySettingsId::IMPACT_ANGLE);
294  const Float spinRate = targetBody.get<Float>(BodySettingsId::BODY_SPIN_RATE);
295  const Size particleCnt = targetBody.get<int>(BodySettingsId::PARTICLE_COUNT);
296  const Float rho = targetBody.get<Float>(BodySettingsId::DENSITY);
297  const Float Q_D = evalBenzAsphaugScalingLaw(2._f * targetRadius, rho);
298  const Float impactEnergy = getImpactEnergy(targetRadius, impactorRadius, impactSpeed) / Q_D;
299 
300  StringLogger logger;
301  logger.setScientific(false);
302  logger.setPrecision(4);
303  logger.write();
304  logger.write("Run parameters");
305  logger.write("-------------------------------------");
306  logger.write(" Target radius (R_pb): ", 1.e-3_f * targetRadius, " km");
307  logger.write(" Impactor radius (r_imp): ", 1.e-3_f * impactorRadius, " km");
308  logger.write(" Impact speed (v_imp): ", 1.e-3_f * impactSpeed, " km/s");
309  logger.write(" Impact angle (phi_imp): ", impactAngle, "°");
310  logger.write(" Impact energy (Q/Q*_D): ", impactEnergy);
311  logger.write(" Target period (P_pb): ", 24._f / spinRate, spinRate == 0._f ? "" : "h");
312  logger.write(" Particle count (N): ", particleCnt);
313  logger.write("-------------------------------------");
314  logger.write();
315  logger.setScientific(true);
316  logger.setPrecision(PRECISION);
317 
318  paramsMsg = logger.toString();
319  }
320 };
321 
322 static void run(const ArgParser& parser, ILogger& logger) {
323  RunParams params;
324  params.targetRadius = parser.tryGetArg<Float>("tr");
325  params.targetPeriod = parser.tryGetArg<Float>("tp");
326  params.impactSpeed = parser.tryGetArg<Float>("v");
327  if (params.impactSpeed) {
328  params.impactSpeed.value() *= 1.e3_f; // km/s -> m/s
329  }
330  params.impactAngle = parser.tryGetArg<Float>("phi");
331  params.impactorRadius = parser.tryGetArg<Float>("ir");
332  params.particleCnt = parser.tryGetArg<int>("n");
333 
334  if (Optional<Float> impactEnergy = parser.tryGetArg<Float>("q")) {
335  // we have to specify also -tr and -v, as output directory is determined fom the computed
336  // impactorRadius. We cannot use values loaded from config files, as it would create circular
337  // dependency: we need impactor radius to get output path, we need output path to load config
338  // files, we need config files to get impactor radius ...
340  const Optional<Float> targetRadius = parser.tryGetArg<Float>("tr");
341  const Optional<Float> impactSpeed = parser.tryGetArg<Float>("v");
342  if (!targetRadius || !impactSpeed) {
343  throw ArgError(
344  "To specify impact energy (-q), you also need to specify the target radius (-tr) and "
345  "impact speed (-v)");
346  }
347  params.impactorRadius = getImpactorRadius(
348  targetRadius.value(), impactSpeed.value() * 1.e3_f, impactEnergy.value(), density);
349  }
350 
351  params.outputPath = parser.tryGetArg<std::string>("o");
352  params.resumePath = parser.tryGetArg<std::string>("i");
353 
354  params.stabTime = parser.tryGetArg<Float>("st");
355  params.fragTime = parser.tryGetArg<Float>("ft");
356  params.reacTime = parser.tryGetArg<Float>("rt");
357 
358  RunFactory factory(params);
359  SharedPtr<JobNode> node = factory.makeSimulation();
360 
361  printBanner(logger);
362  if (!factory.isDryRun()) {
363  logger.write(factory.getBannerMsg());
364 
365  NullJobCallbacks callbacks;
366  node->run(EMPTY_SETTINGS, callbacks);
367  } else {
368  printNoConfigsMsg(logger, factory.getOutputDir());
369  }
370 }
371 
372 int main(int argc, char* argv[]) {
373  StdOutLogger logger;
374 
375  try {
376  ArgParser parser(params);
377  parser.parse(argc, argv);
378 
379  run(parser, logger);
380 
381  return 0;
382 
383  } catch (HelpException& e) {
384  printBanner(logger);
385  logger.write(e.what());
386  return 0;
387  } catch (const Exception& e) {
388  logger.write("Run failed!\n", e.what());
389  return -1;
390  }
391 }
const float radius
Definition: CurveDialog.cpp:18
Float getImpactEnergy(const Float R, const Float r, const Float v)
Computes the impact energy Q from impact parameters.
Definition: Functions.cpp:18
Float getImpactorRadius(const Float R_pb, const Float v_imp, const Float QOverQ_D, const Float rho)
Calculates the impactor radius to satisfy required impact parameters.
Definition: Functions.cpp:35
NAMESPACE_SPH_BEGIN Float evalBenzAsphaugScalingLaw(const Float D, const Float rho)
Returns the critical energy Q_D^* as a function of body diameter.
Definition: Functions.cpp:5
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 int PRECISION
Number of valid digits of numbers on output.
Definition: Globals.h:25
int main(int argc, char *argv[])
Definition: Impact.cpp:372
INLINE auto round(const T &f)
Definition: MathUtils.h:356
constexpr Float LARGE
Definition: MathUtils.h:34
@ SPH
Main SPH simulation.
@ NBODY
Main N-body simulation, using initial conditions either from SPH (handoff) or manually specified.
@ IMPACT_ANGLE
Impact angle in degrees, i.e. angle between velocity vector and normal at the impact point.
@ IMPACT_SPEED
Impact speed in m/s.
Includes common headers.
Exception thrown if the arguments are invalid.
Definition: ArgsParser.h:23
Provides functions for parsing command-line arguments.
Definition: ArgsParser.h:62
void parse(const int argc, char **argv)
Parses the input arguments and stored the parsed values.
Definition: ArgsParser.cpp:16
Optional< TValue > tryGetArg(const std::string &name) const
Returns the value of an argument or NOTHING if the argument was not parsed.
Definition: ArgsParser.h:110
Input for the binary file, generated by BinaryOutput.
Definition: Output.h:352
Expected< Info > getInfo(const Path &path) const
Opens the file and reads header info without reading the rest of the file.
Definition: Output.cpp:814
Generic exception.
Definition: Exceptions.h:10
virtual const char * what() const noexcept
Definition: Exceptions.h:18
Wrapper of type that either contains a value of given type, or an error message.
Definition: Expected.h:25
const Error & error() const
Returns the error message.
Definition: Expected.h:94
Exception thrown when used passes -h or –help parameter.
Definition: ArgsParser.h:32
Interface providing generic (text, human readable) output of the program.
Definition: Logger.h:22
void setPrecision(const Size newPrecision)
Changes the precision of printed numbers.
Definition: Logger.h:47
void write(TArgs &&... args)
Creates and logs a message by concatenating arguments.
Definition: Logger.h:37
void setScientific(const bool newScientific)
Sets/unsets scientific notation.
Definition: Logger.h:52
virtual void run(const RunSettings &global, IJobCallbacks &callbacks) override
Evaluates the node and all its providers.
Definition: Node.cpp:184
void connect(SharedPtr< JobNode > dependent, const std::string &slotName)
Connects this node to given dependent node.
Definition: Node.cpp:60
static RunSettings getDefaultSettings(const std::string &name)
INLINE Type & value()
Returns the reference to the stored value.
Definition: Optional.h:172
INLINE Type valueOr(const TOther &other) const
Returns the stored value if the object has been initialized, otherwise returns provided parameter.
Definition: Optional.h:188
Object representing a path on a filesystem.
Definition: Path.h:17
std::string native() const
Returns the native version of the path.
Definition: Path.cpp:71
bool isDryRun() const
Definition: Impact.cpp:161
Path getOutputDir() const
Definition: Impact.cpp:169
SharedPtr< JobNode > makeSimulation()
Definition: Impact.cpp:137
RunFactory(const RunParams &params)
Definition: Impact.cpp:131
std::string getBannerMsg() const
Definition: Impact.cpp:165
Generic object containing various settings and parameters of the run.
Definition: Settings.h:108
void unset(const TEnum idx)
Removes given parameter from settings.
Definition: Settings.h:313
TValue get(const TEnum idx, std::enable_if_t<!std::is_enum< std::decay_t< TValue >>::value, int >=0) const
Returns a value of given type from the settings.
Definition: Settings.h:326
static const Settings & getDefaults()
\brief Returns a reference to object containing default values of all settings.
Settings & set(const TEnum idx, TValue &&value, std::enable_if_t<!std::is_enum< std::decay_t< TValue >>::value, int >=0)
Saves a value into the settings.
Definition: Settings.h:226
bool tryLoadFileOrSaveCurrent(const Path &path, const Settings &overrides=EMPTY_SETTINGS)
If the specified file exists, loads the settings from it, otherwise creates the file and saves the cu...
static RunSettings getDefaultSettings(const std::string &name)
Standard output logger.
Definition: Logger.h:138
Logger writing messages to string stream.
Definition: Logger.h:145
std::string toString() const
Returns all written messages as a string. Messages are not erased from the logger by this.
Definition: Logger.cpp:35
const EmptySettingsTag EMPTY_SETTINGS
Definition: Settings.h:32
@ DAMAGE_MIN
Estimate minimal value of damage used to determine timestepping error.
@ PARTICLE_COUNT
Number of SPH particles in the body.
@ DENSITY
Density at zero pressure.
@ STRESS_TENSOR_MIN
Estimated minial value of stress tensor components used to determined timestepping error.
@ RUN_OUTPUT_INTERVAL
Time interval of dumping data to disk.
@ RUN_OUTPUT_PATH
Path where all output files (dumps, logs, ...) will be written.
Definition: MemoryPool.h:5
Optional< Float > targetPeriod
Definition: Impact.cpp:74
std::string getOutputPath() const
Returns the name of the created output directory.
Definition: Impact.cpp:89
Optional< Float > fragTime
Definition: Impact.cpp:81
Optional< Float > targetRadius
Definition: Impact.cpp:73
Optional< std::string > resumePath
Definition: Impact.cpp:84
Optional< Float > stabTime
Definition: Impact.cpp:80
Optional< int > particleCnt
Definition: Impact.cpp:78
Optional< Float > impactSpeed
Definition: Impact.cpp:77
Optional< Float > impactAngle
Definition: Impact.cpp:76
Optional< Float > reacTime
Definition: Impact.cpp:82
Optional< std::string > outputPath
Definition: Impact.cpp:85
Optional< Float > impactorRadius
Definition: Impact.cpp:75