https://mooseframework.inl.gov
PNGOutput.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #ifdef MOOSE_HAVE_LIBPNG
11 
12 #include <fstream>
13 #include "PNGOutput.h"
14 #include "FEProblemBase.h"
15 #include "NonlinearSystem.h"
16 #include "AuxiliarySystem.h"
17 #include "libmesh/mesh_tools.h"
18 
19 registerMooseObject("MooseApp", PNGOutput);
20 
23 {
25  params.addParam<bool>("transparent_background",
26  false,
27  "Determination of whether the background will be transparent.");
28  params.addRequiredParam<VariableName>("variable",
29  "The name of the variable to use when creating the image");
30  params.addParam<Real>("max", 1, "The maximum for the variable we want to use");
31  params.addParam<Real>("min", 0, "The minimum for the variable we want to use");
32  MooseEnum color("GRAY BRYW BWR RWB BR");
33  params.addRequiredParam<MooseEnum>("color", color, "Choose the color scheme to use.");
34  params.addRangeCheckedParam<unsigned int>(
35  "resolution", 25, "resolution>0", "The length of the longest side of the image in pixels.");
36  params.addRangeCheckedParam<Real>("out_bounds_shade",
37  .5,
38  "out_bounds_shade>=0 & out_bounds_shade<=1",
39  "Color for the parts of the image that are out of bounds."
40  "Value is between 1 and 0.");
41  params.addRangeCheckedParam<Real>("transparency",
42  1,
43  "transparency>=0 & transparency<=1",
44  "Value is between 0 and 1"
45  "where 0 is completely transparent and 1 is completely opaque. "
46  "Default transparency of the image is no transparency.");
47  params.addClassDescription("Output data in the PNG format");
48  return params;
49 }
50 
52  : FileOutput(parameters),
53  _resolution(getParam<unsigned int>("resolution")),
54  _color(parameters.get<MooseEnum>("color")),
55  _transparent_background(getParam<bool>("transparent_background")),
56  _transparency(getParam<Real>("transparency")),
57  _nl_sys_num(libMesh::invalid_uint),
58  _variable(getParam<VariableName>("variable")),
59  _max(getParam<Real>("max")),
60  _min(getParam<Real>("min")),
61  _out_bounds_shade(getParam<Real>("out_bounds_shade"))
62 {
63 }
64 
65 // Funtion for making the _mesh_function object.
66 void
68 {
69 
70  // The number assigned to the variable. Used to build the correct mesh. Default is 0.
71  unsigned int variable_number = 0;
72 
73  // PNGOutput does not currently scale for running in parallel.
74  if (processor_id() != 0)
75  mooseInfo("PNGOutput is not currently scalable.");
76 
77  bool var_found = false;
79  {
80  variable_number = _problem_ptr->getAuxiliarySystem().getVariable(0, _variable).number();
81  var_found = true;
82  }
83 
84  else
85  for (const auto nl_sys_num : make_range(_problem_ptr->numNonlinearSystems()))
87  {
88  variable_number =
90  _nl_sys_num = nl_sys_num;
91  var_found = true;
92  }
93 
94  if (!var_found)
95  paramError("variable", "This doesn't exist.");
96 
97  const std::vector<unsigned int> var_nums = {variable_number};
98 
99  // If we want the background to be transparent, we need a number over 1.
101  _out_bounds_shade = 2;
102 
103  // Find the values that will be used for rescaling purposes.
105 
106  // Set up the mesh_function
108  _mesh_function = std::make_unique<libMesh::MeshFunction>(
109  *_es_ptr,
112  var_nums);
113  else
114  _mesh_function = std::make_unique<libMesh::MeshFunction>(
115  *_es_ptr,
118  var_nums);
119  _mesh_function->init();
120 
121  // Need to enable out of mesh with the given control color scaled in reverse
122  // so when scaling is done, this value retains it's original value.
123  _mesh_function->enable_out_of_mesh_mode(reverseScale(_out_bounds_shade));
124 }
125 
126 // Function to find the min and max values so that all the values can be scaled between the two.
127 void
129 {
130  // The max and min.
131  // If the max value wasn't specified in the input file, find it from the system.
132  if (!_pars.isParamSetByUser("max"))
133  {
136  else
138  }
139  else
140  _scaling_max = _max;
141 
142  // If the min value wasn't specified in the input file, find it from the system.
143  if (!_pars.isParamSetByUser("min"))
144  {
147  else
149  }
150  else
151  _scaling_min = _min;
152 
153  // The amount the values will need to be shifted.
154  _shift_value = 0;
155 
156  // Get the shift value.
157  if (_scaling_min != 0)
158  {
159  // Shiftvalue will be the same magnitude, but
160  // going in the opposite direction of the scalingMin
162  }
163 
164  // Shift the max.
166 }
167 
168 // Function to apply the scale to the data points.
169 // Needed to be able to see accurate images that cover the appropriate color spectrum.
170 inline Real
171 PNGOutput::applyScale(Real value_to_scale)
172 {
173  return ((value_to_scale + _shift_value) / _scaling_max);
174 }
175 
176 // Function to reverse the scaling that happens to a value.
177 // Needed to be able to accurately control the _out_bounds_shade.
178 inline Real
179 PNGOutput::reverseScale(Real value_to_unscale)
180 {
181  return ((value_to_unscale * _scaling_max) - _shift_value);
182 }
183 
184 // Function that controls the colorization of the png image for non-grayscale images.
185 void
186 PNGOutput::setRGB(png_byte * rgb, Real selection)
187 {
188  // With this system we have a color we start with when the value is 0 and another it approaches as
189  // the value increases all the way to 255. If we want it to approach another color from that new
190  // color, it will do so for the next 255, so the transition is from 256 - 511. For each
191  // additional color we want to transition to, we need another 255. Transitioning from no color, or
192  // black to Red then Green then Blue then the values of from black as it becomes Red would be 0 -
193  // 255, Red to Green as 256 - 511 and then Green to Blue as 512 - 767 which gives us our total
194  // colorSpectrum of 0 - 767, which includes those colors and each of their states in the
195  // transistion.
196  unsigned int number_of_destination_colors = 1;
197  switch (_color)
198  {
199  // BRYW. Three destination colors (R,Y,W).
200  case 1:
201  number_of_destination_colors = 3;
202  break;
203 
204  // BWR. Two destination colors (W,R).
205  case 2:
206  number_of_destination_colors = 2;
207  break;
208 
209  // RWB. Two destination colors (W,B).
210  case 3:
211  number_of_destination_colors = 2;
212  break;
213 
214  // BR. One destination color (R).
215  case 4:
216  number_of_destination_colors = 1;
217  break;
218  }
219 
220  // We need to convert the number of colors into the spectrum max, then convert the value from the
221  // mesh to a point somewhere in the range of 0 to color_spectrum_max.
222  auto color_spectrum_max = (256 * number_of_destination_colors) - 1;
223  auto color = (unsigned int)(selection * color_spectrum_max);
224 
225  // Unless we specifically say some part is transparent, we want the whole image to be opaque.
226  auto tran = (unsigned int)(_transparency * 255);
227 
228  // Make sure everything is within our colorSpectrum. If it's bigger, then we want a
229  // transparent background.
230  if (color > color_spectrum_max)
231  {
232  color = color_spectrum_max;
233  tran = 0;
234  }
235 
236  auto magnitude = color % 256;
237 
238  switch (_color)
239  {
240  // Current color scheme: Blue->Red->Yellow->White
241  case 1:
242  // Blue->Red
243  if (color < 256)
244  {
245  rgb[0] = magnitude;
246  rgb[1] = 0;
247  rgb[2] = 50; // 255 - magnitude;
248  }
249  // Red->Yellow
250  else if (color < 512)
251  {
252  rgb[0] = 255;
253  rgb[1] = magnitude;
254  rgb[2] = 0;
255  }
256  // Yellow->White
257  else
258  {
259  rgb[0] = 255;
260  rgb[1] = 255;
261  rgb[2] = magnitude;
262  }
263  break;
264 
265  // Color Scheme: Blue->White->Red
266  // Using the RGB values found in Paraview
267  case 2:
268  // Blue->White
269  if (color < 256)
270  {
271  rgb[0] = (int)(255.0 * (0.231373 + (0.002485 * (float)magnitude)));
272  rgb[1] = (int)(255.0 * (0.298039 + (0.002223 * (float)magnitude)));
273  rgb[2] = (int)(255.0 * (0.752941 + (0.000439 * (float)magnitude)));
274  }
275  // White->Red
276  else
277  {
278  rgb[0] = (int)(255.0 * (0.865003 - (0.000624 * (float)magnitude)));
279  rgb[1] = (int)(255.0 * (0.865003 - (0.003331 * (float)magnitude)));
280  rgb[2] = (int)(255.0 * (0.865003 - (0.002808 * (float)magnitude)));
281  }
282  break;
283 
284  // Red->White->Blue
285  case 3:
286  // Red->White
287  if (color < 256)
288  {
289  rgb[0] = 255;
290  rgb[1] = magnitude;
291  rgb[2] = magnitude;
292  }
293  // White->Blue
294  else
295  {
296  rgb[0] = 255 - magnitude;
297  rgb[1] = 255 - magnitude;
298  rgb[2] = 255;
299  }
300  break;
301 
302  // Blue->Red
303  case 4:
304  // Blue->Red
305  rgb[0] = magnitude;
306  rgb[1] = 0;
307  rgb[2] = 255 - magnitude;
308  break;
309  }
310  // Add any transparency.
311  rgb[3] = tran;
312 }
313 
314 void
316 {
317  makeMeshFunc();
318  _box = MeshTools::create_bounding_box(*_mesh_ptr);
319 
320  // Make sure this happens on processor 0
321  if (processor_id() == 0)
322  makePNG();
323 }
324 
325 // Function the writes the PNG out to the appropriate filename.
326 void
328 {
329  // Get the max and min of the BoundingBox
330  Point max_point = _box.max();
331  Point min_point = _box.min();
332 
333  // The the total distance on the x and y axes.
334  Real dist_x = max_point(0) - min_point(0);
335  Real dist_y = max_point(1) - min_point(1);
336 
337  // Width and height for the PNG image.
338  Real width;
339  Real height;
340 
341  // Variable to record the resolution variable after normalized to work with pixels in longest
342  // direction.
343  Real normalized_resolution;
344 
345  // The longer dimension becomes the value to which we scale the other.
346  if (dist_x > dist_y)
347  {
348  width = _resolution;
349  height = (_resolution / dist_x) * dist_y;
350  normalized_resolution = (((Real)_resolution) / dist_x);
351  }
352  else
353  {
354  height = _resolution;
355  width = (_resolution / dist_y) * dist_x;
356  normalized_resolution = (((Real)_resolution) / dist_y);
357  }
358 
359  // Create the filename based on base and the test step number.
360  std::ostringstream png_file;
361  png_file << _file_base << "_" << std::setfill('0') << std::setw(3) << _t_step << ".png";
362 
363  // libpng is built on C, so by default it takes FILE*.
364  FILE * fp = nullptr;
365  png_structp pngp = nullptr;
366  png_infop infop = nullptr;
367  // Required depth for proper image clarity.
368  Real depth = 8;
369  // Allocate resources.
370  std::vector<png_byte> row((width + 1) * 4);
371 
372  // Check if we can open and write to the file.
373  MooseUtils::checkFileWriteable(png_file.str());
374 
375  // Open the file with write and bit modes.
376  fp = fopen(png_file.str().c_str(), "wb");
377 
378  pngp = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
379  if (!pngp)
380  mooseError("Failed to make the pointer string for the png.");
381 
382  infop = png_create_info_struct(pngp);
383  if (!infop)
384  mooseError("Failed to make an info pointer for the png.");
385 
386  // Initializes the IO for the png. Needs FILE* to compile.
387  png_init_io(pngp, fp);
388 
389  // Set up the PNG header.
390  png_set_IHDR(pngp,
391  infop,
392  width,
393  height,
394  depth,
395  (_color ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_GRAY),
396  PNG_INTERLACE_NONE,
397  PNG_COMPRESSION_TYPE_DEFAULT,
398  PNG_FILTER_TYPE_DEFAULT);
399 
400  png_write_info(pngp, infop);
401 
402  // Initiallizing the point that will be used for populating the mesh values.
403  // Initializing x, y, z to zero so that we don't access the point before it's
404  // been set. z = 0 for all the png's.
405  Point pt(0, 0, 0);
406 
407  // Dense vector that we can pass into the _mesh_function to fill with a value for a given point.
408  DenseVector<Number> dv(0);
409 
410  // Loop through to create the image.
411  for (Real y = max_point(1); y >= min_point(1); y -= 1. / normalized_resolution)
412  {
413  pt(1) = y;
414  unsigned int index = 0;
415  for (Real x = min_point(0); x <= max_point(0); x += 1. / normalized_resolution)
416  {
417  pt(0) = x;
418  (*_mesh_function)(pt, _time, dv, nullptr);
419 
420  // Determine whether to create the PNG in color or grayscale
421  if (_color)
422  setRGB(&row.data()[index * 4], applyScale(dv(0)));
423  else
424  row.data()[index] = applyScale(dv(0)) * 255;
425 
426  index++;
427  }
428  png_write_row(pngp, row.data());
429  }
430 
431  // Close the file and take care of some other png end stuff.
432  png_write_end(pngp, nullptr);
433  if (fp != nullptr)
434  fclose(fp);
435  if (infop != nullptr)
436  png_free_data(pngp, infop, PNG_FREE_ALL, -1);
437  if (pngp != nullptr)
438  png_destroy_write_struct(&pngp, &infop);
439 }
440 
441 #endif
unsigned int _nl_sys_num
What nonlinear system the variable is in.
Definition: PNGOutput.h:72
void mooseInfo(Args &&... args) const
Definition: MooseBase.h:317
Real _min
Definition: PNGOutput.h:80
void makePNG()
Function that creates the PNG.
Definition: PNGOutput.C:327
const InputParameters & _pars
The object&#39;s parameters.
Definition: MooseBase.h:362
std::unique_ptr< libMesh::MeshFunction > _mesh_function
Pointer to the libMesh::MeshFunction object in which the read data is stored.
Definition: PNGOutput.h:65
const unsigned int invalid_uint
const MooseEnum _color
Way to specify color vs grayscale image creation.
Definition: PNGOutput.h:56
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition: MooseBase.h:435
virtual std::size_t numNonlinearSystems() const override
void makeMeshFunc()
Function to create the mesh_function.
Definition: PNGOutput.C:67
static InputParameters validParams()
Basic constructor. Takes parameters passed in to create a PNGOutput object.
Definition: PNGOutput.C:22
unsigned int number() const
Get variable number coming from libMesh.
const bool _transparent_background
Indicates whether to make the background transparent.
Definition: PNGOutput.h:59
Real reverseScale(Real value_to_unscale)
Function for reversing the applyScale function.
Definition: PNGOutput.C:179
T * get(const std::unique_ptr< T > &u)
The MooseUtils::get() specializations are used to support making forwards-compatible code changes fro...
Definition: MooseUtils.h:1155
Real _out_bounds_shade
Value of the colors that are outside of the libmesh bounds.
Definition: PNGOutput.h:88
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::string _file_base
The base filename from the input paramaters.
Definition: FileOutput.h:89
void calculateRescalingValues()
Function to populate values to the variables used for scaling.
Definition: PNGOutput.C:128
int & _t_step
The current time step.
Definition: Output.h:220
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
const unsigned int _resolution
Variable to determine the size, or resolution, of the image.
Definition: PNGOutput.h:53
virtual libMesh::DofMap & dofMap()
Gets writeable reference to the dof map.
Definition: SystemBase.C:1155
virtual NumericVector< Number > & serializedSolution()
Returns a reference to a serialized version of the solution vector for this subproblem.
Definition: SystemBase.C:1638
registerMooseObject("MooseApp", PNGOutput)
FEProblemBase * _problem_ptr
Pointer the the FEProblemBase object for output object (use this)
Definition: Output.h:185
const Real _transparency
Controls transparency level for the general image.
Definition: PNGOutput.h:62
static InputParameters validParams()
Definition: FileOutput.C:24
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
Real _scaling_max
Definition: PNGOutput.h:84
Real _max
Variables that store the max and min of the values in the variable used.
Definition: PNGOutput.h:79
Real _shift_value
Definition: PNGOutput.h:85
virtual Real max() const =0
virtual Real min() const =0
AuxiliarySystem & getAuxiliarySystem()
bool checkFileWriteable(const std::string &filename, bool throw_on_unwritable=true)
Check if the file is writable (path exists and permissions)
Definition: MooseUtils.C:303
virtual bool hasVariable(const std::string &var_name) const
Query a system for a variable.
Definition: SystemBase.C:843
virtual NonlinearSystem & getNonlinearSystem(const unsigned int sys_num)
Real applyScale(Real value_to_scale)
Function for applying scaling to given values.
Definition: PNGOutput.C:171
bool isParamSetByUser(const std::string &name) const
Method returns true if the parameter was set by the user.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Real _scaling_min
Values used for rescaling the image.
Definition: PNGOutput.h:83
BoundingBox _box
The boundaries of the image.
Definition: PNGOutput.h:68
libMesh::EquationSystems * _es_ptr
Reference the the libMesh::EquationSystems object that contains the data.
Definition: Output.h:194
VariableName _variable
The name of the variable to use to create the png.
Definition: PNGOutput.h:75
IntRange< T > make_range(T beg, T end)
MooseMesh * _mesh_ptr
A convenience pointer to the current mesh (reference or displaced depending on "use_displaced") ...
Definition: Output.h:197
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:267
PNGOutput(const InputParameters &parameters)
Definition: PNGOutput.C:51
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
MooseVariableFieldBase & getVariable(THREAD_ID tid, const std::string &var_name) const
Gets a reference to a variable of with specified name.
Definition: SystemBase.C:90
An outputter with filename support.
Definition: FileOutput.h:20
void setRGB(png_byte *rgb, Real selection)
Method for assigning color values to the PNG.
Definition: PNGOutput.C:186
processor_id_type processor_id() const
Real & _time
The current time for output purposes.
Definition: Output.h:214
void ErrorVector unsigned int
virtual void output()
Called to run the functions in this class.
Definition: PNGOutput.C:315