votca 2024.2-dev
Loading...
Searching...
No Matches
csgapplication.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2021 The VOTCA Development Team (http://www.votca.org)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18// Third party includes
19#include <boost/algorithm/string/trim.hpp>
20#include <memory>
21
22// Local VOTCA includes
23#include "votca/csg/cgengine.h"
29#include "votca/csg/version.h"
30
31namespace votca {
32namespace csg {
33
35 // register all io plugins
39
40 if (NeedsTopology()) {
41 AddProgramOptions()("top", boost::program_options::value<std::string>(),
42 " atomistic topology file");
43 }
44 if (DoMapping()) {
45 if (DoMappingDefault()) {
46 AddProgramOptions("Mapping options")(
47 "cg", boost::program_options::value<std::string>(),
48 " coarse graining mapping and bond definitions (xml-file)")(
49 "map-ignore", boost::program_options::value<std::string>(),
50 " list of molecules to ignore separated by ;")("no-map",
51 " disable mapping "
52 "and act on original "
53 "trajectory");
54 } else {
55 AddProgramOptions("Mapping options")(
56 "cg", boost::program_options::value<std::string>(),
57 " [OPTIONAL] coarse graining mapping and bond definitions\n"
58 " (xml-file). If no file is given, program acts on original "
59 "trajectory")(
60 "map-ignore", boost::program_options::value<std::string>(),
61 " list of molecules to ignore if mapping is done separated by ;");
62 }
63 }
64
65 if (DoTrajectory()) {
66 AddProgramOptions("Trajectory options")(
67 "trj", boost::program_options::value<std::string>(),
68 " atomistic trajectory file")(
69 "begin", boost::program_options::value<double>()->default_value(0.0),
70 " skip frames before this time (only works for Gromacs files)")(
71 "first-frame", boost::program_options::value<Index>()->default_value(0),
72 " start with this frame")("nframes",
73 boost::program_options::value<Index>(),
74 " process the given number of frames");
75 }
76
77 if (DoThreaded()) {
78 /*
79 * TODO default value of 1 for nt is not smart
80 */
81 AddProgramOptions("Threading options")(
82 "nt", boost::program_options::value<Index>()->default_value(1),
83 " number of threads");
84 }
85}
86
88 do_mapping_ = false;
89 if (NeedsTopology()) {
90 CheckRequired("top", "no topology file specified");
91 }
92
93 // check for mapping options
94 if (DoMapping()) {
95 // default mapping is on
96 if (DoMappingDefault()) {
97 // if the user does not explicitly ask to turn it off, cg is needed
98 if (OptionsMap().count("no-map") == 0) {
99 CheckRequired("cg", "no coarse graining definition specified");
100 do_mapping_ = true;
101 }
102 if (OptionsMap().count("no-map") && OptionsMap().count("cg")) {
103 ShowHelpText(std::cout);
104 throw std::runtime_error(
105 "no-map and cg options are mutually exclusive!");
106 }
107 } // default mapping is off, if user gives cg, then do mapping
108 else if (OptionsMap().count("cg")) {
109 do_mapping_ = true;
110 }
111 }
112
113 /* check threading options */
114 if (DoThreaded()) {
115 nthreads_ = OptionsMap()["nt"].as<Index>();
116 /* TODO
117 * does the number of threads make sense?
118 * which criteria should be used? smaller than system's cores?
119 */
120 }
121
122 return true;
123}
124
125void CsgApplication::ShowHelpText(std::ostream &out) {
126 std::string name = ProgramName();
127 if (VersionString() != "") {
128 name = name + ", version " + VersionString();
129 }
130
131 HelpTextHeader(name);
132 HelpText(out);
133
134 out << "\n\n" << VisibleOptions() << std::endl;
135}
136
138 while (app_->ProcessData(this)) {
139 if (app_->SynchronizeThreads()) {
140 Index id = getId();
141 app_->threadsMutexesOut_[id]->Lock();
142 app_->MergeWorker(this);
143 app_->threadsMutexesOut_[(id + 1) % app_->nthreads_]->Unlock();
144 }
145 }
146}
147
149
150 Index id;
151 id = worker->getId();
152
153 if (SynchronizeThreads()) {
154 // wait til its your turn
155 threadsMutexesIn_[id]->Lock();
156 }
158 if (nframes_ == 0) {
160
161 if (SynchronizeThreads()) {
162 // done processing? don't forget to unlock next worker anyway
163 threadsMutexesIn_[(id + 1) % nthreads_]->Unlock();
164 }
165
166 return false;
167 }
168 nframes_--;
169 if (!is_first_frame_ || worker->getId() != 0) {
170 // get frame
171 bool tmpRes = traj_reader_->NextFrame(worker->top_);
172 if (!tmpRes) {
174 if (SynchronizeThreads()) {
175 threadsMutexesIn_[(id + 1) % nthreads_]->Unlock();
176 }
177 return false;
178 }
179 }
180 if (worker->getId() == 0) {
181 is_first_frame_ = false;
182 }
183
185 if (SynchronizeThreads()) {
186 // unlock next frame for input
187 threadsMutexesIn_[(id + 1) % nthreads_]->Unlock();
188 }
189 // evaluate
190 if (do_mapping_) {
191 worker->map_->Apply();
192 worker->EvalConfiguration(&worker->top_cg_, &worker->top_);
193 } else {
194 worker->EvalConfiguration(&worker->top_);
195 }
196
197 return true;
198}
199
201 // create reader for atomistic topology
202 std::unique_ptr<TopologyReader> reader =
203 TopReaderFactory().Create(OptionsMap()["top"].as<std::string>());
204 if (reader == nullptr) {
205 throw std::runtime_error(std::string("input format not supported: ") +
206 OptionsMap()["top"].as<std::string>());
207 }
208
209 class DummyWorker : public Worker {
210 public:
211 void EvalConfiguration(Topology *top, Topology *top_ref) override {
212 app_->EvalConfiguration(top, top_ref);
213 }
214 };
215
216 // create the master worker
217 if (DoThreaded()) {
218 myWorkers_.push_back(ForkWorker());
219 } else {
220 myWorkers_.emplace_back(std::make_unique<DummyWorker>());
221 }
222 myWorkers_.back()->setApplication(this);
223 myWorkers_.back()->setId(0);
224 Worker *master = (myWorkers_.back().get());
225
226 CGEngine cg;
227
229 // read in the topology for master
231 reader->ReadTopology(OptionsMap()["top"].as<std::string>(), master->top_);
232 // Ensure that the coarse grained topology will have the same boundaries
233 master->top_cg_.setBox(master->top_.getBox());
234
235 std::cout << "I have " << master->top_.BeadCount() << " beads in "
236 << master->top_.MoleculeCount() << " molecules" << std::endl;
237 master->top_.CheckMoleculeNaming();
238
239 if (do_mapping_) {
240 // read in the coarse graining definitions (xml files)
241 cg.LoadMoleculeType(OptionsMap()["cg"].as<std::string>());
242 // create the mapping + cg topology
243
244 if (OptionsMap().count("map-ignore") != 0) {
245 tools::Tokenizer tok(OptionsMap()["map-ignore"].as<std::string>(), ";");
246 for (std::string str : tok) {
247 boost::trim(str);
248 if (str.length() > 0) {
249 cg.AddIgnore(str);
250 }
251 }
252 }
253
254 master->map_ = cg.CreateCGTopology(master->top_, master->top_cg_);
255
256 std::cout << "I have " << master->top_cg_.BeadCount() << " beads in "
257 << master->top_cg_.MoleculeCount()
258 << " molecules for the coarsegraining" << std::endl;
259
260 // If the trajectory reader is off but mapping flag is specified do apply
261 // the mapping, this switch is necessary in cases where xml files are
262 // specified, which do not contain positional information. In such cases
263 // it is not possible to apply the positional mapping, a trajectory file
264 // must be read in.
265 if (DoTrajectory() == false) {
266 master->map_->Apply();
267 if (!EvaluateTopology(&master->top_cg_, &master->top_)) {
268 return;
269 }
270 }
271 } else if (!EvaluateTopology(&master->top_)) {
272 return;
273 }
274
276 // Here trajectory parsing starts
278 if (DoTrajectory() && OptionsMap().count("trj")) {
279 double begin = 0;
280 Index first_frame;
281 bool has_begin = false;
282
283 if (OptionsMap().count("begin")) {
284 has_begin = true;
285 begin = OptionsMap()["begin"].as<double>();
286 }
287
288 nframes_ = -1;
289 if (OptionsMap().count("nframes")) {
290 nframes_ = OptionsMap()["nframes"].as<Index>();
291 }
292
293 first_frame = OptionsMap()["first-frame"].as<Index>();
294
295 // create reader for trajectory
297 TrjReaderFactory().Create(OptionsMap()["trj"].as<std::string>());
298 if (traj_reader_ == nullptr) {
299 throw std::runtime_error(std::string("input format not supported: ") +
300 OptionsMap()["trj"].as<std::string>());
301 }
302 // open the trajectory
303 traj_reader_->Open(OptionsMap()["trj"].as<std::string>());
304
306 // Create all the workers
308 for (Index thread = 1; thread < nthreads_ && DoThreaded(); thread++) {
309 myWorkers_.push_back(ForkWorker());
310 myWorkers_.back()->setApplication(this);
311 myWorkers_.back()->setId(thread);
312
313 // this will be changed to CopyTopologyData
314 // read in the topology
315
316 reader->ReadTopology(OptionsMap()["top"].as<std::string>(),
317 myWorkers_.back()->top_);
318 myWorkers_.back()->top_.CheckMoleculeNaming();
319
320 if (do_mapping_) {
321 // create the mapping + cg topology
322 myWorkers_.back()->map_ = cg.CreateCGTopology(
323 myWorkers_.back()->top_, myWorkers_.back()->top_cg_);
324 }
325 }
326
328 // Proceed to first frame of interest
330
331 traj_reader_->FirstFrame(master->top_);
333 std::cout
334 << "NOTE: You are using OpenBox boundary conditions. Check if this "
335 "is intended.\n"
336 << std::endl;
337 }
338 // seek first frame, let thread0 do that
339 bool bok;
340 for (bok = true; bok == true; bok = traj_reader_->NextFrame(master->top_)) {
341 if ((has_begin && (master->top_.getTime() < begin)) || first_frame > 1) {
342 first_frame--;
343 continue;
344 }
345 break;
346 }
347 if (!bok) { // trajectory was too short and we did not proceed to first
348 // frame
349 traj_reader_->Close();
350
351 throw std::runtime_error(
352 "trajectory was too short, did not process a single frame");
353 }
354
355 // notify all observers that coarse graining has begun
356 if (do_mapping_) {
357 master->map_->Apply();
358 BeginEvaluate(&master->top_cg_, &master->top_);
359 } else {
360 BeginEvaluate(&master->top_);
361 }
362
363 is_first_frame_ = true;
365 // start threads
366 if (DoThreaded()) {
367 for (size_t thread = 0; thread < myWorkers_.size(); thread++) {
368
369 if (SynchronizeThreads()) {
370 threadsMutexesIn_.push_back(std::make_unique<tools::Mutex>());
371 // lock each worker for input
372 threadsMutexesIn_.back()->Lock();
373
374 threadsMutexesOut_.push_back(std::make_unique<tools::Mutex>());
375 // lock each worker for output
376 threadsMutexesOut_.back()->Lock();
377 }
378 }
379 for (auto &myWorker_ : myWorkers_) {
380 myWorker_->Start();
381 }
382
383 if (SynchronizeThreads()) {
384 // unlock first thread and start ordered input/output
385 threadsMutexesIn_[0]->Unlock();
386 threadsMutexesOut_[0]->Unlock();
387 }
388 // mutex needed for merging if SynchronizeThreads()==False
389 tools::Mutex mergeMutex;
390 for (auto &myWorker : myWorkers_) {
391 myWorker->WaitDone();
392 if (!SynchronizeThreads()) {
393 mergeMutex.Lock();
394 MergeWorker(myWorker.get());
395 mergeMutex.Unlock();
396 }
397 }
398
399 } else {
400 master->Start();
401 master->WaitDone();
402 }
403
404 EndEvaluate();
405
406 myWorkers_.clear();
407 threadsMutexesIn_.clear();
408 threadsMutexesOut_.clear();
409 traj_reader_->Close();
410 }
411}
412
414 for (CGObserver *ob : observers_) {
415 ob->BeginCG(top, top_ref);
416 }
417}
418
420 for (CGObserver *ob : observers_) {
421 ob->EndCG();
422 }
423}
424
426 for (CGObserver *ob : observers_) {
427 ob->EvalConfiguration(top, top_ref);
428 }
429}
430
431std::unique_ptr<CsgApplication::Worker> CsgApplication::ForkWorker(void) {
432 throw std::runtime_error("ForkWorker not implemented in application");
433 return nullptr;
434}
435
437 throw std::runtime_error("MergeWorker not implemented in application");
438}
439
440} // namespace csg
441} // namespace votca
coarse graining engine
Definition cgengine.h:57
std::unique_ptr< TopologyMap > CreateCGTopology(const Topology &in, Topology &out)
Definition cgengine.cc:41
void AddIgnore(const std::string &pattern)
ignores molecule in mapping process
Definition cgengine.h:78
void LoadMoleculeType(const std::string &filename)
Definition cgengine.cc:70
Observer class for analysis hook.
Definition cgobserver.h:36
Worker, derived from Thread, does the work.
virtual void EvalConfiguration(Topology *top, Topology *top_ref=nullptr)=0
overload with the actual computation
std::unique_ptr< TopologyMap > map_
Index getId()
returns worker id
void Run(void) override
Run() executes the actual code.
virtual bool DoMapping(void)
overload and return true to enable mapping command line options
virtual bool EvaluateTopology(Topology *, Topology *=nullptr)
called after topology was loaded
std::vector< std::unique_ptr< tools::Mutex > > threadsMutexesIn_
stores Mutexes used to impose order for input
void ShowHelpText(std::ostream &out) override
bool EvaluateOptions() override
Process command line options.
virtual bool SynchronizeThreads(void)
virtual void EndEvaluate()
called after the last frame
void Initialize() override
Initialize application data.
std::list< CGObserver * > observers_
virtual void MergeWorker(Worker *worker)
virtual void BeginEvaluate(Topology *top, Topology *top_ref=nullptr)
called before the first frame
virtual bool DoMappingDefault(void)
if DoMapping is true, will by default require mapping or not
virtual bool DoTrajectory(void)
overload and return true to enable trajectory command line options
bool ProcessData(Worker *worker)
Gets frames from TrajectoryReader in an ordered way and, if successful, calls Worker::EvalConfigurati...
virtual bool NeedsTopology(void)
if topology is always needed
virtual std::unique_ptr< Worker > ForkWorker(void)
virtual void EvalConfiguration(Topology *top, Topology *top_ref=nullptr)
std::vector< std::unique_ptr< tools::Mutex > > threadsMutexesOut_
stores Mutexes used to impose order for output
std::vector< std::unique_ptr< Worker > > myWorkers_
std::unique_ptr< TrajectoryReader > traj_reader_
void Run(void) override
Main body of application.
virtual bool DoThreaded(void)
static void RegisterPlugins(void)
topology of the whole system
Definition topology.h:81
double getTime() const
Definition topology.h:317
BoundaryCondition::eBoxtype getBoxType() const
Definition topology.h:394
void setBox(const Eigen::Matrix3d &box, BoundaryCondition::eBoxtype boxtype=BoundaryCondition::typeAuto)
Definition topology.h:272
Index MoleculeCount() const
number of molecules in the system
Definition topology.h:144
const Eigen::Matrix3d & getBox() const
Definition topology.h:298
Index BeadCount() const
Definition topology.h:150
void CheckMoleculeNaming(void)
checks weather molecules with the same name really contain the same number of beads
Definition topology.cc:169
boost::program_options::variables_map & OptionsMap()
get available program options & descriptions
virtual void HelpText(std::ostream &out)=0
help text of application without version information
boost::program_options::options_description & VisibleOptions()
filters out the Hidden group from the options descriptions
virtual std::string ProgramName()=0
program name
virtual std::string VersionString()
version string of application
Definition application.h:55
boost::program_options::options_description_easy_init AddProgramOptions(const std::string &group="")
add option for command line
void CheckRequired(const std::string &option_name, const std::string &error_msg="")
Check weather required option is set.
void WaitDone()
WaitDone() will not exit until thread ends computation.
Definition thread.cc:66
void Start()
Starts and runs a thread.
Definition thread.cc:42
break string into words
Definition tokenizer.h:72
FileFormatFactory< TopologyReader > & TopReaderFactory()
void HelpTextHeader(const std::string &tool_name)
Definition version.cc:42
FileFormatFactory< TrajectoryReader > & TrjReaderFactory()
base class for all analysis tools
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26