function []=dakota_in_write(method,dvar,dresp,params,filei,corrin,varargin)
%DAKOTA_IN_WRITE - Write a Dakota .in input file.
%
%   Usage:
%      []=dakota_in_write(method,dvar,dresp,params,filei,varargin)
%      []=dakota_in_write(dmeth ,dvar,dresp,params,filei,varargin)
%
%   where the required input is,
%      method        (character, dakota method name)
%      dmeth         (dakota_method, method class object)
%      dvar          (structure array, variable class objects)
%      dresp         (structure array, response class objects)
%      params        (structure array, method-independent parameters)
%      filei         (character, name of .in file)
%
%   The method and filei will be prompted for if empty. params may be empty, in 
%   which case defaults will be used.
%
%   The optional varargin are not yet used.
%
%   This function writes a Dakota .in input file to be used by Dakota. This 
%   file is independent of the particular analysis package.
%
%   This data would typically be generated by a MATLAB script for a specific 
%   model, using the method, variable, and response class objects. This 
%   function may be called by dakota_in_data.
%
%   "Copyright 2009, by the California Institute of Technology. ALL RIGHTS 
%   RESERVED. United States Government Sponsorship acknowledged. Any commercial 
%   use must be negotiated with the Office of Technology Transfer at the 
%   California Institute of Technology. (NTR 47078)
%
%   This software may be subject to U.S. export control laws. By accepting this 
%   software, the user agrees to comply with all applicable U.S. export laws 
%   and regulations. User has the responsibility to obtain export licenses, or 
%   other export authority as may be required before exporting such information 
%   to foreign countries or providing access to foreign persons."
%
if ~nargin
    help dakota_in_write
    return
end

%  process the input parameters

if ~exist('method','var') || isempty(method)
    method=input('Method?  ','s');
end
if     ischar(method)
    dmeth=dakota_method(method);
elseif isa(method,'dakota_method')
    dmeth=method;
else
    error(['Method ''' inputname(1) ''' is unrecognized class ''' class(method) '''']);
end

if ~exist('filei' ,'var') || isempty(filei)
    filei=input('Dakota input file to write?  ','s');
end
[pathstr,name,ext] = fileparts(filei);
if isempty(ext)
% fileparts only considers '.in' to be the extension, not '.qmu.in'
    ext='.qmu.in';
end
filei2=fullfile(pathstr,[name ext]);

display(sprintf('Opening Dakota input file ''%s''',filei2));
fidi=fopen(sprintf('%s',filei2),'w');
if (fidi < 0)
    error('''%s'' could not be opened',filei2);
end

if ~exist('params','var')
    params=struct();
end
params=dakota_in_params(params);

%  write the strategy section

if IssmConfig('_DAKOTA_VERSION_') < 6,
	strategy_write(fidi,params);
else
	environment_write(fidi,params);
end

%  write the method section

method_write(fidi,dmeth,dresp,params);

%  write the model section

model_write(fidi);

%  write the variables section

variables_write(fidi,dmeth,dvar,corrin);

%  write the interface section

interface_write(fidi,params);

%  write the responses section

responses_write(fidi,dmeth,dresp,params);

fclose(fidi);
display('End of file successfully written');

% Uncomment to print contents of Dakota input file (for debugging)
% type([name ext]);

end

%%  function to write the strategy section of the file

function []=strategy_write(fidi,params)

display('Writing strategy section of Dakota input file');

fprintf(fidi,'strategy,\n');
fprintf(fidi,'\tsingle_method\n\n');
param_write(fidi,'\t  ','graphics','','\n',params);
param_write(fidi,'\t  ','tabular_graphics_data','','\n',params);
param_write(fidi,'\t  ','tabular_graphics_file',' ''','''\n',params);
fprintf(fidi,'\n');

end

%%  function to write the environment section of the file

function []=environment_write(fidi,params)

	display('Writing environment section of Dakota input file');

	fprintf(fidi,'environment,\n');
	param_write(fidi,'\t  ','graphics','','\n',params);
	param_write(fidi,'\t  ','tabular_graphics_data','','\n',params);
	param_write(fidi,'\t  ','tabular_graphics_file',' ''','''\n',params);
	fprintf(fidi,'\n');

end

%%  function to write the method section of the file

function []=method_write(fidi,dmeth,dresp,params)

display('Writing method section of Dakota input file');

fprintf(fidi,'method,\n');
fprintf(fidi,'\t%s\n',dmeth.method);

dmeth_params_write(dmeth,fidi);

%  write response levels
if strcmp(dmeth.type,'nond')
    for i=1:length(dmeth.responses)
        fhresp=str2func([dmeth.responses{i} '.dakota_rlev_write']);
        fhresp(fidi,dresp,params);
    end
end
fprintf(fidi,'\n');

end

%%  function to write the model section of the file

function []=model_write(fidi)

display('Writing model section of Dakota input file');

fprintf(fidi,'model,\n');
fprintf(fidi,'\t%s\n\n','single');

end

%%  function to write the variables section of the file

function []=variables_write(fidi,dmeth,dvar,corrin)

display('Writing variables section of Dakota input file');

fprintf(fidi,'variables,\n');

%  variables vary by method
for i=1:length(dmeth.variables)
    fhvar=str2func([dmeth.variables{i} '.dakota_write']);
    fhvar(fidi,dvar);
end

%  linear constraints vary by method

for i=1:length(dmeth.lcspec)
    fhvar=str2func([dmeth.lcspec{i}    '.dakota_write']);
    fhvar(fidi,dvar);
end

% write correlation matrix: 
if ~isempty(corrin),
	fprintf(fidi,'uncertain_correlation_matrix\n');
	for i=1:size(corrin,1),
		for j=1:size(corrin,2),
			fprintf(fidi,'%g ',corrin(i,j));
		end
		fprintf(fidi,'\n');
	end
end

fprintf(fidi,'\n');

end

%%  function to write the interface section of the file

function []=interface_write(fidi,params)

display('Writing interface section of Dakota input file');

fprintf(fidi,'interface,\n');

if     ~params.system && ~params.fork && ~params.direct
    params.fork=true;
elseif (params.system+params.fork+params.direct > 1)
    error('Too many interfaces selected')
end

if     params.system || params.fork
    param_write(fidi,'\t','asynchronous','','\n',params);
    param_write(fidi,'\t  ','evaluation_concurrency',' = ','\n',params);
    param_write(fidi,'\t  ','analysis_concurrency','   = ','\n',params);
    param_write(fidi,'\t  ','evaluation_servers','     = ','\n',params);
    param_write(fidi,'\t  ','evaluation_self_scheduling','','\n',params);
    param_write(fidi,'\t  ','evaluation_static_scheduling','','\n',params);
    param_write(fidi,'\t  ','analysis_servers','       = ','\n',params);
    param_write(fidi,'\t  ','analysis_self_scheduling','','\n',params);
    param_write(fidi,'\t  ','analysis_static_scheduling','','\n',params);
    param_write(fidi,'\t','algebraic_mappings',' = ','\n',params);
    param_write(fidi,'\t','system','','\n',params);
    param_write(fidi,'\t','fork','','\n',params);
    param_write(fidi,'\t  ','analysis_driver',' = ''','''\n',params);
    if ~isempty(params.input_filter)
        param_write(fidi,'\t  ','input_filter','    = ''','''\n',params);
    end
    if ~isempty(params.output_filter)
        param_write(fidi,'\t  ','output_filter','   = ''','''\n',params);
    end
    param_write(fidi,'\t  ','failure_capture','   ','\n',params);
    param_write(fidi,'\t  ','deactivate','        ','\n',params);
    param_write(fidi,'\t  ','parameters_file',' = ''','''\n',params);
    param_write(fidi,'\t  ','results_file','    = ''','''\n',params);
    param_write(fidi,'\t  ','verbatim', '','\n',params);
    param_write(fidi,'\t  ','aprepro', '','\n',params);
    param_write(fidi,'\t  ','file_tag', '','\n',params);
    param_write(fidi,'\t  ','file_save','','\n',params);
elseif params.direct
%  Error: asynchronous capability not yet supported in direct interfaces.
%  Update: it is now possible to run in parallel in direct interfaces.
    param_write(fidi,'\t','algebraic_mappings',' = ','\n',params);
    param_write(fidi,'\t','direct','','\n',params);
    param_write(fidi,'\t  ','analysis_driver','     = ''','''\n',params);
    if IssmConfig('_DAKOTA_VERSION_') < 6,
		param_write(fidi,'\t  ','evaluation_static_scheduling','','\n',params);
	else
		param_write(fidi,'\t  ','evaluation_scheduling',' ','\n',params);
		param_write(fidi,'\t  ','processors_per_evaluation',' = ','\n',params);
	end
    if ~isempty(params.analysis_components)
        [pathstr,name,ext] = fileparts(params.analysis_components);
        if isempty(ext)
            ext='.m';
        end
        params.analysis_components=fullfile(pathstr,[name ext]);
        param_write(fidi,'\t  ','analysis_components',' = ''','''\n',params);
    end
    if ~isempty(params.input_filter)
        param_write(fidi,'\t  ','input_filter','    = ''','''\n',params);
    end
    if ~isempty(params.output_filter)
        param_write(fidi,'\t  ','output_filter','   = ''','''\n',params);
    end
    param_write(fidi,'\t  ','failure_capture','   ','\n',params);
    param_write(fidi,'\t  ','deactivate','        ','\n',params);
    param_write(fidi,'\t  ','processors_per_analysis',' = ''','''\n',params);
end

fprintf(fidi,'\n');

end

%%  function to write the responses section of the file

function []=responses_write(fidi,dmeth,dresp,params)

display('Writing responses section of Dakota input file');

fprintf(fidi,'responses,\n');
%fprintf(fidi,'calibration_terms = 1 \n');


%  functions, gradients, and hessians vary by method

rdesc={};

for i=1:length(dmeth.responses)
    fhresp=str2func([dmeth.responses{i} '.dakota_write']);
    [rdesc]=fhresp(fidi,dresp,rdesc);
end
%  write accumulated response descriptors for all response classes

if ~isempty(rdesc)
    fprintf(fidi,'\tresponse_descriptors =\n');
    vector_write(fidi,sprintf('\t  '),rdesc,6,76);
end

ghspec_write(fidi,params,dmeth.ghspec);

fprintf(fidi,'\n');

end

%%  function to write gradient and hessian specifications

function []=ghspec_write(fidi,params,ghspec)

%  gradients

if ismember('grad',ghspec)
    if     ~params.numerical_gradients && ~params.analytic_gradients
        params.numerical_gradients=true;
    elseif (params.numerical_gradients+params.analytic_gradients > 1)
        error('Too many gradients selected')
    end

    if     params.numerical_gradients
        param_write(fidi,'\t','numerical_gradients','','\n',params);
        param_write(fidi,'\t  ','method_source',' ','\n',params);
        param_write(fidi,'\t  ','interval_type',' ','\n',params);
        param_write(fidi,'\t  ','fd_gradient_step_size',' = ','\n',params);
    elseif params.analytic_gradients
        param_write(fidi,'\t','analytic_gradients','','\n',params);
%     elseif params.mixed_gradients
    end
else
    fprintf(fidi,'\tno_gradients\n');
end

%  hessians (no implemented methods use them yet)

if ismember('hess',ghspec)
    error('Hessians needed by method but not provided');
else
    fprintf(fidi,'\tno_hessians\n');
end

end
