/*!\file:  gradient_core.cpp
 * \brief compute inverse method gradient direction.
 */ 

#include "../toolkits/toolkits.h"
#include "../objects/objects.h"
#include "../shared/shared.h"
#include "../io/io.h"
#include "../EnumDefinitions/EnumDefinitions.h"
#include "./solutions.h"
#include "../modules/modules.h"
#include "../include/include.h"
#include "../solvers/solvers.h"

void gradient_core(FemModel* femmodel,int step,bool orthogonalize){ 

	/*Intermediaries*/
	double  norm_inf;
	double *norm_list    = NULL;
	Vec     new_gradient = NULL;
	Vec     gradient     = NULL;
	Vec     old_gradient = NULL;

	/*Compute gradient*/
	_printf_(VerboseControl(),"   compute cost function gradient\n");
	Gradjx(&gradient,&norm_list,femmodel->elements,femmodel->nodes, femmodel->vertices,femmodel->loads, femmodel->materials,femmodel->parameters);

	if (orthogonalize){
		_printf_(VerboseControl(),"   orthogonalization\n");
		ControlInputGetGradientx(&old_gradient,femmodel->elements,femmodel->nodes, femmodel->vertices,femmodel->loads, femmodel->materials,femmodel->parameters);
		Orthx(&new_gradient,gradient,old_gradient); VecFree(&old_gradient); VecFree(&gradient);
	}
	else{ 
		new_gradient=gradient;
	}

	/*Check that gradient is clean*/
	VecNorm(new_gradient,NORM_INFINITY,&norm_inf);
	if(norm_inf<=0)    _error_("||∂J/∂α||∞ = 0    gradient norm is zero");
	if(isnan(norm_inf))_error_("||∂J/∂α||∞ = NaN  gradient norm is NaN");

	/*plug back into inputs: */
	ControlInputSetGradientx(femmodel->elements,femmodel->nodes,femmodel->vertices,femmodel->loads,femmodel->materials,femmodel->parameters,new_gradient);
	VecFree(&new_gradient);

	/*Scale Gradients*/
	ControlInputScaleGradientx(femmodel->elements,femmodel->nodes,femmodel->vertices,femmodel->loads,femmodel->materials,femmodel->parameters,norm_list,step);

	/*Clean up and return*/
	xfree((void**)&norm_list);
}
