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 "THMObject.h"
13 : #include "FlowModel.h"
14 : #include "THMProblem.h"
15 : #include "InputParameterWarehouse.h"
16 : #include "LoggingInterface.h"
17 : #include "NamingInterface.h"
18 : #include "ADFunctorInterface.h"
19 :
20 : class THMProblem;
21 : class THMMesh;
22 : class ThermalHydraulicsApp;
23 : class Convergence;
24 :
25 : /**
26 : * Base class for THM components
27 : */
28 : class Component : public THMObject,
29 : public LoggingInterface,
30 : public NamingInterface,
31 : public ADFunctorInterface
32 : {
33 : public:
34 : Component(const InputParameters & parameters);
35 :
36 : /// Component setup status type
37 : enum EComponentSetupStatus
38 : {
39 : CREATED, ///< only created
40 : MESH_PREPARED, ///< mesh set up
41 : INITIALIZED_PRIMARY, ///< mesh set up, called primary init
42 : INITIALIZED_SECONDARY, ///< mesh set up, called both inits
43 : CHECKED ///< mesh set up, called both inits, checked
44 : };
45 :
46 : /// Return a string for the setup status
47 : std::string stringify(EComponentSetupStatus status) const;
48 :
49 : /**
50 : * Get the component name
51 : * @return The name of the component. For composite component, return its parent name
52 : */
53 : const std::string & cname() const;
54 :
55 15614 : Component * parent() { return _parent; }
56 :
57 : /**
58 : * Const reference to mesh, which can be called at any point
59 : *
60 : * Note that overloading mesh() was not possible due to the need to call this
61 : * const version, even when the component is not const.
62 : */
63 3948 : const THMMesh & constMesh() const { return _mesh; }
64 :
65 : /**
66 : * Non-const reference to THM mesh, which can only be called before the end of mesh setup
67 : */
68 : THMMesh & mesh();
69 :
70 : /**
71 : * Gets the THM problem
72 : */
73 : THMProblem & getTHMProblem() const;
74 :
75 : /**
76 : * Test if a parameter exists in the object's input parameters
77 : * @param name The name of the parameter
78 : * @return true if the parameter exists, false otherwise
79 : */
80 : template <typename T>
81 : bool hasParam(const std::string & name) const;
82 :
83 : /**
84 : * Returns a list of names of components that this component depends upon
85 : */
86 : const std::vector<std::string> & getDependencies() const { return _dependencies; }
87 :
88 : /**
89 : * Wrapper function for \c init() that marks the function as being called
90 : */
91 : void executeInit();
92 :
93 : /**
94 : * Wrapper function for \c initSecondary() that marks the function as being called
95 : */
96 : void executeInitSecondary();
97 :
98 : /**
99 : * Wrapper function for \c check() that marks the function as being called
100 : */
101 : void executeCheck() const;
102 :
103 : /**
104 : * Wrapper function for \c setupMesh() that marks the function as being called
105 : */
106 : void executeSetupMesh();
107 :
108 : /**
109 : * Adds relationship managers for the component
110 : */
111 15677 : virtual void addRelationshipManagers(Moose::RelationshipManagerType /*input_rm_type*/) {}
112 :
113 6296 : virtual void addVariables() {}
114 :
115 24 : virtual void addMooseObjects() {}
116 :
117 : /**
118 : * Gets the Component's nonlinear Convergence object if it has one
119 : */
120 : virtual Convergence * getNonlinearConvergence() const;
121 :
122 : /**
123 : * Return a reference to a component via a parameter name
124 : * @tparam T the type of the component we are requesting
125 : * @param name The parameter name that has the component name
126 : */
127 : template <typename T>
128 : const T & getComponent(const std::string & name) const;
129 :
130 : /**
131 : * Return a reference to a component given its name
132 : * @tparam T the type of the component we are requesting
133 : * @param cname The name of the component
134 : */
135 : template <typename T>
136 : const T & getComponentByName(const std::string & cname) const;
137 :
138 : /**
139 : * Check the existence and type of a component via a parameter name
140 : * @tparam T the type of the component we are requesting
141 : * @param name The parameter name that has the component name
142 : * @return true if the component with given name and type exists, otherwise false
143 : */
144 : template <typename T>
145 : bool hasComponent(const std::string & name) const;
146 :
147 : /**
148 : * Check the existence and type of a component given its name
149 : * @tparam T the type of the component we are requesting
150 : * @param cname The name of the component
151 : * @return true if the component with given name and type exists, otherwise false
152 : */
153 : template <typename T>
154 : bool hasComponentByName(const std::string & cname) const;
155 :
156 : /**
157 : * Connect with control logic
158 : */
159 : void connectObject(const InputParameters & params,
160 : const std::string & mooseName,
161 : const std::string & name) const;
162 : /**
163 : * Connect with control logic
164 : */
165 : void connectObject(const InputParameters & params,
166 : const std::string & mooseName,
167 : const std::string & name,
168 : const std::string & par_name) const;
169 :
170 : /**
171 : * Makes a function controllable if it is constant
172 : *
173 : * @param[in] fn_name name of the function
174 : * @param[in] control_name name of control parameter
175 : * @param[in] param name of controlled parameter
176 : */
177 : void makeFunctionControllableIfConstant(const FunctionName & fn_name,
178 : const std::string & control_name,
179 : const std::string & param = "value") const;
180 :
181 : /**
182 : * Throws an error if the supplied setup status of this component has not been reached
183 : *
184 : * This is useful for getter functions that rely on data initialized after the
185 : * constructor; if an error is not thrown, then uninitialized data could be
186 : * returned from these functions.
187 : *
188 : * @param[in] status Setup status that this component must have reached
189 : */
190 : void checkSetupStatus(const EComponentSetupStatus & status) const;
191 :
192 : /**
193 : * Checks that a component exists
194 : *
195 : * @param[in] comp_name name of the component
196 : */
197 : void checkComponentExistsByName(const std::string & comp_name) const;
198 :
199 : /**
200 : * Checks that the component of a certain type exists, where the name is given by a parameter
201 : *
202 : * @tparam T enforced type of component
203 : * @param[in] param parameter name for component name
204 : */
205 : template <typename T>
206 : void checkComponentOfTypeExists(const std::string & param) const;
207 :
208 : /**
209 : * Checks that the component of a certain type exists
210 : *
211 : * @tparam T enforced type of component
212 : * @param[in] comp_name component name
213 : */
214 : template <typename T>
215 : void checkComponentOfTypeExistsByName(const std::string & comp_name) const;
216 :
217 : /**
218 : * Logs an error
219 : */
220 : template <typename... Args>
221 269 : void logError(Args &&... args) const
222 : {
223 269 : logComponentError(cname(), std::forward<Args>(args)...);
224 269 : }
225 :
226 : /**
227 : * Logs a warning
228 : */
229 : template <typename... Args>
230 46 : void logWarning(Args &&... args) const
231 : {
232 46 : logComponentWarning(cname(), std::forward<Args>(args)...);
233 46 : }
234 :
235 : /**
236 : * Adds a component name to the list of dependencies
237 : *
238 : * @param[in] dependency name of component to add to list of dependencies
239 : */
240 : void addDependency(const std::string & dependency);
241 :
242 : /**
243 : * Gets an enum parameter
244 : *
245 : * This function takes the name of a MooseEnum parameter that is tied to an
246 : * enum defined in THM. If the value is invalid, an error will be logged,
247 : * and a negative integer will be cast into the enum type.
248 : *
249 : * @tparam T enum type
250 : * @param[in] param name of the MooseEnum parameter
251 : * @param[in] log_error If true, log an error if the valid is invalid
252 : */
253 : template <typename T>
254 : T getEnumParam(const std::string & param, bool log_error = true) const;
255 :
256 : /**
257 : * Whether the problem is transient
258 : */
259 13931 : bool problemIsTransient() const { return getTHMProblem().isTransient(); }
260 :
261 : /**
262 : * Gets the node IDs corresponding to this component
263 : */
264 : const std::vector<dof_id_type> & getNodeIDs() const;
265 :
266 : /**
267 : * Gets the element IDs corresponding to this component
268 : */
269 : const std::vector<dof_id_type> & getElementIDs() const;
270 :
271 : /**
272 : * Gets the subdomain names for this component
273 : *
274 : * @return vector of subdomain names for this component
275 : */
276 : virtual const std::vector<SubdomainName> & getSubdomainNames() const;
277 :
278 : /**
279 : * Gets the coordinate system types for this component
280 : *
281 : * @return vector of coordinate system types for this component
282 : */
283 : virtual const std::vector<Moose::CoordinateSystemType> & getCoordSysTypes() const;
284 :
285 : /**
286 : * Runtime check to make sure that a parameter of specified type exists in the component's input
287 : * parameters
288 : *
289 : * This is intended to help developers write code. The idea is to provide a useful message when
290 : * developers make typos, etc. If this check fails, the code execution will be stopped.
291 : *
292 : * @tparam T The type of the parameter to be checked
293 : * @param function_name The name of the function calling this method
294 : * @param param_name The name of the parameter to be checked
295 : */
296 : template <typename T>
297 : void insistParameterExists(const std::string & function_name,
298 : const std::string & param_name) const;
299 :
300 : /**
301 : * Checks that a parameter value is less than a value
302 : *
303 : * @tparam T type of parameter
304 : * @param[in] param parameter name
305 : * @param[in] value_max value which parameter value must be less than
306 : */
307 : template <typename T>
308 : void checkParameterValueLessThan(const std::string & param, const T & value_max) const;
309 :
310 : /**
311 : * Checks that the size of a vector parameter is less than a value
312 : *
313 : * @tparam T type of element in the vector parameter
314 : * @param[in] param parameter name
315 : * @param[in] n_entries value which parameter size must be less than
316 : */
317 : template <typename T>
318 : void checkSizeLessThan(const std::string & param, const unsigned int & n_entries) const;
319 :
320 : /**
321 : * Checks that the size of a vector parameter is greater than a value
322 : *
323 : * @tparam T type of element in the vector parameter
324 : * @param[in] param parameter name
325 : * @param[in] n_entries value which parameter size must be greater than
326 : */
327 : template <typename T>
328 : void checkSizeGreaterThan(const std::string & param, const unsigned int & n_entries) const;
329 :
330 : /**
331 : * Checks that the size of two vector parameters are equal
332 : *
333 : * @tparam T1 type of element in the first vector parameter
334 : * @tparam T2 type of element in the second vector parameter
335 : * @param[in] param1 first parameter name
336 : * @param[in] param2 second parameter name
337 : */
338 : template <typename T1, typename T2>
339 : void checkEqualSize(const std::string & param1, const std::string & param2) const;
340 :
341 : /**
342 : * Checks that the size of a vector parameter equals a value
343 : *
344 : * This version does not supply a description to the value.
345 : *
346 : * @tparam T type of element in the vector parameter
347 : * @param[in] param parameter name
348 : * @param[in] n_entries value which parameter size must be equal to
349 : */
350 : template <typename T>
351 : void checkSizeEqualsValue(const std::string & param, const unsigned int & n_entries) const;
352 :
353 : /**
354 : * Checks that the size of a vector parameter equals a value
355 : *
356 : * This version supplies a description to the value.
357 : *
358 : * @tparam T type of element in the vector parameter
359 : * @param[in] param parameter name
360 : * @param[in] n_entries value which parameter size must be equal to
361 : * @param[in] description description of the value that size must be equal to
362 : */
363 : template <typename T>
364 : void checkSizeEqualsValue(const std::string & param,
365 : const unsigned int & n_entries,
366 : const std::string & description) const;
367 :
368 : /**
369 : * Checks that the size of a vector parameter equals the value of another parameter
370 : *
371 : * @tparam T1 type of element in the vector parameter
372 : * @tparam T2 type of the parameter whose value is compared to size
373 : * @param[in] param1 vector parameter name
374 : * @param[in] param2 name of parameter whose value is compared to size
375 : */
376 : template <typename T1, typename T2>
377 : void checkSizeEqualsParameterValue(const std::string & param1, const std::string & param2) const;
378 :
379 : /**
380 : * Checks that exactly one parameter out of a list is provided
381 : *
382 : * @param[in] params vector of parameter names
383 : * @param[in] need_one_specified Need one of the parameters specified?
384 : */
385 : void checkMutuallyExclusiveParameters(const std::vector<std::string> & params,
386 : bool need_one_specified = true) const;
387 :
388 : protected:
389 : /**
390 : * Initializes the component
391 : *
392 : * The reason this function exists (as opposed to just having everything in
393 : * the constructor) is because some initialization depends on all components
394 : * existing, since many components couple to other components. Therefore,
395 : * when deciding whether code should go into the constructor or this function,
396 : * one should use the following reasoning: if an operation does not require
397 : * the existence of other components, then put that operation in the
398 : * constructor; otherwise, put it in this function.
399 : */
400 1368 : virtual void init() {}
401 :
402 : /**
403 : * Perform secondary initialization, which relies on init() being called
404 : * for all components.
405 : */
406 8876 : virtual void initSecondary() {}
407 :
408 : /**
409 : * Check the component integrity
410 : */
411 343 : virtual void check() const {}
412 :
413 : /**
414 : * Performs mesh setup such as creating mesh or naming mesh sets
415 : */
416 2409 : virtual void setupMesh() {}
417 :
418 : /**
419 : * Method to add a relationship manager for the objects being added to the system. Relationship
420 : * managers have to be added relatively early. In many cases before the Action::act() method
421 : * is called.
422 : *
423 : * This method was copied from Action.
424 : *
425 : * @param moose_object_pars The MooseObject to inspect for RelationshipManagers to add
426 : */
427 : void addRelationshipManagersFromParameters(const InputParameters & moose_object_pars);
428 :
429 : Node * addNode(const Point & pt);
430 : Elem * addNodeElement(dof_id_type node);
431 :
432 : /**
433 : * Sets the next subdomain ID, name, and coordinate system
434 : *
435 : * @param[in] subdomain_id subdomain index
436 : * @param[in] subdomain_name name of the new subdomain
437 : * @param[in] coord_system type of coordinate system
438 : */
439 : virtual void
440 : setSubdomainInfo(SubdomainID subdomain_id,
441 : const std::string & subdomain_name,
442 5393 : const Moose::CoordinateSystemType & coord_system = Moose::COORD_XYZ);
443 :
444 : /// Pointer to a parent component (used in composed components)
445 : Component * _parent;
446 :
447 : /// THM problem this component is part of
448 : /// TODO: make _sim private (applications need to switch to getters to avoid breaking).
449 : /// Also, rename to "_thm_problem" at that point.
450 : THMProblem & _sim;
451 :
452 : /// The Factory associated with the MooseApp
453 : Factory & _factory;
454 :
455 : const Real & _zero;
456 :
457 : /// The THM mesh
458 : /// TODO: make _mesh private (applications need to switch to getters to avoid breaking)
459 : THMMesh & _mesh;
460 :
461 : /// Node IDs of this component
462 : std::vector<dof_id_type> _node_ids;
463 : /// Element IDs of this component
464 : std::vector<dof_id_type> _elem_ids;
465 :
466 : /// List of subdomain IDs this components owns
467 : std::vector<SubdomainID> _subdomain_ids;
468 : /// List of subdomain names this components owns
469 : std::vector<SubdomainName> _subdomain_names;
470 : /// List of coordinate system for each subdomain
471 : std::vector<Moose::CoordinateSystemType> _coord_sys;
472 :
473 : private:
474 : /**
475 : * Method for adding a single relationship manager
476 : *
477 : * This method was copied from Action.
478 : *
479 : * @param moose_object_pars The parameters of the MooseObject that requested the RM
480 : * @param rm_name The class type of the RM, e.g. ElementSideNeighborLayers
481 : * @param rm_type The RelationshipManagerType, e.g. geometric, algebraic, coupling
482 : * @param rm_input_parameter_func The RM callback function, typically a lambda defined in the
483 : * requesting MooseObject's validParams function
484 : * @param sys_type A RMSystemType that can be used to limit the systems and consequent dof_maps
485 : * that the RM can be attached to
486 : */
487 : void
488 : addRelationshipManager(const InputParameters & moose_object_pars,
489 : std::string rm_name,
490 : Moose::RelationshipManagerType rm_type,
491 : Moose::RelationshipManagerInputParameterCallback rm_input_parameter_func,
492 : Moose::RMSystemType sys_type = Moose::RMSystemType::NONE);
493 :
494 : /// Component setup status
495 : mutable EComponentSetupStatus _component_setup_status;
496 :
497 : /// List of names of components that this component depends upon
498 : std::vector<std::string> _dependencies;
499 :
500 : public:
501 : static InputParameters validParams();
502 : };
503 :
504 : template <typename T>
505 : bool
506 33073 : Component::hasParam(const std::string & name) const
507 : {
508 33073 : return parameters().have_parameter<T>(name);
509 : }
510 :
511 : template <typename T>
512 : const T &
513 950 : Component::getComponent(const std::string & pname) const
514 : {
515 : const std::string & comp_name = getParam<std::string>(pname);
516 950 : return getComponentByName<T>(comp_name);
517 : }
518 :
519 : template <typename T>
520 : const T &
521 : Component::getComponentByName(const std::string & comp_name) const
522 : {
523 31035 : return _sim.getComponentByName<T>(comp_name);
524 : }
525 :
526 : template <typename T>
527 : bool
528 482 : Component::hasComponent(const std::string & pname) const
529 : {
530 : const std::string & comp_name = getParam<std::string>(pname);
531 482 : return hasComponentByName<T>(comp_name);
532 : }
533 :
534 : template <typename T>
535 : bool
536 : Component::hasComponentByName(const std::string & comp_name) const
537 : {
538 47634 : if (_sim.hasComponentOfType<T>(comp_name))
539 : return true;
540 : else
541 : return false;
542 : }
543 :
544 : template <typename T>
545 : T
546 8722 : Component::getEnumParam(const std::string & param, bool log_error) const
547 : {
548 : const MooseEnum & moose_enum = getParam<MooseEnum>(param);
549 8722 : const T value = THM::stringToEnum<T>(moose_enum);
550 8722 : if (log_error && static_cast<int>(value) < 0) // cast necessary for scoped enums
551 : {
552 : // Get the keys from the MooseEnum. Unfortunately, this returns a list of
553 : // *all* keys, including the invalid key that was supplied. Thus, that key
554 : // needs to be manually excluded below.
555 0 : const std::vector<std::string> & keys = moose_enum.getNames();
556 :
557 : // Create the string of keys to go in the error message. The last element of
558 : // keys is skipped because the invalid key should always be last.
559 0 : std::string keys_string = "{";
560 0 : for (unsigned int i = 0; i < keys.size() - 1; ++i)
561 : {
562 0 : if (i != 0)
563 : keys_string += ",";
564 0 : keys_string += "'" + keys[i] + "'";
565 : }
566 : keys_string += "}";
567 :
568 0 : logError("The parameter '" + param + "' was given an invalid value ('" +
569 0 : std::string(moose_enum) + "'). Valid values (case-insensitive) are " + keys_string);
570 0 : }
571 :
572 8722 : return value;
573 : }
574 :
575 : template <typename T>
576 : void
577 33073 : Component::insistParameterExists(const std::string & function_name,
578 : const std::string & param_name) const
579 : {
580 33073 : if (!hasParam<T>(param_name))
581 0 : mooseError(name(),
582 : ": Calling ",
583 : function_name,
584 : " failed, parameter '",
585 : param_name,
586 : "' does not exist or does not have the type you requested. Double check your "
587 : "spelling and/or type of the parameter.");
588 33073 : }
589 :
590 : template <typename T>
591 : void
592 474 : Component::checkComponentOfTypeExists(const std::string & param) const
593 : {
594 948 : insistParameterExists<std::string>(__FUNCTION__, param);
595 :
596 : const std::string & comp_name = getParam<std::string>(param);
597 474 : checkComponentOfTypeExistsByName<T>(comp_name);
598 474 : }
599 :
600 : template <typename T>
601 : void
602 27330 : Component::checkComponentOfTypeExistsByName(const std::string & comp_name) const
603 : {
604 27330 : if (!_sim.hasComponentOfType<T>(comp_name))
605 : {
606 36 : if (_sim.hasComponent(comp_name))
607 36 : logError("The component '", comp_name, "' is not of type '", demangle(typeid(T).name()), "'");
608 : else
609 18 : logError("The component '", comp_name, "' does not exist");
610 : }
611 27330 : }
612 :
613 : template <typename T>
614 : void
615 : Component::checkParameterValueLessThan(const std::string & param, const T & value_max) const
616 : {
617 : insistParameterExists<T>(__FUNCTION__, param);
618 :
619 : const auto & value = getParam<T>(param);
620 : if (value >= value_max)
621 : logError("The value of parameter '", param, "' (", value, ") must be less than ", value_max);
622 : }
623 :
624 : template <typename T>
625 : void
626 : Component::checkSizeLessThan(const std::string & param, const unsigned int & n_entries) const
627 : {
628 : insistParameterExists<std::vector<T>>(__FUNCTION__, param);
629 :
630 : const auto & value = getParam<std::vector<T>>(param);
631 : if (value.size() >= n_entries)
632 : logError("The number of entries in the parameter '",
633 : param,
634 : "' (",
635 : value.size(),
636 : ") must be less than ",
637 : n_entries);
638 : }
639 :
640 : template <typename T>
641 : void
642 6365 : Component::checkSizeGreaterThan(const std::string & param, const unsigned int & n_entries) const
643 : {
644 12730 : insistParameterExists<std::vector<T>>(__FUNCTION__, param);
645 :
646 : const auto & value = getParam<std::vector<T>>(param);
647 6365 : if (value.size() <= n_entries)
648 0 : logError("The number of entries in the parameter '",
649 : param,
650 : "' (",
651 0 : value.size(),
652 : ") must be greater than ",
653 : n_entries);
654 6365 : }
655 :
656 : template <typename T1, typename T2>
657 : void
658 12917 : Component::checkEqualSize(const std::string & param1, const std::string & param2) const
659 : {
660 12917 : insistParameterExists<std::vector<T1>>(__FUNCTION__, param1);
661 25834 : insistParameterExists<std::vector<T2>>(__FUNCTION__, param2);
662 :
663 : const auto & value1 = getParam<std::vector<T1>>(param1);
664 : const auto & value2 = getParam<std::vector<T2>>(param2);
665 12917 : if (value1.size() != value2.size())
666 12 : logError("The number of entries in parameter '",
667 : param1,
668 : "' (",
669 24 : value1.size(),
670 : ") must equal the number of entries of parameter '",
671 : param2,
672 : "' (",
673 12 : value2.size(),
674 : ")");
675 12917 : }
676 :
677 : template <typename T>
678 : void
679 400 : Component::checkSizeEqualsValue(const std::string & param, const unsigned int & n_entries) const
680 : {
681 800 : insistParameterExists<std::vector<T>>(__FUNCTION__, param);
682 :
683 : const auto & param_value = getParam<std::vector<T>>(param);
684 400 : if (param_value.size() != n_entries)
685 0 : logError("The number of entries in parameter '",
686 : param,
687 : "' (",
688 0 : param_value.size(),
689 : ") must be equal to ",
690 : n_entries);
691 400 : }
692 :
693 : template <typename T>
694 : void
695 : Component::checkSizeEqualsValue(const std::string & param,
696 : const unsigned int & n_entries,
697 : const std::string & description) const
698 : {
699 : insistParameterExists<std::vector<T>>(__FUNCTION__, param);
700 :
701 : const auto & param_value = getParam<std::vector<T>>(param);
702 : if (param_value.size() != n_entries)
703 : logError("The number of entries in parameter '",
704 : param,
705 : "' (",
706 : param_value.size(),
707 : ") must be equal to ",
708 : description,
709 : " (",
710 : n_entries,
711 : ")");
712 : }
713 :
714 : template <typename T1, typename T2>
715 : void
716 : Component::checkSizeEqualsParameterValue(const std::string & param1,
717 : const std::string & param2) const
718 : {
719 : insistParameterExists<std::vector<T1>>(__FUNCTION__, param1);
720 : insistParameterExists<T2>(__FUNCTION__, param2);
721 :
722 : const auto & value1 = getParam<std::vector<T1>>(param1);
723 : const auto & value2 = getParam<T2>(param2);
724 : if (value1.size() != value2)
725 : logError("The number of entries in parameter '",
726 : param1,
727 : "' (",
728 : value1.size(),
729 : ") must be equal to the value of parameter '",
730 : param2,
731 : "' (",
732 : value2,
733 : ")");
734 : }
|