BioDynaMo  v1.05.169-060d86f1
adaptor.cc
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------
2 //
3 // Copyright (C) 2021 CERN & University of Surrey for the benefit of the
4 // BioDynaMo collaboration. All Rights Reserved.
5 //
6 // Licensed under the Apache License, Version 2.0 (the "License");
7 // you may not use this file except in compliance with the License.
8 //
9 // See the LICENSE file distributed with this work for details.
10 // See the NOTICE file distributed with this work for additional information
11 // regarding copyright ownership.
12 //
13 // -----------------------------------------------------------------------------
14 
15 #include <cstdlib>
16 #include <fstream>
17 #include <memory>
18 #include <sstream>
19 
20 #include "core/util/io.h"
25 
26 #ifndef __ROOTCLING__
27 
28 #include <vtkCPDataDescription.h>
29 #include <vtkCPInputDataDescription.h>
30 #include <vtkCPProcessor.h>
31 #include <vtkCPPythonScriptPipeline.h>
32 #include <vtkDoubleArray.h>
33 #include <vtkFieldData.h>
34 #include <vtkIdTypeArray.h>
35 #include <vtkImageData.h>
36 #include <vtkImageDataStreamer.h>
37 #include <vtkIntArray.h>
38 #include <vtkNew.h>
39 #include <vtkPointData.h>
40 #include <vtkPoints.h>
41 #include <vtkStringArray.h>
42 #include <vtkUnstructuredGrid.h>
43 #include <vtkXMLImageDataWriter.h>
44 #include <vtkXMLPImageDataWriter.h>
45 #include <vtkXMLPUnstructuredGridWriter.h>
46 #include <vtkXMLUnstructuredGridWriter.h>
47 
48 namespace bdm {
49 
50 // ----------------------------------------------------------------------------
52  vtkCPProcessor* g_processor_ = nullptr;
53  std::unordered_map<std::string, VtkAgents*> vtk_agents_;
54  std::unordered_map<std::string, VtkDiffusionGrid*> vtk_dgrids_;
55  vtkCPDataDescription* data_description_ = nullptr;
56 };
57 
58 // ----------------------------------------------------------------------------
59 std::atomic<uint64_t> ParaviewAdaptor::counter_;
60 
61 // ----------------------------------------------------------------------------
63  counter_++;
64  impl_ = std::unique_ptr<ParaviewAdaptor::ParaviewImpl>(
66 }
67 
68 // ----------------------------------------------------------------------------
70  auto* param = Simulation::GetActive()->GetParam();
71  counter_--;
72 
73  if (impl_) {
74  if (counter_ == 0 && impl_->g_processor_) {
75  impl_->g_processor_->RemoveAllPipelines();
76  impl_->g_processor_->Finalize();
77  impl_->g_processor_->Delete();
78  impl_->g_processor_ = nullptr;
79  }
80  if (param->export_visualization &&
81  param->visualization_export_generate_pvsm) {
84  }
85 
86  if (impl_->data_description_ != nullptr) {
87  impl_->data_description_->Delete();
88  }
89  for (auto& el : impl_->vtk_agents_) {
90  delete el.second;
91  }
92  for (auto& el : impl_->vtk_dgrids_) {
93  delete el.second;
94  }
95  }
96 }
97 
98 // ----------------------------------------------------------------------------
100  if (!initialized_) {
101  Initialize();
102  initialized_ = true;
103  }
104 
105  auto* sim = Simulation::GetActive();
106  auto* param = sim->GetParam();
107  uint64_t total_steps = sim->GetScheduler()->GetSimulatedSteps();
108  if (total_steps % param->visualization_interval != 0) {
109  return;
110  }
111 
112  real_t time = param->simulation_time_step * total_steps;
113  impl_->data_description_->SetTimeData(time, total_steps);
114 
116 
117  if (param->insitu_visualization) {
119  }
120  if (param->export_visualization) {
122  }
123 }
124 
125 // ----------------------------------------------------------------------------
127  auto* sim = Simulation::GetActive();
128  auto* param = sim->GetParam();
129 
130  if (param->insitu_visualization && impl_->g_processor_ == nullptr) {
131 #ifdef __APPLE__
132  Log::Warning("ParaviewAdaptor",
133  "Insitu visualization is currently not supported on MacOS. "
134  "Please use export visualization.");
135 #endif // __APPLE__
136  impl_->g_processor_ = vtkCPProcessor::New();
137  impl_->g_processor_->Initialize();
138  }
139 
140  if (param->insitu_visualization) {
141  const std::string& script =
142  ParaviewAdaptor::BuildPythonScriptString(param->pv_insitu_pipeline);
143  std::ofstream ofs;
144  auto* sim = Simulation::GetActive();
145  std::string final_python_script_name =
146  Concat(sim->GetOutputDir(), "/insitu_pipeline.py");
147  ofs.open(final_python_script_name);
148  ofs << script;
149  ofs.close();
150  vtkNew<vtkCPPythonScriptPipeline> pipeline;
151  pipeline->Initialize(final_python_script_name.c_str());
152  impl_->g_processor_->AddPipeline(pipeline.GetPointer());
153  }
154 
155  if (impl_->data_description_ == nullptr) {
156  impl_->data_description_ = vtkCPDataDescription::New();
157  } else {
158  impl_->data_description_->Delete();
159  impl_->data_description_ = vtkCPDataDescription::New();
160  }
161  impl_->data_description_->SetTimeData(0, 0);
162 
163  for (auto& pair : param->visualize_agents) {
164  impl_->vtk_agents_[pair.first.c_str()] =
165  new VtkAgents(pair.first.c_str(), impl_->data_description_);
166  }
167  for (auto& entry : param->visualize_diffusion) {
168  impl_->vtk_dgrids_[entry.name] =
169  new VtkDiffusionGrid(entry.name, impl_->data_description_);
170  }
171 }
172 
173 // ----------------------------------------------------------------------------
175 #ifdef BDM_PV_EXPERIMENTAL
176  vtkCommand::kOpenGLCacheEnable = true;
177  vtkCommand::kOpenGLCacheIndex = 0;
178 #endif // BDM_PV_EXPERIMENTAL
179  impl_->g_processor_->RequestDataDescription(impl_->data_description_);
180  impl_->data_description_->ForceOutputOn();
181  impl_->g_processor_->CoProcess(impl_->data_description_);
182 }
183 
184 // ----------------------------------------------------------------------------
187 
188  auto step = impl_->data_description_->GetTimeStep();
189 
190  for (auto& el : impl_->vtk_agents_) {
191  el.second->WriteToFile(step);
192  }
193 
194  for (auto& el : impl_->vtk_dgrids_) {
195  el.second->WriteToFile(step);
196  }
197 }
198 
199 // ----------------------------------------------------------------------------
203  if (impl_->data_description_->GetUserData() == nullptr) {
204  vtkNew<vtkStringArray> json;
205  json->SetName("metadata");
206  json->InsertNextValue(
207  GenerateSimulationInfoJson(impl_->vtk_agents_, impl_->vtk_dgrids_));
208  vtkNew<vtkFieldData> field;
209  field->AddArray(json);
210  impl_->data_description_->SetUserData(field);
211  }
212 }
213 
214 // ----------------------------------------------------------------------------
217  for (auto& pair : impl_->vtk_agents_) {
218  const auto& agents = rm->GetTypeIndex()->GetType(pair.second->GetTClass());
219  pair.second->Update(&agents);
220  }
221 }
222 
223 // ----------------------------------------------------------------------------
226 
227  rm->ForEachDiffusionGrid([&](DiffusionGrid* grid) {
228  auto it = impl_->vtk_dgrids_.find(grid->GetContinuumName());
229  if (it != impl_->vtk_dgrids_.end()) {
230  it->second->Update(grid);
231  }
232  });
233 }
234 
235 // ----------------------------------------------------------------------------
237  // create simulation_info.json
239  std::ofstream ofstr;
240  auto* sim = Simulation::GetActive();
241  ofstr.open(Concat(sim->GetOutputDir(), "/", kSimulationInfoJson));
242  ofstr << GenerateSimulationInfoJson(impl_->vtk_agents_, impl_->vtk_dgrids_);
243  ofstr.close();
245  }
246 }
247 
248 // ----------------------------------------------------------------------------
253  auto* sim = Simulation::GetActive();
254  std::stringstream python_cmd;
255  std::string pv_dir = std::getenv("ParaView_DIR");
256  std::string bdmsys = std::getenv("BDMSYS");
257 
258  python_cmd << pv_dir << "/bin/pvbatch --force-offscreen-rendering " << bdmsys
259  << "/include/core/visualization/paraview/generate_pv_state.py "
260  << sim->GetOutputDir() << "/" << kSimulationInfoJson;
261  int ret_code = system(python_cmd.str().c_str());
262  if (ret_code) {
263  // pvbatch can die part way through and still leave a usable state file:
264  // generate_pv_state.py writes the .pvsm before the render pass that crashes
265  // on headless macOS 26 (NSOpenGLContext was removed there). So judge the
266  // outcome by whether the state file exists, rather than by the platform we
267  // happen to be compiled for - a genuine ParaView failure must stay fatal
268  // everywhere, macOS included.
269  auto state_file =
270  Concat(sim->GetOutputDir(), "/", sim->GetUniqueName(), ".pvsm");
271  if (FileExists(state_file)) {
272  Log::Warning("ParaviewAdaptor::GenerateParaviewState",
273  "pvbatch exited with code ", ret_code,
274  " but wrote a ParaView state file. It may be incomplete: "
275  "the animation time range is the most likely omission.\n",
276  "Command\n", python_cmd.str());
277  } else {
278  Log::Fatal("ParaviewAdaptor::GenerateParaviewState",
279  "Error during generation of ParaView state. pvbatch exited "
280  "with code ",
281  ret_code, " and wrote no state file to ", state_file, "\n",
282  "Command\n", python_cmd.str());
283  }
284  }
285 }
286 
287 // ----------------------------------------------------------------------------
289  const std::string& python_script) {
290  std::stringstream script;
291 
292  std::ifstream ifs;
293  ifs.open(python_script, std::ifstream::in);
294  if (!ifs.is_open()) {
295  Log::Fatal("ParaviewAdaptor::BuildPythonScriptString",
296  Concat("Python script (", python_script,
297  ") was not found or could not be opened."));
298  }
299  script << ifs.rdbuf();
300  ifs.close();
301 
302  std::string default_python_script =
303  std::string(std::getenv("BDMSYS")) +
304  std::string(
305  "/include/core/visualization/paraview/default_insitu_pipeline.py");
306 
307  std::ifstream ifs_default;
308  ifs_default.open(default_python_script, std::ifstream::in);
309  if (!ifs_default.is_open()) {
310  Log::Fatal("ParaviewAdaptor::BuildPythonScriptString",
311  Concat("Python script (", default_python_script,
312  ") was not found or could not be opened."));
313  }
314  script << std::endl << ifs_default.rdbuf();
315  ifs_default.close();
316  return script.str();
317 }
318 
319 } // namespace bdm
320 
321 #else
322 
323 namespace bdm {
324 
326 
328 
330 
332 
334 
336 
338  const std::string& python_script) {}
339 } // namespace bdm
340 
341 #endif
const std::string & GetContinuumName() const
Returns the name of the continuum.
static void Fatal(const std::string &location, const Args &... parts)
Prints fatal error message.
Definition: log.h:115
static void Warning(const std::string &location, const Args &... parts)
Prints warning message.
Definition: log.h:67
void BuildAgentsVTKStructures()
Create the required vtk objects to visualize agents.
Definition: adaptor.cc:215
void WriteSimulationInfoJsonFile()
Definition: adaptor.cc:236
void CreateVtkObjects()
Creates the VTK objects that represent the agents in ParaView.
Definition: adaptor.cc:200
void Visualize() override
Visualize one timestep based on the configuration in Param
Definition: adaptor.cc:99
static std::atomic< uint64_t > counter_
Definition: adaptor.h:54
void BuildDiffusionGridVTKStructures()
Create the required vtk objects to visualize diffusion grids.
Definition: adaptor.cc:224
static void GenerateParaviewState()
Definition: adaptor.cc:252
static std::string BuildPythonScriptString(const std::string &python_script)
Definition: adaptor.cc:288
~ParaviewAdaptor() override
Definition: adaptor.cc:69
bool initialized_
only needed for insitu visualization
Definition: adaptor.h:57
void InsituVisualization()
Execute the insitu pipelines that were defined in Initialize
Definition: adaptor.cc:174
bool simulation_info_json_generated_
Definition: adaptor.h:58
std::unique_ptr< ParaviewImpl > impl_
Definition: adaptor.h:50
void ExportVisualization()
Definition: adaptor.cc:185
const TypeIndex * GetTypeIndex() const
void ForEachDiffusionGrid(TFunctor &&f) const
ResourceManager * GetResourceManager()
Returns the ResourceManager instance.
Definition: simulation.cc:244
static Simulation * GetActive()
This function returns the currently active Simulation simulation.
Definition: simulation.cc:68
const Param * GetParam() const
Returns the simulation parameters.
Definition: simulation.cc:254
const std::vector< Agent * > & GetType(TClass *tclass) const
Definition: type_index.cc:70
Definition: agent.cc:39
std::string Concat(const Args &... parts)
Concatenates all arguments into a string. Equivalent to streaming all arguments into a stringstream a...
Definition: string.h:70
bool FileExists(const std::string &file_name)
Definition: io.cc:78
double real_t
Definition: real_t.h:21
std::string GenerateSimulationInfoJson(const std::unordered_map< std::string, VtkAgents * > &vtk_agents, const std::unordered_map< std::string, VtkDiffusionGrid * > &vtk_dgrids)
Definition: helper.cc:24
void WriteToFile(const std::string &filename, const std::string &content)
Definition: io.cc:83
vtkCPProcessor * g_processor_
Definition: adaptor.cc:52
vtkCPDataDescription * data_description_
Definition: adaptor.cc:55
std::unordered_map< std::string, VtkDiffusionGrid * > vtk_dgrids_
Definition: adaptor.cc:54
std::unordered_map< std::string, VtkAgents * > vtk_agents_
Definition: adaptor.cc:53