https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
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",
31 "The maximum for the variable we want to use. If unspecified, the maximum "
32 "of the variable's DOF values is used");
33 params.addParam<Real>("min",
34 "The minimum for the variable we want to use. If unspecified, the minimum "
35 "of the variable's DOF values is used");
36
37 params.addParam<Point>("frame_center",
38 Point(0, 0, 0),
39 "Center of the frame when defining the rectangular plotting region "
40 "dimensions. Applied after the frame rotation");
41 params.addParam<Point>(
42 "first_axis",
43 Point(1, 0, 0),
44 "First axis to generate the rectangular plotting region. The axis vector will be normalized");
45 params.addParam<Point>("second_axis",
46 Point(0, 1, 0),
47 "Second axis to generate the rectangular plotting region. The axis vector "
48 "will be normalized");
49 params.addParam<Real>(
50 "min_x",
51 "Minimum coordinate along the first axis. Defaults to mesh bounding box min X value");
52 params.addParam<Real>(
53 "max_x",
54 "Maximum coordinate along the first axis. Defaults to mesh bounding box max X value");
55 params.addParam<Real>(
56 "min_y",
57 "Minimum coordinate along the second axis. Default to mesh bounding box min Y value");
58 params.addParam<Real>(
59 "max_y",
60 "Maximum coordinate along the second axis. Default to mesh bounding box max Y value");
61 params.addParamNamesToGroup("frame_center first_axis second_axis min_x max_x min_y max_y",
62 "Rectangular box extent");
63
64 MooseEnum color("GRAY BRYW BWR RWB BR", "BR");
65 params.addRequiredParam<MooseEnum>("color", color, "Choose the color scheme to use.");
66 params.addRangeCheckedParam<unsigned int>(
67 "resolution", 25, "resolution>0", "The length of the longest side of the image in pixels.");
68 params.addRangeCheckedParam<Real>("out_bounds_shade",
69 .5,
70 "out_bounds_shade>=0 & out_bounds_shade<=1",
71 "Color for the parts of the image that are out of bounds."
72 "Value is between 1 and 0.");
73 params.addRangeCheckedParam<Real>("transparency",
74 1,
75 "transparency>=0 & transparency<=1",
76 "Value is between 0 and 1"
77 "where 0 is completely transparent and 1 is completely opaque. "
78 "Default transparency of the image is no transparency.");
79 params.addParamNamesToGroup("color resolution out_bounds_shade transparency", "Plotting");
80 params.addClassDescription("Output data in the PNG format");
81 return params;
82}
83
85 : FileOutput(parameters),
86 _resolution(getParam<unsigned int>("resolution")),
87 _color(parameters.get<MooseEnum>("color")),
88 _transparent_background(getParam<bool>("transparent_background")),
89 _transparency(getParam<Real>("transparency")),
90 _nl_sys_num(libMesh::invalid_uint),
91 _variable(getParam<VariableName>("variable")),
92 _max(isParamValid("max") ? getParam<Real>("max") : std::numeric_limits<Real>::max()),
93 _min(isParamValid("min") ? getParam<Real>("min") : -std::numeric_limits<Real>::max()),
94 _out_bounds_shade(getParam<Real>("out_bounds_shade"))
95{
96 if (!MooseUtils::absoluteFuzzyEqual(
97 getParam<Point>("first_axis") * getParam<Point>("second_axis"), 0))
98 paramWarning("first_axis", "The two axis are not orthogonal");
99}
100
101// Function for making the _mesh_function object.
102void
104{
105 // The number assigned to the variable. Used to build the correct mesh. Default is 0.
106 unsigned int variable_number = 0;
107
108 // PNGOutput does not currently scale for running in parallel.
109 if (processor_id() != 0)
110 mooseInfo("PNGOutput is not currently scalable.");
111
112 bool var_found = false;
114 {
116 var_found = true;
117 }
118
119 else
120 for (const auto nl_sys_num : make_range(_problem_ptr->numNonlinearSystems()))
122 {
123 variable_number =
125 _nl_sys_num = nl_sys_num;
126 var_found = true;
127 }
128
129 if (!var_found)
130 paramError("variable", "This doesn't exist.");
131
132 const std::vector<unsigned int> var_nums = {variable_number};
133
134 // If we want the background to be transparent, we need a number over 1.
137
138 // Find the values that will be used for rescaling purposes.
140
141 // Set up the mesh_function
143 _mesh_function = std::make_unique<libMesh::MeshFunction>(
144 *_es_ptr,
147 var_nums);
148 else
149 _mesh_function = std::make_unique<libMesh::MeshFunction>(
150 *_es_ptr,
153 var_nums);
154 _mesh_function->init();
155
156 // Need to enable out of mesh with the given control color scaled in reverse
157 // so when scaling is done, this value retains it's original value.
158 _mesh_function->enable_out_of_mesh_mode(reverseScale(_out_bounds_shade));
159}
160
161// Function to find the min and max values so that all the values can be scaled between the two.
162void
164{
165 // The max and min.
166 // If the max value wasn't specified in the input file, find it from the system.
167 if (!_pars.isParamValid("max"))
168 {
171 else
173 }
174 else
176
177 // If the min value wasn't specified in the input file, find it from the system.
178 if (!_pars.isParamValid("min"))
179 {
182 else
184 }
185 else
187
188 // The amount the values will need to be shifted.
189 _shift_value = 0;
190
191 // Get the shift value.
192 if (_scaling_min != 0)
193 {
194 // Shiftvalue will be the same magnitude, but
195 // going in the opposite direction of the scalingMin
197 }
198
199 // Shift the max.
201}
202
203// Function to apply the scale to the data points.
204// Needed to be able to see accurate images that cover the appropriate color spectrum.
205inline Real
206PNGOutput::applyScale(Real value_to_scale)
207{
208 return ((value_to_scale + _shift_value) / _scaling_max);
209}
210
211// Function to reverse the scaling that happens to a value.
212// Needed to be able to accurately control the _out_bounds_shade.
213inline Real
214PNGOutput::reverseScale(Real value_to_unscale)
215{
216 return ((value_to_unscale * _scaling_max) - _shift_value);
217}
218
219// Function that controls the colorization of the png image for non-grayscale images.
220void
221PNGOutput::setRGB(png_byte * rgb, Real selection)
222{
223 // With this system we have a color we start with when the value is 0 and another it approaches as
224 // the value increases all the way to 255. If we want it to approach another color from that new
225 // color, it will do so for the next 255, so the transition is from 256 - 511. For each
226 // additional color we want to transition to, we need another 255. Transitioning from no color, or
227 // black to Red then Green then Blue then the values of from black as it becomes Red would be 0 -
228 // 255, Red to Green as 256 - 511 and then Green to Blue as 512 - 767 which gives us our total
229 // colorSpectrum of 0 - 767, which includes those colors and each of their states in the
230 // transistion.
231 unsigned int number_of_destination_colors = 1;
232 switch (_color)
233 {
234 // BRYW. Three destination colors (R,Y,W).
235 case 1:
236 number_of_destination_colors = 3;
237 break;
238
239 // BWR. Two destination colors (W,R).
240 case 2:
241 number_of_destination_colors = 2;
242 break;
243
244 // RWB. Two destination colors (W,B).
245 case 3:
246 number_of_destination_colors = 2;
247 break;
248
249 // BR. One destination color (R).
250 case 4:
251 number_of_destination_colors = 1;
252 break;
253 }
254
255 // We need to convert the number of colors into the spectrum max, then convert the value from the
256 // mesh to a point somewhere in the range of 0 to color_spectrum_max.
257 auto color_spectrum_max = (256 * number_of_destination_colors) - 1;
258 auto color = (unsigned int)(selection * color_spectrum_max);
259
260 // Unless we specifically say some part is transparent, we want the whole image to be opaque.
261 auto tran = (unsigned int)(_transparency * 255);
262
263 // Make sure everything is within our colorSpectrum. If it's bigger, then we want a
264 // transparent background.
265 if (color > color_spectrum_max)
266 {
267 color = color_spectrum_max;
268 tran = 0;
269 }
270
271 auto magnitude = color % 256;
272
273 switch (_color)
274 {
275 // Current color scheme: Blue->Red->Yellow->White
276 case 1:
277 // Blue->Red
278 if (color < 256)
279 {
280 rgb[0] = magnitude;
281 rgb[1] = 0;
282 rgb[2] = 50; // 255 - magnitude;
283 }
284 // Red->Yellow
285 else if (color < 512)
286 {
287 rgb[0] = 255;
288 rgb[1] = magnitude;
289 rgb[2] = 0;
290 }
291 // Yellow->White
292 else
293 {
294 rgb[0] = 255;
295 rgb[1] = 255;
296 rgb[2] = magnitude;
297 }
298 break;
299
300 // Color Scheme: Blue->White->Red
301 // Using the RGB values found in Paraview
302 case 2:
303 // Blue->White
304 if (color < 256)
305 {
306 rgb[0] = (int)(255.0 * (0.231373 + (0.002485 * (float)magnitude)));
307 rgb[1] = (int)(255.0 * (0.298039 + (0.002223 * (float)magnitude)));
308 rgb[2] = (int)(255.0 * (0.752941 + (0.000439 * (float)magnitude)));
309 }
310 // White->Red
311 else
312 {
313 rgb[0] = (int)(255.0 * (0.865003 - (0.000624 * (float)magnitude)));
314 rgb[1] = (int)(255.0 * (0.865003 - (0.003331 * (float)magnitude)));
315 rgb[2] = (int)(255.0 * (0.865003 - (0.002808 * (float)magnitude)));
316 }
317 break;
318
319 // Red->White->Blue
320 case 3:
321 // Red->White
322 if (color < 256)
323 {
324 rgb[0] = 255;
325 rgb[1] = magnitude;
326 rgb[2] = magnitude;
327 }
328 // White->Blue
329 else
330 {
331 rgb[0] = 255 - magnitude;
332 rgb[1] = 255 - magnitude;
333 rgb[2] = 255;
334 }
335 break;
336
337 // Blue->Red
338 case 4:
339 // Blue->Red
340 rgb[0] = magnitude;
341 rgb[1] = 0;
342 rgb[2] = 255 - magnitude;
343 break;
344 }
345 // Add any transparency.
346 rgb[3] = tran;
347}
348
349void
351{
352 makeMeshFunc();
354
355 // Make sure this happens on processor 0
356 if (processor_id() == 0)
357 makePNG();
358}
359
360// Function the writes the PNG out to the appropriate filename.
361void
363{
364 // Get the max and min of the BoundingBox
365 Point max_point = _box.max();
366 Point min_point = _box.min();
367
368 // Modify the bounding box if the user passed different bounds
369 if (isParamValid("min_x"))
370 min_point(0) = getParam<Real>("min_x");
371 if (isParamValid("min_y"))
372 min_point(1) = getParam<Real>("min_y");
373 if (isParamValid("max_x"))
374 max_point(0) = getParam<Real>("max_x");
375 if (isParamValid("max_y"))
376 max_point(1) = getParam<Real>("max_y");
377
378 // The total distance on the two plotting axes.
379 Real dist_x = max_point(0) - min_point(0);
380 Real dist_y = max_point(1) - min_point(1);
381
382 // Width and height for the PNG image.
383 Real width;
384 Real height;
385
386 // The longer dimension becomes the value to which we scale the other.
387 if (dist_x > dist_y)
388 {
389 width = _resolution;
390 height = (_resolution / dist_x) * dist_y;
391 }
392 else
393 {
394 height = _resolution;
395 width = (_resolution / dist_y) * dist_x;
396 }
397
398 // Create the filename based on base and the test step number.
399 std::ostringstream png_file;
400 png_file << _file_base << "_" << std::setfill('0') << std::setw(3) << _t_step << ".png";
401
402 // libpng is built on C, so by default it takes FILE*.
403 FILE * fp = nullptr;
404 png_structp pngp = nullptr;
405 png_infop infop = nullptr;
406 // Required depth for proper image clarity.
407 Real depth = 8;
408 // Allocate resources.
409 std::vector<png_byte> row((width + 1) * 4);
410
411 // Check if we can open and write to the file.
412 MooseUtils::checkFileWriteable(png_file.str());
413
414 // Open the file with write and bit modes.
415 fp = fopen(png_file.str().c_str(), "wb");
416
417 // Verify the libpng we are linked against at runtime matches the headers we
418 // were compiled against. PNG_LIBPNG_VER is major*10000 + minor*100 + release;
419 // libpng only guarantees ABI within the same major.minor, so a mismatch there
420 // can cause png_create_write_struct to segfault rather than fail cleanly.
421 const png_uint_32 runtime_libpng_ver = png_access_version_number();
422 if (runtime_libpng_ver / 100 != PNG_LIBPNG_VER / 100)
423 {
424 mooseWarning("PNG output skipped for ",
425 png_file.str(),
426 ": runtime libpng version ",
427 runtime_libpng_ver,
428 " is ABI-incompatible with the version ",
429 static_cast<png_uint_32>(PNG_LIBPNG_VER),
430 " used at compile time.");
431 if (fp != nullptr)
432 fclose(fp);
433 return;
434 }
435
436 pngp = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
437 if (!pngp)
438 mooseError("Failed to make the pointer string for the png.");
439
440 infop = png_create_info_struct(pngp);
441 if (!infop)
442 mooseError("Failed to make an info pointer for the png.");
443
444 // Initializes the IO for the png. Needs FILE* to compile.
445 png_init_io(pngp, fp);
446
447 // Set up the PNG header.
448 png_set_IHDR(pngp,
449 infop,
450 width,
451 height,
452 depth,
453 (_color ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_GRAY),
454 PNG_INTERLACE_NONE,
455 PNG_COMPRESSION_TYPE_DEFAULT,
456 PNG_FILTER_TYPE_DEFAULT);
457
458 png_write_info(pngp, infop);
459
460 // Initializing the point that will be used for populating the mesh values.
461 Point pt(0, 0, 0);
462
463 // Pre-compute the transformations
464 Point center(0, 0, 0);
465 if (isParamValid("frame_center"))
466 center = getParam<Point>("frame_center");
467 else
468 center = Point((min_point(0) + max_point(0)) / 2, (min_point(1) + max_point(1)) / 2, 0);
469
470 const auto first_axis = getParam<Point>("first_axis").unit();
471 const auto second_axis = getParam<Point>("second_axis").unit();
472
473 // Dense vector that we can pass into the _mesh_function to fill with a value for a given point.
474 DenseVector<Number> dv(0);
475
476 // Loop through to create the image.
477 for (const auto iy : make_range((unsigned int)height))
478 {
479 for (const auto ix : make_range((unsigned int)width + 1))
480 {
481 // Move along the axis from the center
482 // Center the point in the pixel
483 pt = center +
484 (min_point(0) + Real(ix) / _resolution * (max_point(0) - min_point(0))) * first_axis +
485 (min_point(1) + Real(iy) / _resolution * (max_point(1) - min_point(1))) * second_axis;
486
487 (*_mesh_function)(pt, _time, dv, nullptr);
488
489 // Determine whether to create the PNG in color or grayscale
490 if (_color)
491 setRGB(&row.data()[ix * 4], applyScale(dv(0)));
492 else
493 row.data()[ix] = applyScale(dv(0)) * 255;
494 }
495 png_write_row(pngp, row.data());
496 }
497
498 // Close the file and take care of some other png end stuff.
499 png_write_end(pngp, nullptr);
500 if (fp != nullptr)
501 fclose(fp);
502 if (infop != nullptr)
503 png_free_data(pngp, infop, PNG_FREE_ALL, -1);
504 if (pngp != nullptr)
505 png_destroy_write_struct(&pngp, &infop);
506}
507
508#endif
void mooseInfo(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
Definition MooseError.h:401
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition MooseError.h:345
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
Point center
Definition MortarUtils.C:58
registerMooseObject("MooseApp", PNGOutput)
void ErrorVector unsigned int
virtual NonlinearSystem & getNonlinearSystem(const unsigned int sys_num)
AuxiliarySystem & getAuxiliarySystem()
virtual std::size_t numNonlinearSystems() const override
An outputter with filename support.
Definition FileOutput.h:21
static InputParameters validParams()
Definition FileOutput.C:24
std::string _file_base
The base filename from the input paramaters.
Definition FileOutput.h:89
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...
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 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...
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 addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another,...
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:457
const InputParameters & _pars
The object's parameters.
Definition MooseBase.h:384
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
unsigned int number() const
Get variable number coming from libMesh.
FEProblemBase * _problem_ptr
Pointer the the FEProblemBase object for output object (use this)
Definition Output.h:185
int & _t_step
The current time step.
Definition Output.h:220
libMesh::EquationSystems * _es_ptr
Reference the the libMesh::EquationSystems object that contains the data.
Definition Output.h:194
Real & _time
The current time for output purposes.
Definition Output.h:214
MooseMesh * _mesh_ptr
A convenience pointer to the current mesh (reference or displaced depending on "use_displaced")
Definition Output.h:197
Output class to create a 2D figure in PNG format.
Definition PNGOutput.h:26
virtual void output()
Called to run the functions in this class.
Definition PNGOutput.C:350
Real applyScale(Real value_to_scale)
Function for applying scaling to given values.
Definition PNGOutput.C:206
Real _shift_value
Definition PNGOutput.h:87
static InputParameters validParams()
Definition PNGOutput.C:22
const bool _transparent_background
Indicates whether to make the background transparent.
Definition PNGOutput.h:61
void calculateRescalingValues()
Function to populate values to the variables used for scaling.
Definition PNGOutput.C:163
Real _scaling_min
Values used for rescaling the image.
Definition PNGOutput.h:85
void setRGB(png_byte *rgb, Real selection)
Method for assigning color values to the PNG.
Definition PNGOutput.C:221
Real _out_bounds_shade
Value of the colors that are outside of the libmesh bounds.
Definition PNGOutput.h:90
Real _scaling_max
Definition PNGOutput.h:86
void makePNG()
Function that creates the PNG.
Definition PNGOutput.C:362
Real _max
Variables that store the max and min of the values in the variable used.
Definition PNGOutput.h:81
const Real _transparency
Controls transparency level for the general image.
Definition PNGOutput.h:64
BoundingBox _box
The boundaries of the image.
Definition PNGOutput.h:70
Real reverseScale(Real value_to_unscale)
Function for reversing the applyScale function.
Definition PNGOutput.C:214
std::unique_ptr< libMesh::MeshFunction > _mesh_function
Pointer to the libMesh::MeshFunction object in which the read data is stored.
Definition PNGOutput.h:67
unsigned int _nl_sys_num
What nonlinear system the variable is in.
Definition PNGOutput.h:74
const MooseEnum _color
Way to specify color vs grayscale image creation.
Definition PNGOutput.h:58
Real _min
Definition PNGOutput.h:82
VariableName _variable
The name of the variable to use to create the png.
Definition PNGOutput.h:77
PNGOutput(const InputParameters &parameters)
Definition PNGOutput.C:84
void makeMeshFunc()
Function to create the mesh_function.
Definition PNGOutput.C:103
const unsigned int _resolution
Variable to determine the size, or resolution, of the image.
Definition PNGOutput.h:55
void paramWarning(const std::string &param, Args... args) const
MooseVariableFieldBase & getVariable(THREAD_ID tid, const std::string &var_name) const
Gets a reference to a variable of with specified name.
Definition SystemBase.C:91
virtual bool hasVariable(const std::string &var_name) const
Query a system for a variable.
Definition SystemBase.C:850
virtual libMesh::DofMap & dofMap()
Gets writeable reference to the dof map.
virtual NumericVector< Number > & serializedSolution()
Returns a reference to a serialized version of the solution vector for this subproblem.
virtual Real max() const=0
virtual Real min() const=0
processor_id_type processor_id() const
bool checkFileWriteable(const std::string &filename, bool throw_on_unwritable)
Definition MooseUtils.C:318
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
const unsigned int invalid_uint