Line data Source code
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 : #pragma once
11 :
12 : #include "RestartableData.h"
13 : #include "JsonIO.h"
14 : #include "MooseUtils.h"
15 : #include "ReporterState.h"
16 : #include "ReporterContext.h"
17 : #include "libmesh/parallel_object.h"
18 : #include "libmesh/dense_vector.h"
19 : #include <memory>
20 :
21 : class MooseApp;
22 : class Receiver;
23 :
24 : /**
25 : * This is a helper class for managing the storage of declared Reporter object values. This design
26 : * of the system is a generalization of the VectorPostprocessor system that the Reporter objects
27 : * replaced.
28 : *
29 : * Foremost, this object doesn't store the data. It simply acts as helper for using the restart
30 : * system of the MooseApp. This object automatically handles the old, older, ... data. All
31 : * declarations create std::pair<T, std::vector<T>> restartable data on the MooseApp, where the
32 : * first value is the current value and the data in the vector are the older data.
33 : *
34 : * The ReporterState object is a RestartableData object that serves as a helper for managing the
35 : * time history. A "context" object also exists that uses the ReporterState for performing special
36 : * operations. Refer to ReporterState.h/C for more information.
37 : *
38 : * It is important to note that the Reporter values are not threaded. However, the Reporter
39 : * objects are UserObject based, so the calculation of the values can be threaded.
40 : *
41 : * This object also relies on ReporterName objects, which are simply a combination of the
42 : * Reporter object name and the data name. If you recall the VectorPostprocessor system on which
43 : * this is based required an object name and a vector name. The ReporterName class simply provides
44 : * a convenient way to provide that information in a single object. Special Parser syntax
45 : * was also defined so that application developers do not have to have input parameters for
46 : * both the object and data names (see Parser.C/h).
47 : */
48 : class ReporterData
49 : {
50 : public:
51 : class WriteKey
52 : {
53 : /**
54 : * An object that can be passed to FEProblemBase::getReporterData to provide non-const access
55 : * the ReporterData object that is used to store reporter values.
56 : *
57 : * An attempt was made to limit access to the ReporterData as much as possible to encourage
58 : * developers to use the Reporter and ReporterInterface. This key also prevents the need for
59 : * FEProblemBase to give friend access to these classes, which gives complete access.
60 : * Thanks for the idea @loganharbour
61 : */
62 141865 : WriteKey() {} // private constructor
63 : friend class Reporter;
64 : friend class Postprocessor;
65 : friend class Receiver;
66 : friend class ConstantPostprocessor;
67 : friend class VectorPostprocessor;
68 : friend class ReporterTransferInterface;
69 : };
70 :
71 : ReporterData(MooseApp & moose_app);
72 :
73 : /**
74 : * Return True if a Reporter value with the given type and name have been created.
75 : */
76 : template <typename T>
77 : bool hasReporterValue(const ReporterName & reporter_name) const;
78 :
79 : /**
80 : * Return True if a Reporter value with any type exists with the given name.
81 : */
82 : bool hasReporterValue(const ReporterName & reporter_name) const;
83 :
84 : /**
85 : * @returns True if a ReporterState is defined for the Reporter with name \p reporter_name
86 : * and the given type.
87 : */
88 : template <typename T>
89 : bool hasReporterState(const ReporterName & reporter_name) const;
90 :
91 : /**
92 : * @returns True if a ReporterState is defined for the Reporter with name \p reporter_name.
93 : */
94 : bool hasReporterState(const ReporterName & reporter_name) const;
95 :
96 : /**
97 : * Return a list of all reporter names
98 : */
99 : std::set<ReporterName> getReporterNames() const;
100 :
101 : /**
102 : * Return a list of all postprocessor names
103 : */
104 : std::set<std::string> getPostprocessorNames() const;
105 :
106 : /**
107 : * Get all real reporter values including postprocessor and vector postprocessor values into a
108 : * dense vector
109 : */
110 : DenseVector<Real> getAllRealReporterValues() const;
111 :
112 : /**
113 : * Get full names of all real reporter values
114 : * Note: For a postprocessor, the full name is the postprocessor name plus '/value'.
115 : * For a vector postprocessor, the full name is the vector postprocessor name
116 : * plus the vector name followed by '/#' where '#' is the index of the vector.
117 : */
118 : std::vector<std::string> getAllRealReporterFullNames() const;
119 :
120 : /**
121 : * Method for returning read only references to Reporter values.
122 : * @tparam T The Reporter value C++ type.
123 : * @param reporter_name The name of the reporter value, which includes the object name and the
124 : * data name.
125 : * @param consumer The MooseObject consuming the Reporter value (for error reporting)
126 : * @param mode The mode that the value will be consumed by the by the ReporterInterface object
127 : * @param time_index (optional) When not provided or zero is provided the current value is
128 : * returned. If an index greater than zero is provided then the corresponding
129 : * old data is returned (1 = old, 2 = older, etc.).
130 : */
131 : template <typename T>
132 : const T & getReporterValue(const ReporterName & reporter_name,
133 : const MooseObject & consumer,
134 : const ReporterMode & mode,
135 : const std::size_t time_index = 0) const;
136 :
137 : /**
138 : * Method for returning a read-only reference to Reporter values that already exist.
139 : * @tparam T The Reporter value C++ type.
140 : * @param reporter_name The name of the reporter value, which includes the object name and the
141 : * data name.
142 : * @param time_index (optional) When not provided or zero is provided the current value is
143 : * returned. If an index greater than zero is provided then the corresponding
144 : * old data is returned (1 = old, 2 = older, etc.).
145 : */
146 : template <typename T>
147 : const T & getReporterValue(const ReporterName & reporter_name,
148 : const std::size_t time_index = 0) const;
149 :
150 : /**
151 : * Method for setting Reporter values that already exist.
152 : * @tparam T The Reporter value C++ type.
153 : * @param reporter_name The name of the reporter value, which includes the object name and the
154 : * data name.
155 : * @param value The value to which the Reporter will be changed to.
156 : * @param time_index (optional) When not provided or zero is provided the current value is
157 : * returned. If an index greater than zero is provided then the corresponding
158 : * old data is returned (1 = old, 2 = older, etc.).
159 : * WARNING!
160 : * This method is designed for setting values outside of the traditional interfaces such as is
161 : * necessary for Transfers. This is an advanced capability that should be used with caution.
162 : *
163 : * @see FEProblemBase::setPostprocessorValueByName
164 : */
165 : template <typename T>
166 : void setReporterValue(const ReporterName & reporter_name,
167 : const T & value,
168 : const std::size_t time_index = 0);
169 :
170 : /**
171 : * Method for setting that a specific time index is requested for a Reporter value.
172 : * @tparam T The Reporter value C++ type.
173 : * @param reporter_name The name of the reporter value, which includes the object name and the
174 : * data name.
175 : * @param time_index The time index that is needed
176 : */
177 : template <typename T>
178 : void needReporterTimeIndex(const ReporterName & reporter_name, const std::size_t time_index);
179 :
180 : ///@{
181 : /**
182 : * Method for returning a writable reference to the current Reporter value. This method is
183 : * used by the Reporter class to produce values.
184 : * @tparam T The Reporter value C++ type.
185 : * @tparam S (optional) The ReporterContext for performing specialized actions after the
186 : * values have been computed. For example, ReporterBroadcastContext automatically broadcasts
187 : * the computed value. See ReporterState.C/h for more information.
188 : * @param reporter_name The name of the reporter value, which includes the object name and the
189 : * data name.
190 : * @param mode The mode that the produced value will be computed by the Reporter object
191 : * @param producer The MooseObject that produces this value
192 : * @param args (optional) Any number of optional arguments passed into the Context type given
193 : * by the S template parameter. If S = ReporterContext then the first argument
194 : * can be used as the default value (see ReporterContext.h).
195 : *
196 : * The ReporterContext objects allow for custom handling of data (e.g., broadcasting the
197 : * value). The get/declare methods can be called in any order thus an the underlying
198 : * RestartableData object is often created by the get method before it is declared. Therefore
199 : * the custom functionality cannot be handled by specializing the
200 : * RestartableData/ReporterState object directly because the state is often created prior to
201 : * the declaration that dictates how the produced value shall be computed. Thus, the reason
202 : * for the separate ReporterContext objects.
203 : */
204 : template <typename T, typename S, typename... Args>
205 : T & declareReporterValue(const ReporterName & reporter_name,
206 : const ReporterMode & mode,
207 : const MooseObject & producer,
208 : Args &&... args);
209 :
210 : /**
211 : * Helper function for performing post calculation actions via the ReporterContext objects.
212 : *
213 : * If you recall, the original VectorPostprocessor system included the ability to perform some
214 : * scatter and broadcast actions via the special call on the storage helper object. This
215 : * is a replacement for that method that leverages the ReporterContext objects to perform
216 : * value specific actions, including some automatic operations depending how the data is
217 : * produced and consumed.
218 : *
219 : * See FEProblemBase::joinAndFinalize
220 : */
221 : void finalize(const std::string & object_name);
222 :
223 : /**
224 : * At the end of a timestep this method is called to copy the values back in time in preparation
225 : * for the next timestep.
226 : *
227 : * See FEProblemBase::advanceState
228 : */
229 : void copyValuesBack();
230 :
231 : /**
232 : * When a time step fails, this method is called to revert the current reporter values to their
233 : * old state. @see FEProblemBase::restoreSolutions
234 : *
235 : * @param verbose Set true to print whether the reporters were restored or not.
236 : */
237 : void restoreState(bool verbose = false);
238 :
239 : /**
240 : * Perform integrity check for get/declare calls
241 : */
242 : void check() const;
243 :
244 : /**
245 : * Return true if the supplied mode exists in the produced Reporter values
246 : *
247 : * @see CSV.C/h
248 : */
249 : bool hasReporterWithMode(const std::string & obj_name, const ReporterMode & mode) const;
250 :
251 : /**
252 : * @returns The ReporterContextBase associated with the Reporter with name \p reporter_name.
253 : */
254 : ///@{
255 : const ReporterContextBase & getReporterContextBase(const ReporterName & reporter_name) const;
256 : ReporterContextBase & getReporterContextBase(const ReporterName & reporter_name);
257 : ///@}
258 :
259 : /**
260 : * @Returns The ReporterStateBase associated with the Reporter with name \p reporter_name.
261 : */
262 : ///@{
263 : const ReporterStateBase & getReporterStateBase(const ReporterName & reporter_name) const;
264 : ReporterStateBase & getReporterStateBase(const ReporterName & reporter_name);
265 : ///@}
266 :
267 : /**
268 : * Return the ReporterProducerEnum for an existing ReporterValue
269 : * @param reporter_name The name of the reporter value, which includes the object name and the
270 : * data name.
271 : */
272 : const ReporterProducerEnum & getReporterMode(const ReporterName & reporter_name) const;
273 :
274 : /**
275 : * Gets information pertaining to the Reporter with state \p state and possibly
276 : * context \p context.
277 : */
278 : static std::string getReporterInfo(const ReporterStateBase & state,
279 : const ReporterContextBase * context);
280 :
281 : /**
282 : * Gets information pertaining to the Reporter with name \p reporter_name.
283 : */
284 : std::string getReporterInfo(const ReporterName & reporter_name) const;
285 :
286 : /**
287 : * Gets information about all declared/requested Reporters.
288 : */
289 : std::string getReporterInfo() const;
290 :
291 : private:
292 : /// For accessing the restart/recover system, which is where Reporter values are stored
293 : MooseApp & _app;
294 :
295 : /**
296 : * Helper method for creating the necessary RestartableData for Reporter values.
297 : * @tparam T The desired C++ type for the Reporter value
298 : * @param reporter_name Object/data name for the Reporter value
299 : * @param declare Flag indicating if the ReporterValue is being declared or read. This flag
300 : * is passed to the existing MooseApp restart/recover system that errors if a
301 : * value is declared multiple times.
302 : * @param moose_object The object requesting/declaring the state, used in error handling.
303 : */
304 : template <typename T>
305 : ReporterState<T> & getReporterStateHelper(const ReporterName & reporter_name,
306 : bool declare,
307 : const MooseObject * moose_object = nullptr) const;
308 :
309 : /**
310 : * Helper for registering data with the MooseApp to avoid cyclic includes
311 : */
312 : RestartableDataValue & getRestartableDataHelper(std::unique_ptr<RestartableDataValue> data_ptr,
313 : bool declare) const;
314 :
315 : /// Map from ReporterName -> Reporter state. We need to keep track of all of the states that are
316 : /// created so that we can check them after Reporter declaration to make sure all states have
317 : /// a producer (are delcared). We cannot check _context_ptrs, because a context is only
318 : /// defined for Reporters that have been declared. This is mutable so that it can be inserted
319 : /// into when requesting Reporter values.
320 : mutable std::map<ReporterName, ReporterStateBase *> _states;
321 :
322 : /// The ReporterContext objects are created when a value is declared. The context objects
323 : /// include a reference to the associated ReporterState values. This container stores the
324 : /// context object for each Reporter value.
325 : std::map<ReporterName, std::unique_ptr<ReporterContextBase>> _context_ptrs;
326 : };
327 :
328 : template <typename T>
329 : ReporterState<T> &
330 1247527 : ReporterData::getReporterStateHelper(const ReporterName & reporter_name,
331 : bool declare,
332 : const MooseObject * moose_object /* = nullptr */) const
333 : {
334 1247527 : if (hasReporterState(reporter_name))
335 : {
336 1169894 : const auto error_helper =
337 108 : [this, &reporter_name, &moose_object, &declare](const std::string & suffix)
338 : {
339 12 : std::stringstream oss;
340 : oss << "While " << (declare ? "declaring" : "requesting") << " a "
341 : << reporter_name.specialTypeToName() << " value with the name \""
342 12 : << reporter_name.getValueName() << "\"";
343 12 : if (!reporter_name.isPostprocessor() && !reporter_name.isVectorPostprocessor())
344 12 : oss << " and type \"" << MooseUtils::prettyCppType<T>() << "\"";
345 12 : oss << ",\na Reporter with the same name " << suffix << ".\n\n";
346 12 : oss << getReporterInfo(reporter_name);
347 :
348 12 : if (moose_object)
349 12 : moose_object->mooseError(oss.str());
350 : else
351 0 : mooseError(oss.str());
352 0 : };
353 :
354 1169894 : if (declare && hasReporterValue(reporter_name))
355 4 : error_helper("has already been declared");
356 1169890 : if (!hasReporterState<T>(reporter_name))
357 : {
358 8 : std::stringstream oss;
359 4 : oss << "has been " << (declare || !hasReporterValue(reporter_name) ? "requested" : "declared")
360 12 : << " with a different type";
361 8 : error_helper(oss.str());
362 0 : }
363 : }
364 :
365 : // Reporter states are stored as restartable data. The act of registering restartable data
366 : // may be done multiple times with the same name, which will happen when more than one
367 : // get value is done, or a get value and a declare is done. With this, we create a new
368 : // state every time, but said created state may not be the actual state if this state
369 : // is already registered as restartable data. Therefore, we create a state, and then
370 : // cast the restartable data received back to a state (which may be different than
371 : // the one we created, but that's okay)
372 1247515 : auto state_unique_ptr = std::make_unique<ReporterState<T>>(reporter_name);
373 1247515 : auto & restartable_value = getRestartableDataHelper(std::move(state_unique_ptr), declare);
374 :
375 1247515 : auto * state = dynamic_cast<ReporterState<T> *>(&restartable_value);
376 : mooseAssert(state, "Cast failed. The check above must be broken!");
377 :
378 : // See declareReporterValue for a comment on what happens if a state for the same
379 : // name is requested but with different special types. TLDR: ReporterNames with
380 : // different special types are not unique so they'll be the same entry
381 1247515 : _states.emplace(reporter_name, state);
382 :
383 1247515 : return *state;
384 1247515 : }
385 :
386 : template <typename T>
387 : const T &
388 51090 : ReporterData::getReporterValue(const ReporterName & reporter_name,
389 : const MooseObject & consumer,
390 : const ReporterMode & mode,
391 : const std::size_t time_index /* = 0 */) const
392 : {
393 51090 : auto & state = getReporterStateHelper<T>(reporter_name, /* declare = */ false, &consumer);
394 51086 : state.addConsumer(mode, consumer);
395 51086 : return state.value(time_index);
396 : }
397 :
398 : template <typename T, typename S, typename... Args>
399 : T &
400 77633 : ReporterData::declareReporterValue(const ReporterName & reporter_name,
401 : const ReporterMode & mode,
402 : const MooseObject & producer,
403 : Args &&... args)
404 : {
405 : // Get/create the ReporterState
406 77633 : auto & state = getReporterStateHelper<T>(reporter_name, /* declare = */ true, &producer);
407 :
408 : // They key in _states (ReporterName) is not unique by special type. This is done on purpose
409 : // because we want to store reporter names a single name regardless of special type.
410 : // Because of this, we have the case where someone could request a reporter value
411 : // that is later declared as a pp or a vpp value. In this case, when it is first
412 : // requested, the _state entry will have a key and name with a special type of ANY.
413 : // When it's declared here (later), we will still find the correct entry because
414 : // we don't check the special type in the key/name. But... we want the actual
415 : // key and name to represent a pp or a vpp. Therefore, we'll set it properly,
416 : // remove the entry in the map (which has the ANY key), and re-add it so that it
417 : // has the pp/vpp key. This allows us to identify Reporters that really represent
418 : // pps/vpps in output and in error reporting.
419 77625 : if (reporter_name.isPostprocessor() && !state.getReporterName().isPostprocessor())
420 : {
421 12 : state.setIsPostprocessor();
422 12 : _states.erase(reporter_name);
423 12 : _states.emplace(reporter_name, &state);
424 : }
425 96218 : else if (reporter_name.isVectorPostprocessor() &&
426 18605 : !state.getReporterName().isVectorPostprocessor())
427 : {
428 12 : state.setIsVectorPostprocessor();
429 12 : _states.erase(reporter_name);
430 12 : _states.emplace(reporter_name, &state);
431 : }
432 :
433 : mooseAssert(!_context_ptrs.count(reporter_name), "Context already exists");
434 :
435 : // Create the ReporterContext
436 77625 : auto context_ptr = std::make_unique<S>(_app, producer, state, args...);
437 77625 : context_ptr->init(mode); // initialize the mode, see ContextReporter
438 77625 : _context_ptrs.emplace(reporter_name, std::move(context_ptr));
439 :
440 155250 : return state.value();
441 77625 : }
442 :
443 : template <typename T>
444 : bool
445 1819349 : ReporterData::hasReporterValue(const ReporterName & reporter_name) const
446 : {
447 1819349 : if (!hasReporterValue(reporter_name))
448 38692 : return false;
449 1780657 : return dynamic_cast<const ReporterContext<T> *>(&getReporterContextBase(reporter_name));
450 : }
451 :
452 : template <typename T>
453 : bool
454 1169890 : ReporterData::hasReporterState(const ReporterName & reporter_name) const
455 : {
456 1169890 : if (!hasReporterState(reporter_name))
457 0 : return false;
458 1169890 : return dynamic_cast<const ReporterState<T> *>(&getReporterStateBase(reporter_name));
459 : }
460 :
461 : template <typename T>
462 : const T &
463 1114164 : ReporterData::getReporterValue(const ReporterName & reporter_name,
464 : const std::size_t time_index) const
465 : {
466 1114164 : if (!hasReporterValue<T>(reporter_name))
467 0 : mooseError("Reporter name \"",
468 : reporter_name,
469 : "\" with type \"",
470 : MooseUtils::prettyCppType<T>(),
471 : "\" is not declared.");
472 :
473 : // Force the const version of value, which does not allow for increasing time index
474 : return static_cast<const ReporterState<T> &>(
475 1114164 : getReporterStateHelper<T>(reporter_name, /* declare = */ false))
476 1114164 : .value(time_index);
477 : }
478 :
479 : template <typename T>
480 : void
481 536045 : ReporterData::setReporterValue(const ReporterName & reporter_name,
482 : const T & value,
483 : const std::size_t time_index)
484 : {
485 : // https://stackoverflow.com/questions/123758/how-do-i-remove-code-duplication-between-similar-const-and-non-const-member-func
486 536045 : const auto & me = *this;
487 536045 : const_cast<T &>(me.getReporterValue<T>(reporter_name, time_index)) = value;
488 536045 : }
489 :
490 : template <typename T>
491 : void
492 4640 : ReporterData::needReporterTimeIndex(const ReporterName & reporter_name,
493 : const std::size_t time_index)
494 : {
495 4640 : getReporterValue<T>(reporter_name, 0); // for error checking that it is declared
496 4640 : getReporterStateHelper<T>(reporter_name, /* declare = */ false).value(time_index);
497 4640 : }
498 :
499 : // This is defined here to avoid cyclic includes, see ReporterContext.h
500 : template <typename T>
501 : void
502 2768 : ReporterContext<T>::transfer(ReporterData & r_data,
503 : const ReporterName & r_name,
504 : unsigned int time_index) const
505 : {
506 2768 : r_data.setReporterValue<T>(r_name, _state.value(), time_index);
507 2768 : }
508 :
509 : // This is defined here to avoid cyclic includes, see ReporterContext.h
510 : template <typename T>
511 : void
512 608 : ReporterContext<T>::transferToVector(ReporterData & r_data,
513 : const ReporterName & r_name,
514 : dof_id_type index,
515 : unsigned int time_index) const
516 : {
517 : std::vector<T> & vec =
518 608 : const_cast<std::vector<T> &>(r_data.getReporterValue<std::vector<T>>(r_name, time_index));
519 :
520 608 : if (index >= vec.size())
521 0 : mooseError(
522 : "Requested index ", index, " is outside the bounds of the vector reporter value ", r_name);
523 608 : vec[index] = _state.value();
524 608 : }
525 :
526 : // This is defined here to avoid cyclic includes, see ReporterContext.h
527 : template <typename T>
528 : void
529 192 : ReporterContext<T>::transferFromVector(ReporterData & r_data,
530 : const ReporterName & r_name,
531 : dof_id_type index,
532 : unsigned int time_index) const
533 : {
534 : if constexpr (is_std_vector<T>::value)
535 : {
536 192 : if (index >= _state.value().size())
537 0 : mooseError("Requested index ",
538 : index,
539 : " is outside the bounds of the vector reporter value ",
540 : r_name);
541 :
542 : using R = typename T::value_type;
543 192 : r_data.setReporterValue<R>(r_name, _state.value()[index], time_index);
544 : }
545 : else
546 : {
547 0 : libmesh_ignore(r_data);
548 0 : libmesh_ignore(r_name);
549 0 : libmesh_ignore(index);
550 0 : libmesh_ignore(time_index);
551 0 : mooseError("transferFromVector can only be used for reporter types that are specializatons of "
552 : "std::vector.");
553 : }
554 192 : }
555 :
556 : // This is defined here to avoid cyclic includes, see ReporterContext.h
557 : template <typename T>
558 : void
559 189 : ReporterGeneralContext<T>::declareClone(ReporterData & r_data,
560 : const ReporterName & r_name,
561 : const ReporterMode & mode,
562 : const MooseObject & producer) const
563 : {
564 189 : r_data.declareReporterValue<T, ReporterGeneralContext<T>>(r_name, mode, producer);
565 189 : }
566 :
567 : // This is defined here to avoid cyclic includes, see ReporterContext.h
568 : template <typename T>
569 : void
570 423 : ReporterGeneralContext<T>::declareVectorClone(ReporterData & r_data,
571 : const ReporterName & r_name,
572 : const ReporterMode & mode,
573 : const MooseObject & producer) const
574 : {
575 423 : r_data.declareReporterValue<std::vector<T>, ReporterVectorContext<T>>(r_name, mode, producer);
576 423 : }
577 :
578 : // This is defined here to avoid cyclic includes, see ReporterContext.h
579 : template <typename T>
580 : void
581 0 : ReporterVectorContext<T>::declareClone(ReporterData &,
582 : const ReporterName &,
583 : const ReporterMode &,
584 : const MooseObject &) const
585 : {
586 0 : mooseError("Cannot create clone with ReporterVectorContext.");
587 : }
588 :
589 : // This is defined here to avoid cyclic includes, see ReporterContext.h
590 : template <typename T>
591 : void
592 0 : ReporterVectorContext<T>::declareVectorClone(ReporterData &,
593 : const ReporterName &,
594 : const ReporterMode &,
595 : const MooseObject &) const
596 : {
597 0 : mooseError("Cannot create clone with ReporterVectorContext.");
598 : }
|