@*
ErrorCheck.tmpl

Created by Graham Dennis on 2007-08-26.

Copyright (c) 2007-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._ErrorCheck

@def description: error check
@attr $featureName = 'ErrorCheck'

@def globals
  @#
  @super
  @#
bool _half_step;
  @for $momentGroup in $momentGroups
    @set $momentGroupOutputField = $momentGroup.outputField

// Arrays for full- and half-step moment group results
real* _${momentGroupOutputField.name}_fullstep;
  @end for
  @#
@end def

@def topLevelSequenceBegin($dict)
  @#
  @# Loop over moment groups allocating half step array
  @# and saving the pointer to the original array
  @# Note that we allocate the half step array now and not
  @# later when we actually need it so that if allocating it would
  @# cause us to run out of virtual memory, we do that at the *start*
  @# of the simulation, not half way through
  @for $momentGroup in $momentGroups
    @set $momentGroupOutputField = $momentGroup.outputField

_${momentGroupOutputField.name}_fullstep = _${momentGroup.processedVector.id};
// _${momentGroupOutputField.name}_halfstep has already been allocated
  @end for

for (_half_step = false; ; _half_step = true)
{
  if (!_half_step) {
    _LOG(_SIMULATION_LOG_LEVEL, "Beginning full step integration ...\n");
  @# Loop over the moment groups setting the output field to the fullstep fiel
  @for $momentGroup in $momentGroups
    @set $momentGroupOutputField = $momentGroup.outputField
    
    _active_${momentGroup.processedVector.id} = _${momentGroupOutputField.name}_fullstep;
  @end for
  }
  else {
    _LOG(_SIMULATION_LOG_LEVEL, "Beginning half step integration ...\n");
  @# Loop over the moment groups setting the output field to the already-allocated halfstep field
  @for $momentGroup in $momentGroups
    @set $momentGroupOutputField = $momentGroup.outputField
    
    @for aliasName in momentGroup.processedVector.aliases
      @if not aliasName==c"_${momentGroupOutputField.name}_halfstep"
    _active_${momentGroup.processedVector.id} = $aliasName;
    ${momentGroup.processedVector.functions['initialise'].call()}
      @end if
    @end for
    
    _active_${momentGroup.processedVector.id} = _${momentGroupOutputField.name}_halfstep;
  @end for
  }
  
  @silent $dict['extraIndent'] += 2
  @#
@end def

@def topLevelSequenceEnd($dict)
  
  if (_half_step)
      break;
}
  @silent $dict['extraIndent'] -= 2
@end def


@*
  This function returns a Cheetah template string suitable for passing to loopOverVectorsWithInnerContentTemplate
*@
@def writeOutFunctionImplementationBegin($dict)
  @#
  @set $momentGroup = dict['caller']
  @set $dependentVariables = dict['dependentVariables']
  @set $fieldName = $momentGroup.outputField.name
  @#
  @# So our job is to create 'error_' versions of all of the variables in the momentGroup's outputField.
  @set $newVariableDict = {'vector': $momentGroup.processedVector,
                           'arrayName': c'_${fieldName}_fullstep',
                           'components': ['error_' + component for component in $momentGroup.processedVector.components],
                          }
  @silent dependentVariables.append(newVariableDict)
  @#
  @# Now return the template that will be used to create the error vector
// Copy the error into the fullstep array
_${fieldName}_fullstep[\$index] = _${fieldName}_fullstep[\$index] - _${fieldName}_halfstep[\$index];
  @#
@end def

@def createFixedStepVariable($dict)

// If we are doing half-step integration, divide the step size by 2
if (_half_step)
  _step /= 2.0;
// Regardless, the step size for generated noise is the half-step
_noiseStep /= 2.0;
@end def

@def createToleranceVariable($dict)

// If we are doing half-step integration, divide the tolerance by 16
if (_half_step)
  _tolerance /= 16.0;
@end def

@def integrateFixedStepInnerLoopBegin($dict)
  @#
  @set $integrator = dict['caller']

// If we are doing half-step integration, then we need to do each integrate step twice (but with half the step size)
for (int _error_check_loop_iter = (_half_step ? 2 : 1); _error_check_loop_iter > 0; _error_check_loop_iter--) {
  @silent $dict['extraIndent'] += 2
  @#
@end def

@def integrateFixedStepInnerLoopEnd($dict)
  @#
}
  @silent $dict['extraIndent'] -= 2
  @#
@end def

@def writeOutFunctionImplementationBody($dict)
  @set $momentGroup = dict['caller']
  @set $processedVector = $momentGroup.processedVector
  @set $outputFieldName = momentGroup.outputField.name
_active_${processedVector.id} = _${outputFieldName}_fullstep;

${processedVector.findMaximum('_max_step_error', basis = momentGroup.outputBasis)}@slurp

// Restore active pointer for processed vector
_active_${processedVector.id} = _${outputFieldName}_halfstep;

_LOG(_SIMULATION_LOG_LEVEL, "Maximum step error in moment group ${momentGroup.number+1} was %e\n", _max_step_error);
@end def
