@*
Diagnostics.tmpl

Created by Graham Dennis on 2008-04-08.

Copyright (c) 2008-2012, Graham Dennis

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*@
@extends xpdeint.Features._Diagnostics

@from xpdeint.Geometry.UniformDimensionRepresentation import UniformDimensionRepresentation

@def description: Simulation diagnostics
@attr featureName = 'Diagnostics'

@# As more gets added to this we are going to want a way of turning parts of this on and off
@# The way I think this should be done in the future is to have a _DIAGNOSTIC_LOG function that instead
@# of a log level includes a 'log location' string or the like, then the user can pass a list of
@# log locations to the diagnostics feature, and those 'log locations' are turned on.

@def timestepErrorBegin($dict)
  @#
  @set $vector = dict['vector']
  @#
real _component_errors[_${vector.id}_ncomponents];
for (long _i0 = 0; _i0 < _${vector.id}_ncomponents; _i0++)
  _component_errors[_i0] = 0.0;
  @#
@end def

@def timestepErrorEnd($dict)
  @#
  @set $vector = dict['vector']
  @set $integrator = dict['caller']
  @#
${insertCodeForFeatures('findMax', ['Driver'], {'variable': '_component_errors', 'count': c'_${vector.id}_ncomponents'})}@slurp
if (_error > $integrator.tolerance)
  @if len($vector.field.dimensions)
  _LOG(_WARNING_LOG_LEVEL, "${', '.join([componentName + '_error: %.1e (cutoff: %.1e)' for componentName in vector.components])}\n",
                           ${', '.join([c'_component_errors[${componentIndex}], _cutoff[${componentIndex}]' for componentIndex in range(len(vector.components))])});
  @else
  _LOG(_WARNING_LOG_LEVEL, "${', '.join([componentName + '_error: %.1e (size: %.1e)' for componentName in vector.components])}\n",
                           ${', '.join([c'_component_errors[${componentIndex}], mod(_active_${vector.id}[${componentIndex}])' for componentIndex in range(len(vector.components))])});
  @end if
  @#
@end def

@def updateMaximumError($dict)
  @#
if (_temp_error > _component_errors[_i1])
  _component_errors[_i1] = _temp_error;
  @#
@end def

@# @def adaptiveStepSucceeded($dict)
@#   @#
@#   @set $integrator = dict['caller']
@#   @#
@# _LOG(_WARNING_LOG_LEVEL, "Step size %e succeeded, ${integrator.propagationDimension} = %e, error = %e\n", _step, ${integrator.propagationDimension}, _error);
@#   @#
@# @end def

@def adaptiveStepFailed($dict)
  @#
  @set $integrator = dict['caller']
  @#
if (_error == 2.0*${integrator.tolerance})
  _LOG(_SEGMENT_LOG_LEVEL, "NaN hit on this step. Retrying. (Error set to %e)\n", 2.0*${integrator.tolerance});
_LOG(_SEGMENT_LOG_LEVEL, "Step size %e failed, ${integrator.propagationDimension} = %e, error = %e\n", _step, ${integrator.propagationDimension}, _error);
  @#
@end def

@def nonlocalAccessValidationFunctionContents($dimRepsNeeded, $nonlocalAccessString, $componentName, $func)
  @#
  @for dimRep in [dimRep for dimRep in dimRepsNeeded if dimRep.type == 'long' and isinstance(dimRep, UniformDimensionRepresentation)]
if (${dimRep.loopIndex} < 0 || ${dimRep.loopIndex} >= ${dimRep.globalLattice}) {
  _LOG(
    _ERROR_LOG_LEVEL,
    "Error: Invalid access of dimension ${dimRep.name} in %s on line %i.\n"
    "       Value %li is out of range (%li, %li).\n"
    "       Error was caused by accessing variable '${componentName}' nonlocally.\n",
    filename, line_number, ${dimRep.loopIndex} + ${dimRep.minimum}, ${dimRep.minimum}, ${dimRep.maximum}
  );
}
  @end for
return ${nonlocalAccessString};
  @#
@end def
