/*!\file:  SpawnCoreParallel.cpp
 * \brief: run core ISSM solution using Dakota inputs coming from CPU 0.
 * \sa qmu.cpp DakotaPlugin.cpp
 *
 * This routine needs to be understood simultaneously with qmu.cpp and DakotaPlugin. 
 * SpawnCoreParallel is called by all CPUS, with CPU 0 holding Dakota variable values, along 
 * with variable descriptors. 
 *
 * SpawnCoreParallel takes care of broadcasting the variables and their descriptors across the MPI 
 * ring. Once this is done, we use the variables to modify the inputs for the solution core. 
 * For ex, if "rho_ice" is provided, for ex 920, we include "rho_ice" in the inputs, then 
 * call the core with the modified inputs. This is the way we get Dakota to explore the parameter 
 * spce of the core. 
 *
 * Once the core is called, we process the results of the core, and using the processed results, 
 * we compute response functions. The responses are computed on all CPUS, but they are targeted 
 * for CPU 0, which will get these values back to the Dakota engine. 
 *
 */ 

#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif


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

void SpawnCoreParallel(double* d_responses, int d_numresponses, double* d_variables, char** d_variables_descriptors,int d_numvariables, FemModel* femmodel,int counter){

	/*Notice the d_, which prefixes anything that is being provided to us by the Dakota pluggin. Careful. some things are ours, some are dakotas!: */

	int i;
	extern int my_rank;
	
	char   **responses_descriptors     = NULL; //these are our!  there are only numresponsedescriptors of them, not d_numresponses!!!
	int      numresponsedescriptors;
	char    *string                    = NULL;
	int      string_length;
	int      solution_type;
	bool     control_analysis          = false;
	void (*solutioncore)(FemModel*)    = NULL;

	/*synchronize all cpus, as CPU 0 is probably late (it is starting the entire dakota strategy!) : */
	MPI_Barrier(MPI_COMM_WORLD);
	if(VerboseQmu()) _pprintLine_("qmu iteration: " << counter);
	
	/*retrieve parameters: */
	femmodel->parameters->FindParam(&responses_descriptors,&numresponsedescriptors,QmuResponsedescriptorsEnum);
	femmodel->parameters->FindParam(&solution_type,SolutionTypeEnum);
	femmodel->parameters->FindParam(&control_analysis,InversionIscontrolEnum);

	/* only cpu 0, running dakota is providing us with variables and variables_descriptors and numresponses: broadcast onto other cpus: */
	DakotaMPI_Bcast(&d_variables,&d_variables_descriptors,&d_numvariables,&d_numresponses);

	/*Modify core inputs in objects contained in femmodel, to reflect the dakota variables inputs: */
	InputUpdateFromDakotax(femmodel->elements,femmodel->nodes,femmodel->vertices,femmodel->loads,femmodel->materials,femmodel->parameters,d_variables,d_variables_descriptors,d_numvariables);

	/*Determine solution sequence: */
	if(VerboseQmu()) _pprintLine_("Starting " << EnumToStringx(solution_type) << " core:");
	CorePointerFromSolutionEnum(&solutioncore,femmodel->parameters,solution_type);
	#ifdef _HAVE_CONTROL_
	if(control_analysis)solutioncore=&control_core;
	#else
	_error2_("ISSM was not compiled with control capabilities, exiting!");
	#endif

	/*Run the core solution sequence: */
	solutioncore(femmodel);

	/*compute responses: */
	if(VerboseQmu()) _pprintLine_("compute dakota responses:");
	DakotaResponsesx(d_responses,femmodel->elements,femmodel->nodes,femmodel->vertices,femmodel->loads,femmodel->materials,femmodel->parameters,responses_descriptors,numresponsedescriptors,d_numresponses);
	
	/*Free ressources:*/
	DakotaFree(&d_variables,&d_variables_descriptors,&responses_descriptors, d_numvariables, numresponsedescriptors);
}

