SPH
IoJobs.cpp
Go to the documentation of this file.
1 #include "run/jobs/IoJobs.h"
2 #include "io/FileSystem.h"
3 #include "io/Output.h"
4 #include "post/MarchingCubes.h"
5 #include "post/MeshFile.h"
6 #include "run/IRun.h"
7 #include "system/Factory.h"
8 #include "system/Statistics.h"
9 #include "system/Timer.h"
10 #include <thread>
11 
13 
14 // ----------------------------------------------------------------------------------------------------------
15 // LoadFileJob
16 // ----------------------------------------------------------------------------------------------------------
17 
18 static RegisterEnum<UnitEnum> sUnits({
19  { UnitEnum::SI, "si", "SI unit system" },
20  { UnitEnum::CGS, "cgs", "CGS unit system" },
21  { UnitEnum::NBODY, "nbody", "N-body (Hénon) units" },
22 });
23 
25  VirtualSettings connector;
26  addGenericCategory(connector, instName);
27 
28  VirtualSettings::Category& cat = connector.addCategory("Input");
29  cat.connect("File", "file", path)
32  cat.connect("Unit system", "units", units);
33  return connector;
34 }
35 
36 void LoadFileJob::evaluate(const RunSettings& UNUSED(global), IRunCallbacks& UNUSED(callbacks)) {
37  if (!FileSystem::pathExists(path)) {
38  throw InvalidSetup("File '" + path.native() + "' does not exist or cannot be accessed.");
39  }
40  AutoPtr<IInput> input = Factory::getInput(path);
41  Storage storage;
42  Statistics stats;
43  Outcome outcome = input->load(path, storage, stats);
44  if (!outcome) {
45  throw InvalidSetup(outcome.error());
46  }
47 
48  result = makeShared<ParticleData>();
49 
50  // set up overrides for resuming simulations
51  if (stats.has(StatisticsId::RUN_TIME)) {
53  }
54  if (stats.has(StatisticsId::TIMESTEP_VALUE)) {
57  }
58  if (Optional<Size> dumpIdx = OutputFile::getDumpIdx(Path(path))) {
60  }
61 
62  Float G;
63  switch (UnitEnum(units)) {
64  case UnitEnum::SI:
66  break;
67  case UnitEnum::CGS:
68  G = 1.e3_f * Constants::gravity;
69  break;
70  case UnitEnum::NBODY:
71  G = 1._f;
72  break;
73  default:
75  }
76 
78 
79  result->storage = std::move(storage);
80  result->stats = std::move(stats);
81 }
82 
83 static JobRegistrar sRegisterLoadFile(
84  "load file",
85  "I/O",
86  [](const std::string& UNUSED(name)) { return makeAuto<LoadFileJob>(); },
87  "Loads particle state from a file");
88 
89 // ----------------------------------------------------------------------------------------------------------
90 // FileSequenceJob
91 // ----------------------------------------------------------------------------------------------------------
92 
94  VirtualSettings connector;
95  addGenericCategory(connector, instName);
96 
97  VirtualSettings::Category& inputCat = connector.addCategory("Input");
98  inputCat.connect("First file", "first_file", firstFile)
101  inputCat.connect("Maximum framerate", "max_fps", maxFps);
102 
103  return connector;
104 }
105 
108  if (!FileSystem::pathExists(firstFile)) {
109  throw InvalidSetup("File '" + firstFile.native() + "' does not exist.");
110  }
111 
112  FlatMap<Size, Path> fileMap;
113  Optional<OutputFile> referenceMask = OutputFile::getMaskFromPath(firstFile);
114  if (!referenceMask) {
115  throw InvalidSetup("Cannot deduce sequence from file '" + firstFile.native() + "'.");
116  }
117 
118  Optional<Size> firstIndex = OutputFile::getDumpIdx(firstFile);
119  SPH_ASSERT(firstIndex); // already checked above
120 
121  const Path dir = firstFile.parentPath();
122  for (Path relativePath : FileSystem::iterateDirectory(dir)) {
123  const Path path = dir / relativePath;
125  if (pathMask && pathMask->getMask() == referenceMask->getMask()) {
126  // belongs to the same file sequence
128  SPH_ASSERT(index);
129 
130  if (index.value() >= firstIndex.value()) {
131  fileMap.insert(index.value(), path);
132  }
133  }
134  }
135  return fileMap;
136 }
137 
138 
139 void FileSequenceJob::evaluate(const RunSettings& UNUSED(global), IRunCallbacks& callbacks) {
140  AutoPtr<IInput> input = Factory::getInput(firstFile);
141  Storage storage;
142  Statistics stats;
143 
144  FlatMap<Size, Path> sequence = getFileSequence(firstFile);
145  const Size firstIndex = sequence.begin()->key;
146  const Size lastIndex = (sequence.end() - 1)->key;
147  for (auto& element : sequence) {
148  const Size index = element.key;
149 
150  Timer frameTimer;
151  Outcome outcome = input->load(element.value, storage, stats);
152  if (!outcome) {
153  throw InvalidSetup(outcome.error());
154  }
155 
156  stats.set(StatisticsId::INDEX, int(index));
157  if (sequence.size() > 1) {
158  stats.set(StatisticsId::RELATIVE_PROGRESS, Float(index - firstIndex) / (lastIndex - firstIndex));
159  } else {
161  }
162 
163  if (index == firstIndex) {
164  callbacks.onSetUp(storage, stats);
165  }
166  callbacks.onTimeStep(storage, stats);
167 
168  if (callbacks.shouldAbortRun()) {
169  break;
170  }
171 
172  const Size elapsed = frameTimer.elapsed(TimerUnit::MILLISECOND);
173  const Size minElapsed = 1000 / maxFps;
174  if (elapsed < minElapsed) {
175  std::this_thread::sleep_for(std::chrono::milliseconds(minElapsed - elapsed));
176  }
177  }
178 
179  result = makeShared<ParticleData>();
180  result->storage = std::move(storage);
181  result->stats = std::move(stats);
182 }
183 
184 static JobRegistrar sRegisterFileSequence(
185  "load sequence",
186  "sequence",
187  "I/O",
188  [](const std::string& name) { return makeAuto<FileSequenceJob>(name); },
189  "Loads and displays a sequence of particle states.");
190 
191 // ----------------------------------------------------------------------------------------------------------
192 // SaveFileJob
193 // ----------------------------------------------------------------------------------------------------------
194 
195 SaveFileJob::SaveFileJob(const std::string& name)
196  : IParticleJob(name) {
198  .set(RunSettingsId::RUN_OUTPUT_PATH, std::string(""))
199  .set(RunSettingsId::RUN_OUTPUT_NAME, std::string("final.ssf"))
202 }
203 
205  VirtualSettings connector;
206  addGenericCategory(connector, instName);
207 
208  VirtualSettings::Category& outputCat = connector.addCategory("Output");
209  outputCat.connect<Path>("File", settings, RunSettingsId::RUN_OUTPUT_NAME)
211  .setFileFormats(getOutputFormats());
212  outputCat.connect<EnumWrapper>("Format", settings, RunSettingsId::RUN_OUTPUT_TYPE);
214  .setEnabler([this] {
215  const IoEnum type = settings.get<IoEnum>(RunSettingsId::RUN_OUTPUT_TYPE);
216  return type == IoEnum::TEXT_FILE || type == IoEnum::VTK_FILE;
217  });
218 
219  return connector;
220 }
221 
222 void SaveFileJob::evaluate(const RunSettings& UNUSED(global), IRunCallbacks& UNUSED(callbacks)) {
223  SharedPtr<ParticleData> data = this->getInput<ParticleData>("particles");
224 
225  AutoPtr<IOutput> output = Factory::getOutput(settings);
226  output->dump(data->storage, data->stats);
227 
228  result = data;
229 }
230 
231 static JobRegistrar sRegisterOutput(
232  "save file",
233  "I/O",
234  [](const std::string& name) { return makeAuto<SaveFileJob>(name); },
235  "Saves the input particle state into a file.");
236 
237 // ----------------------------------------------------------------------------------------------------------
238 // SaveMeshJob
239 // ----------------------------------------------------------------------------------------------------------
240 
242  VirtualSettings connector;
243  addGenericCategory(connector, instName);
244 
245  VirtualSettings::Category& outputCat = connector.addCategory("Output");
246  outputCat.connect("File", "file", path)
248  .setFileFormats({
249  { "Wavefront OBJ file", "obj" },
250  { "Stanford PLY file", "ply" },
251  });
252 
253  VirtualSettings::Category& meshCat = connector.addCategory("Mesh parameters");
254  meshCat.connect<Float>("Resolution", "resolution", resolution);
255  meshCat.connect<Float>("Surface level", "level", level);
256  meshCat.connect<Float>("Smoothing multiplier", "smoothing_mult", smoothingMult);
257  meshCat.connect<bool>("Refine mesh", "refine", refine);
258  meshCat.connect<bool>("Anisotropic kernels", "aniso", anisotropic);
259  meshCat.connect<bool>("Scale to unit size", "scale_to_unit", scaleToUnit);
260 
261  return connector;
262 }
263 
264 void SaveMeshJob::evaluate(const RunSettings& global, IRunCallbacks& callbacks) {
265  SharedPtr<ParticleData> data = this->getInput<ParticleData>("particles");
266 
267  // sanitize resolution
268  const Box bbox = getBoundingBox(data->storage);
269  const Float boxSize = maxElement(bbox.size());
270 
271  SharedPtr<IScheduler> scheduler = Factory::getScheduler(global);
272 
273  McConfig config;
274  config.gridResolution = clamp(resolution, 1.e-3_f * boxSize, 0.2_f * boxSize);
275  config.surfaceLevel = level;
276  config.smoothingMult = smoothingMult;
277  config.useAnisotropicKernels = anisotropic;
278  config.progressCallback = [&callbacks](const Float progress) {
279  Statistics stats;
280  stats.set(StatisticsId::RELATIVE_PROGRESS, progress);
281  callbacks.onTimeStep(Storage(), stats);
282  return !callbacks.shouldAbortRun();
283  };
284  Array<Triangle> triangles = getSurfaceMesh(*scheduler, data->storage, config);
285 
286  if (scaleToUnit) {
287  for (Triangle& t : triangles) {
288  for (Size i = 0; i < 3; ++i) {
289  t[i] = (t[i] - bbox.center()) / boxSize;
290  }
291  }
292  }
293 
294  if (refine) {
295  Mesh mesh = getMeshFromTriangles(triangles, 1.e-6_f);
296  for (Size i = 0; i < 5; ++i) {
297  refineMesh(mesh);
298  }
299  triangles = getTrianglesFromMesh(mesh);
300  }
301 
302  AutoPtr<IMeshFile> saver = getMeshFile(path);
303  Outcome outcome = saver->save(path, std::move(triangles));
304  if (!outcome) {
305  throw InvalidSetup("Saving mesh failed.\n\n" + outcome.error());
306  }
307 
308  result = data;
309 }
310 
311 static JobRegistrar sRegisterMeshSaver(
312  "save mesh",
313  "I/O",
314  [](const std::string& name) { return makeAuto<SaveMeshJob>(name); },
315  "Creates a triangular mesh from the input particles and saves it to file.");
316 
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
#define NOT_IMPLEMENTED
Helper macro marking missing implementation.
Definition: Assert.h:100
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
INLINE Float maxElement(const T &value)
Returns maximum element, simply the value iself by default.
Definition: Generic.h:29
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
Basic interface defining a single run.
FlatMap< Size, Path > getFileSequence(const Path &firstFile)
Definition: IoJobs.cpp:107
UnitEnum
Definition: IoJobs.h:8
VirtualSettings::Category & addGenericCategory(VirtualSettings &connector, std::string &instanceName)
Adds a common settings category, used by all jobs.
Definition: Job.cpp:43
Array< Triangle > getSurfaceMesh(IScheduler &scheduler, const Storage &storage, const McConfig &config)
Returns the triangle mesh of the body surface (or surfaces of bodies).
constexpr INLINE T clamp(const T &f, const T &f1, const T &f2)
Definition: MathBasic.h:35
AutoPtr< IMeshFile > getMeshFile(const Path &path)
Deduces mesh type from extension of given path.
Definition: MeshFile.cpp:212
void refineMesh(Mesh &mesh)
Improves mesh quality using edge flips (valence equalization) and tangential relaxation.
Definition: Mesh.cpp:29
Mesh getMeshFromTriangles(ArrayView< const Triangle > triangles, const Float eps)
Converts array of triangles into a mesh.
Definition: Mesh.cpp:83
Array< Triangle > getTrianglesFromMesh(const Mesh &mesh)
Expands the mesh into an array of triangles.
Definition: Mesh.cpp:169
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
Saving and loading particle data.
@ VELOCITY
Current velocities of particles, always a vector quantity.
@ POSITION
Positions of particles, always a vector quantity.
Statistics gathered and periodically displayed during the run.
@ RUN_TIME
Current time of the simulation in code units. Does not necessarily have to be 0 when run starts.
@ TIMESTEP_VALUE
Current value of timestep.
@ INDEX
Current number of time step, indexed from 0.
Box getBoundingBox(const Storage &storage, const Float radius)
Convenience function to get the bounding box of all particles.
Definition: Storage.cpp:832
Measuring time intervals and executing periodic events.
Array< IVirtualEntry::FileFormat > getInputFormats()
Convenience function, returning the list of input file formats defined by IoEnum.
Array< IVirtualEntry::FileFormat > getOutputFormats()
Convenience function, returning the list of output file formats defined by IoEnum.
Wrapper of pointer that deletes the resource from destructor.
Definition: AutoPtr.h:15
INLINE const TError & error() const
Returns the error message.
Definition: Outcome.h:88
Helper object defining three-dimensional interval (box).
Definition: Box.h:17
INLINE Vector center() const
Returns the center of the box.
Definition: Box.h:112
INLINE Vector size() const
Returns box dimensions.
Definition: Box.h:106
EntryControl & setPathType(const PathType &newType)
Sets the type of the path.
EntryControl & setFileFormats(Array< FileFormat > &&formats)
Sets the allowed file formats.
virtual void evaluate(const RunSettings &global, IRunCallbacks &callbacks) override
Runs the operation provided by the job.
Definition: IoJobs.cpp:139
virtual VirtualSettings getSettings() override
Returns a settings object which allows to query and modify the state of the job.
Definition: IoJobs.cpp:93
Container of key-value pairs.
Definition: FlatMap.h:19
INLINE Iterator< Element > end()
Returns the iterator pointing to the one-past-last element.
Definition: FlatMap.h:165
INLINE TValue & insert(const TKey &key, const TValue &value)
Adds a new element into the map or sets new value of element with the same key.
Definition: FlatMap.h:65
INLINE Iterator< Element > begin()
Returns the iterator pointing to the first element.
Definition: FlatMap.h:155
INLINE Size size() const
Returns the number of elements in the map.
Definition: FlatMap.h:145
std::string instName
Definition: Job.h:100
virtual Expected< Path > dump(const Storage &storage, const Statistics &stats)=0
Saves data from particle storage into the file.
Base class for all jobs providing particle data.
Definition: Job.h:242
SharedPtr< ParticleData > result
Data filled by the job when it finishes.
Definition: Job.h:245
Callbacks executed by the simulation to provide feedback to the user.
Definition: IRun.h:27
virtual bool shouldAbortRun() const =0
Returns whether current run should be aborted or not.
virtual void onSetUp(const Storage &storage, Statistics &stats)=0
Called right before the run starts, i.e. after initial conditions are set up.
virtual void onTimeStep(const Storage &storage, Statistics &stats)=0
Called every timestep.
Thrown when components of the run are mutually incompatible.
Definition: Exceptions.h:24
virtual void evaluate(const RunSettings &global, IRunCallbacks &callbacks) override
Runs the operation provided by the job.
Definition: IoJobs.cpp:36
virtual VirtualSettings getSettings() override
Returns a settings object which allows to query and modify the state of the job.
Definition: IoJobs.cpp:24
INLINE Type & value()
Returns the reference to the stored value.
Definition: Optional.h:172
static Optional< Size > getDumpIdx(const Path &path)
Extracts the dump index from given path generated by OutputFile.
Definition: Output.cpp:49
static Optional< OutputFile > getMaskFromPath(const Path &path, const Size firstDumpIdx=0)
Attemps to get the OutputFile from one of the path generated from it.
Definition: Output.cpp:72
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
Path parentPath() const
Returns the parent directory. If the path is empty or root, return empty path.
Definition: Path.cpp:35
SaveFileJob(const std::string &name)
Definition: IoJobs.cpp:195
virtual VirtualSettings getSettings() override
Returns a settings object which allows to query and modify the state of the job.
Definition: IoJobs.cpp:204
virtual void evaluate(const RunSettings &global, IRunCallbacks &callbacks) override
Runs the operation provided by the job.
Definition: IoJobs.cpp:222
virtual VirtualSettings getSettings() override
Returns a settings object which allows to query and modify the state of the job.
Definition: IoJobs.cpp:241
virtual void evaluate(const RunSettings &global, IRunCallbacks &UNUSED(callbacks)) override
Definition: IoJobs.cpp:264
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
Object holding various statistics about current run.
Definition: Statistics.h:22
TValue get(const StatisticsId idx) const
Returns value of a statistic.
Definition: Statistics.h:88
Statistics & set(const StatisticsId idx, TValue &&value)
Sets new values of a statistic.
Definition: Statistics.h:52
bool has(const StatisticsId idx) const
Checks if the object contains a statistic with given ID.
Definition: Statistics.h:44
Container storing all quantities used within the simulations.
Definition: Storage.h:230
Basic time-measuring tool. Starts automatically when constructed.
Definition: Timer.h:27
int64_t elapsed(const TimerUnit unit) const
Returns elapsed time in timer units. Does not reset the timer.
Definition: Timer.cpp:55
Triangle.h.
Definition: Triangle.h:14
EntryControl & connect(const std::string &name, const std::string &key, TValue &value)
Connects to given reference.
Holds a map of virtual entries, associated with a unique name.
Category & addCategory(const std::string &name)
Creates a new category of entries.
Creating code components based on values from settings.
IoEnum
Definition: Settings.h:859
@ TEXT_FILE
Formatted human-readable text file.
@ BINARY_FILE
@ RUN_OUTPUT_QUANTITIES
List of quantities to write to text output. Binary output always stores all quantitites.
@ RUN_OUTPUT_PATH
Path where all output files (dumps, logs, ...) will be written.
@ TIMESTEPPING_INITIAL_TIMESTEP
@ RUN_OUTPUT_TYPE
Selected format of the output file, see IoEnum.
@ RUN_OUTPUT_FIRST_INDEX
Index of the first generated output file. Might not be zero if the simulation is resumed.
@ GRAVITY_CONSTANT
Gravitational constant. To be generalized.
@ RUN_OUTPUT_NAME
File name of the output file (including extension), where d is a placeholder for output number.
constexpr Float gravity
Gravitational constant (CODATA 2014)
Definition: Constants.h:29
AutoPtr< IOutput > getOutput(const RunSettings &settings)
Definition: Factory.cpp:563
SharedPtr< IScheduler > getScheduler(const RunSettings &settings=RunSettings::getDefaults())
Definition: Factory.cpp:178
AutoPtr< IInput > getInput(const Path &path)
Definition: Factory.cpp:601
bool pathExists(const Path &path)
Checks if a file or directory exists (or more precisely, if a file or directory is accessible).
Definition: FileSystem.cpp:21
DirectoryAdapter iterateDirectory(const Path &directory)
Definition: FileSystem.cpp:338
Wrapper of an enum.
Definition: Settings.h:37
Helper class, allowing to register job into the global list of jobs.
Definition: Job.h:203
Function< bool(Float progress)> progressCallback
Generic functor called during MC evaluation.
Float smoothingMult
Multiplier of the smoothing lengths.
Float gridResolution
Absolute size of each produced triangle.
bool useAnisotropicKernels
If true, anisotropic kernels of Yu & Turk (2010) are used instead of normal isotropic kernels.
Float surfaceLevel
Definition: Mesh.h:7
Storage storage
Holds all particle positions and other quantities.
Definition: Job.h:35
Statistics stats
Final statistics of the simulation.
Definition: Job.h:38
RunSettings overrides
Overrides associated with the particle state.
Definition: Job.h:43
Helper class for adding individual enums to the enum map.
Definition: EnumMap.h:175