• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

QCAD_Solver.cpp

Go to the documentation of this file.
00001 //*****************************************************************//
00002 //    Albany 2.0:  Copyright 2012 Sandia Corporation               //
00003 //    This Software is released under the BSD license detailed     //
00004 //    in the file "license.txt" in the top-level Albany directory  //
00005 //*****************************************************************//
00006 
00007 #include "QCAD_Solver.hpp"
00008 #include "QCAD_CoupledPoissonSchrodinger.hpp"
00009 #include "Piro_Epetra_LOCASolver.hpp"
00010 #include "Stokhos.hpp"
00011 #include "Stokhos_Epetra.hpp"
00012 #include "Sacado_PCE_OrthogPoly.hpp"
00013 #include "Teuchos_XMLParameterListHelpers.hpp"
00014 
00015 //needed?
00016 //#include "Teuchos_RCP.hpp"
00017 //#include "Teuchos_VerboseObject.hpp"
00018 //#include "Teuchos_FancyOStream.hpp"
00019 
00020 #include "Teuchos_ParameterList.hpp"
00021 
00022 #include "Albany_Utils.hpp"
00023 #include "Albany_SolverFactory.hpp"
00024 #include "Albany_StateInfoStruct.hpp"
00025 #include "Albany_EigendataInfoStruct.hpp"
00026 
00027 #ifdef ALBANY_CI
00028 #include "AnasaziConfigDefs.hpp"
00029 #include "AnasaziBasicEigenproblem.hpp"
00030 #include "AnasaziBlockDavidsonSolMgr.hpp"
00031 #include "AnasaziBasicOutputManager.hpp"
00032 #endif
00033 
00034 
00035 
00036 namespace QCAD {
00037   
00038   void SolveModel(const SolverSubSolver& ss);
00039   void SolveModel(const SolverSubSolver& ss, 
00040       Albany::StateArrays*& pInitialStates, Albany::StateArrays*& pFinalStates);
00041   void SolveModel(const QCAD::SolverSubSolver& ss, 
00042       Teuchos::RCP<Albany::EigendataStruct>& pInitialEData, 
00043       Teuchos::RCP<Albany::EigendataStruct>& pFinalEData);
00044   void SolveModel(const SolverSubSolver& ss, 
00045       Albany::StateArrays*& pInitialStates, Albany::StateArrays*& pFinalStates,
00046       Teuchos::RCP<Albany::EigendataStruct>& pInitialEData,
00047       Teuchos::RCP<Albany::EigendataStruct>& pFinalEData);
00048 
00049 
00050 
00051   void CopyStateToContainer(Albany::StateArrays& src,
00052           std::string stateNameToCopy,
00053           std::vector<Intrepid::FieldContainer<RealType> >& dest);
00054   void CopyContainerToState(std::vector<Intrepid::FieldContainer<RealType> >& src,
00055           Albany::StateArrays& dest,
00056           std::string stateNameOfCopy);
00057   void CopyContainer(std::vector<Intrepid::FieldContainer<RealType> >& src,
00058          std::vector<Intrepid::FieldContainer<RealType> >& dest);
00059   void AddContainerToContainer(std::vector<Intrepid::FieldContainer<RealType> >& src,
00060              std::vector<Intrepid::FieldContainer<RealType> >& dest,
00061              double srcFactor, double thisFactor); // dest = thisFactor * dest + srcFactor * src
00062   void AddContainerToState(std::vector<Intrepid::FieldContainer<RealType> >& src,
00063           Albany::StateArrays& dest,
00064          std::string stateName, double srcFactor, double thisFactor); // dest[stateName] = thisFactor * dest[stateName] + srcFactor * src
00065 
00066   
00067   void CopyState(Albany::StateArrays& src, Albany::StateArrays& dest,  std::string stateNameToCopy);
00068   void AddStateToState(Albany::StateArrays& src, std::string srcStateNameToAdd, 
00069            Albany::StateArrays& dest, std::string destStateNameToAddTo);
00070   void SubtractStateFromState(Albany::StateArrays& src, std::string srcStateNameToSubtract,
00071             Albany::StateArrays& dest, std::string destStateNameToSubtractFrom);
00072   
00073   double getMaxDifference(Albany::StateArrays& states, 
00074         std::vector<Intrepid::FieldContainer<RealType> >& prevState,
00075         std::string stateName);
00076 
00077   double getNorm2Difference(Albany::StateArrays& states,   
00078           std::vector<Intrepid::FieldContainer<RealType> >& prevState,
00079           std::string stateName);
00080   double getNorm2(std::vector<Intrepid::FieldContainer<RealType> >& container, const Teuchos::RCP<const Epetra_Comm>& comm);
00081   int getElementCount(std::vector<Intrepid::FieldContainer<RealType> >& container);
00082   
00083   void ResetEigensolverShift(const Teuchos::RCP<EpetraExt::ModelEvaluator>& Solver, double newShift,
00084            Teuchos::RCP<Teuchos::ParameterList>& eigList);
00085   double GetEigensolverShift(const SolverSubSolver& ss, double pcBelowMinPotential);
00086   void   SetPreviousDensityMixing(const Teuchos::RCP<EpetraExt::ModelEvaluator::InArgs> inArgs, double mixingFactor);
00087 
00088 
00089   //String processing helper functions
00090   std::vector<std::string> string_split(const std::string& s, char delim, bool bProtect=false);
00091   std::string string_remove_whitespace(const std::string& s);
00092   std::vector<std::string> string_parse_function(const std::string& s);
00093   std::map<std::string,std::string> string_parse_arrayref(const std::string& s);
00094   std::vector<int> string_expand_compoundindex(const std::string& indexStr, int min_index, int max_index);
00095 
00096 }
00097 
00098 
00099 
00100 QCAD::Solver::
00101 Solver(const Teuchos::RCP<Teuchos::ParameterList>& appParams,
00102        const Teuchos::RCP<const Epetra_Comm>& comm,
00103        const Teuchos::RCP<const Epetra_Vector>& initial_guess) :
00104   maxIter(0),ps_converge_tol(1e-6)
00105 {
00106   using std::string;
00107 
00108   solverComm = comm;
00109   mainAppParams = appParams;
00110 
00111   // Get sub-problem input xml files from problem parameters
00112   Teuchos::ParameterList& problemParams = appParams->sublist("Problem");
00113 
00114   // Validate Problem parameters against list for this specific problem
00115   problemParams.validateParameters(*getValidProblemParameters(),0);
00116 
00117   string problemName, problemDimStr;
00118   problemName = problemParams.get<string>("Name");
00119   problemNameBase = problemName.substr( 0, problemName.length()-3 ); //remove " xD" where x = 1, 2, or 3
00120   problemDimStr = problemName.substr( problemName.length()-2 ); // "xD" where x = 1, 2, or 3
00121   
00122   if(problemDimStr == "1D") numDims = 1;
00123   else if(problemDimStr == "2D") numDims = 2;
00124   else if(problemDimStr == "3D") numDims = 3;
00125   else TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00126            << "Error!  Cannot extract dimension from problem name: "
00127            << problemName << std::endl);
00128 
00129   if( !(problemNameBase == "Poisson" || problemNameBase == "Schrodinger" || problemNameBase == "Schrodinger CI" ||
00130   problemNameBase == "Poisson Schrodinger" || problemNameBase == "Poisson Schrodinger CI"))
00131     TEUCHOS_TEST_FOR_EXCEPTION (true, Teuchos::Exceptions::InvalidParameter, std::endl 
00132         << "Error!  Invalid problem base name: "
00133         << problemNameBase << std::endl);
00134   
00135 
00136   // Check if "verbose" mode is enabled
00137   bVerbose = (comm->MyPID() == 0) && problemParams.get<bool>("Verbose Output", false);
00138 
00139   eigensolverName = problemParams.get<string>("Schrodinger Eigensolver","LOBPCG");
00140   bRealEvecs = problemParams.get<bool>("Eigenvectors are Real",false);
00141 
00142   // Get problem parameters used for iterating Poisson-Schrodinger loop
00143   if(problemNameBase == "Poisson Schrodinger" || problemNameBase == "Poisson Schrodinger CI") {
00144     bUseIntegratedPS = problemParams.get<bool>("Use Integrated Poisson Schrodinger",true);
00145     maxIter = problemParams.get<int>("Maximum PS Iterations", 100);
00146     shiftPercentBelowMin = problemParams.get<double>("Eigensolver Percent Shift Below Potential Min", 1.0);
00147     ps_converge_tol = problemParams.get<double>("Iterative PS Convergence Tolerance", 1e-6);
00148     fixedPSOcc = problemParams.get<double>("Iterative PS Fixed Occupation", -1.0);
00149   }
00150 
00151   // Get problem parameters used for Poisson-Schrodinger-CI mode
00152   if(problemNameBase == "Poisson Schrodinger CI") {
00153     minCIParticles = problemParams.get<int>("Minimum CI Particles",0);
00154     maxCIParticles = problemParams.get<int>("Maximum CI Particles",10);
00155     bUseTotalSpinSymmetry = problemParams.get<bool>("Use S2 Symmetry in CI", false);
00156   }
00157 
00158   // Get problem parameters used for Schrodinger-CI mode
00159   if(problemNameBase == "Schrodinger CI") {
00160     nCIParticles = problemParams.get<int>("CI Particles");
00161     nCIExcitations = problemParams.get<int>("CI Excitations");
00162     assert(nCIParticles >= nCIExcitations);
00163   }
00164 
00165   // Get the number of eigenvectors - needed for all problems-modes except "Poisson"
00166   nEigenvectors = 0;
00167   if(problemNameBase != "Poisson") {
00168     nEigenvectors = problemParams.get<int>("Number of Eigenvalues");
00169   }
00170 
00171   // Get and set the default Piro parameters from a file, if given
00172   std::string piroFilename  = problemParams.get<std::string>("Piro Defaults Filename", "");
00173   if(piroFilename.length() > 0) {
00174     const Albany_MPI_Comm mpiComm = Albany::getMpiCommFromEpetraComm(*comm);
00175     Teuchos::RCP<Teuchos::Comm<int> > tcomm = Albany::createTeuchosCommFromMpiComm(mpiComm);
00176     Teuchos::RCP<Teuchos::ParameterList> defaultPiroParams = Teuchos::createParameterList("Default Piro Parameters");
00177     Teuchos::updateParametersFromXmlFileAndBroadcast(piroFilename, defaultPiroParams.ptr(), *tcomm);
00178     Teuchos::ParameterList& piroList = appParams->sublist("Piro", false);
00179     piroList.setParametersNotAlreadySet(*defaultPiroParams);
00180   }
00181     
00182   // Get debug filenames -- empty string = don't output
00183   Teuchos::ParameterList& debugParams = appParams->sublist("Debug Output");
00184   std::string debug_initpoissonXML = debugParams.get<std::string>("Initial Poisson XML Input","");
00185   std::string debug_poissonXML     = debugParams.get<std::string>("Poisson XML Input","");
00186   std::string debug_schroXML       = debugParams.get<std::string>("Schrodinger XML Input","");
00187   std::string debug_psXML          = debugParams.get<std::string>("Poisson-Schrodinger XML Input","");
00188   std::string debug_ciXML          = debugParams.get<std::string>("Poisson-CI XML Input","");
00189   std::string debug_nochargeXML    = debugParams.get<std::string>("CI No Charge XML Input","");
00190   std::string debug_deltaXML       = debugParams.get<std::string>("CI Delta XML Input","");
00191   std::string debug_coulombXML     = debugParams.get<std::string>("CI Coulomb XML Input","");
00192   std::string debug_coulombImXML   = debugParams.get<std::string>("CI Coulomb Imaginary XML Input","");
00193 
00194   std::string debug_initpoissonExo = debugParams.get<std::string>("Initial Poisson Exodus Output","");
00195   std::string debug_poissonExo     = debugParams.get<std::string>("Poisson Exodus Output","");
00196   std::string debug_schroExo       = debugParams.get<std::string>("Schrodinger Exodus Output","");
00197   std::string debug_ciExo          = debugParams.get<std::string>("Poisson-CI Exodus Output","");
00198   std::string debug_nochargeExo    = debugParams.get<std::string>("CI No Charge Exodus Output","");
00199   std::string debug_deltaExo       = debugParams.get<std::string>("CI Delta Exodus Output","");
00200   std::string debug_coulombExo     = debugParams.get<std::string>("CI Coulomb Exodus Output","");
00201   std::string debug_coulombImExo   = debugParams.get<std::string>("CI Coulomb Imaginary Exodus Output","");
00202 
00203 
00204   // Get name of output exodus file specified in Discretization section
00205   std::string outputExo = appParams->sublist("Discretization").get<std::string>("Exodus Output File Name");
00206 
00207   // Create Solver parameter lists based on problem name
00208   if( problemNameBase == "Poisson" ) {
00209     subProblemAppParams["Poisson"] = createPoissonInputFile(appParams, numDims, nEigenvectors, "none",
00210                   debug_poissonXML, outputExo);
00211     defaultSubSolver = "Poisson";
00212   }
00213 
00214   else if( problemNameBase == "Schrodinger" ) {
00215     subProblemAppParams["Schrodinger"] = createSchrodingerInputFile(appParams, numDims, nEigenvectors, "none",
00216                     debug_schroXML, debug_schroExo);
00217     defaultSubSolver = "Schrodinger";
00218   }
00219 
00220   else if( problemNameBase == "Poisson Schrodinger" ) {
00221     subProblemAppParams["InitPoisson"] = createPoissonInputFile(appParams, numDims, nEigenvectors, "initial poisson",
00222                 debug_initpoissonXML, debug_initpoissonExo);
00223     subProblemAppParams["Poisson"]     = createPoissonInputFile(appParams, numDims, nEigenvectors, "couple to schrodinger",
00224                 debug_poissonXML, debug_poissonExo);
00225     subProblemAppParams["Schrodinger"] = createSchrodingerInputFile(appParams, numDims, nEigenvectors, "couple to poisson",
00226                     debug_schroXML, debug_schroExo);
00227     if(bUseIntegratedPS) {
00228       subProblemAppParams["PoissonSchrodinger"] = createPoissonSchrodingerInputFile(appParams, numDims, nEigenvectors,
00229                         debug_psXML, outputExo);
00230       defaultSubSolver = "PoissonSchrodinger";
00231     }
00232     else defaultSubSolver = "Poisson";    
00233   }
00234 
00235   else if( problemNameBase == "Schrodinger CI" ) {
00236     subProblemAppParams["CoulombPoisson"]   = createPoissonInputFile(appParams, numDims, nEigenvectors, "Coulomb",
00237                      debug_coulombXML, debug_coulombExo);
00238     if(!bRealEvecs)
00239       subProblemAppParams["CoulombPoissonIm"] = createPoissonInputFile(appParams, numDims, nEigenvectors, "Coulomb imaginary",
00240                        debug_coulombImXML, debug_coulombImExo); // no debug output
00241     subProblemAppParams["Schrodinger"] = createSchrodingerInputFile(appParams, numDims, nEigenvectors, "none",
00242                     debug_schroXML, debug_schroExo);
00243     defaultSubSolver = "Schrodinger";
00244   }
00245 
00246   else if( problemNameBase == "Poisson Schrodinger CI" ) {
00247     bUseIntegratedPS = false;  // TODO: add integrated end option to this -- (need to extract 1P eigenvectors from coupled PS solver below)
00248 
00249     subProblemAppParams["InitPoisson"] = createPoissonInputFile(appParams, numDims, nEigenvectors, "initial poisson",
00250                 debug_initpoissonXML, debug_initpoissonExo);
00251     subProblemAppParams["Poisson"] = createPoissonInputFile(appParams, numDims, nEigenvectors, "couple to schrodinger",
00252                   debug_poissonXML, debug_poissonExo);
00253     subProblemAppParams["Schrodinger"] = createSchrodingerInputFile(appParams, numDims, nEigenvectors, "couple to poisson",
00254                     debug_schroXML, debug_schroExo);
00255     subProblemAppParams["CIPoisson"] = createPoissonInputFile(appParams, numDims, nEigenvectors, "CI", debug_ciXML, debug_ciExo);
00256 
00257     if(bUseIntegratedPS)
00258       subProblemAppParams["PoissonSchrodinger"] = createPoissonSchrodingerInputFile(appParams, numDims, nEigenvectors,
00259                         debug_psXML, outputExo);
00260     defaultSubSolver = "CIPoisson";
00261 
00262     // Note: no debug output for CI support poisson solvers in this mode
00263     subProblemAppParams["CoulombPoisson"]   = createPoissonInputFile(appParams, numDims, nEigenvectors, "Coulomb",
00264                      debug_coulombXML, debug_coulombExo);
00265     if(!bRealEvecs)
00266       subProblemAppParams["CoulombPoissonIm"] = createPoissonInputFile(appParams, numDims, nEigenvectors, "Coulomb imaginary",
00267                        debug_coulombImXML, debug_coulombImExo);
00268     subProblemAppParams["NoChargePoisson"]  = createPoissonInputFile(appParams, numDims, nEigenvectors, "no charge", 
00269                      debug_nochargeXML, debug_nochargeExo);
00270     subProblemAppParams["DeltaPoisson"]     = createPoissonInputFile(appParams, numDims, nEigenvectors, "delta",
00271                      debug_deltaXML, debug_deltaExo);
00272   }    
00273 
00274   else TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
00275           std::endl << "Error in QCAD::Solver constructor:  " <<
00276           "Invalid problem name base: " << problemNameBase << std::endl);
00277 
00278   //Save the initial guess passed to the solver
00279   saved_initial_guess = initial_guess;
00280 
00281   //Temporarily create a map of sub-solvers, used for obtaining the initial parameter vector and 
00282   //   for figuring out what types of derivatives are supported.
00283   std::map<std::string, SolverSubSolverData> subSolversData;
00284   std::map<std::string, Teuchos::RCP<Teuchos::ParameterList> >::const_iterator itp;
00285   for(itp = subProblemAppParams.begin(); itp != subProblemAppParams.end(); ++itp) {
00286     const std::string& name = itp->first;
00287     const Teuchos::RCP<Teuchos::ParameterList>& param_list = itp->second;
00288     const SolverSubSolver& sub = CreateSubSolver( param_list , *comm);
00289 
00290     subSolversData[ name ] = CreateSubSolverData( sub );
00291 
00292     // Create Epetra map for solution vector (second response vector).  Assume 
00293     //  each subSolver has the same map, so just get the first one.
00294     if(itp == subProblemAppParams.begin()) {
00295       Teuchos::RCP<const Epetra_Map> sub_x_map = sub.app->getMap();
00296       TEUCHOS_TEST_FOR_EXCEPT( sub_x_map == Teuchos::null );
00297       epetra_x_map = Teuchos::rcp(new Epetra_Map( *sub_x_map ));
00298     }    
00299   }
00300 
00301   //Determine whether we should support DgDp (all sub-solvers must support DpDg for QCAD::Solver to)
00302   bSupportDpDg = true;  
00303   std::map<std::string, SolverSubSolverData>::const_iterator it;
00304   for(it = subSolversData.begin(); it != subSolversData.end(); ++it) {
00305     deriv_support = (it->second).deriv_support;
00306     if(deriv_support.none()) { bSupportDpDg = false; break; } //test if p=0, g=0 DgDp is supported
00307   }
00308 
00309   // We support all dg/dp layouts model supports, plus the linear op layout
00310   if(bSupportDpDg) deriv_support.plus(DERIV_LINEAR_OP);
00311 
00312   //Setup Parameter and responses maps
00313   
00314   // input file can have 
00315   //    <Parameter name="Parameter 0" type="string" value="Poisson[0]" />
00316   //    <Parameter name="Parameter 1" type="string" value="Poisson[1:3]" />
00317   //
00318   //    <Parameter name="Response 0" type="string" value="Poisson[0] # charge" />
00319   //    <Parameter name="Response 0" type="string" value="Schrodinger[1,3]" />
00320   //    <Parameter name="Response 0" type="string" value="=dist(Poisson[1:4],Poisson[4:7]) # distance example" />
00321 
00322   
00323   Teuchos::ParameterList& paramList = problemParams.sublist("Parameters");
00324   setupParameterMapping(paramList, defaultSubSolver, subSolversData);
00325 
00326   Teuchos::ParameterList& responseList = problemParams.sublist("Response Functions");
00327   setupResponseMapping(responseList, defaultSubSolver, nEigenvectors, subSolversData);
00328 
00329   num_p = (nParameters > 0) ? 1 : 0; // Only use first parameter (p) vector, if there are any parameters
00330   num_g = (responseFns.size() > 0) ? 1 : 0; // Only use first response vector (but really one more than num_g -- 2nd holds solution vector)
00331 
00332 
00333 #ifndef EPETRA_NO_32BIT_GLOBAL_INDICES
00334   typedef int GlobalIndex;
00335 #else
00336   typedef long long GlobalIndex;
00337 #endif
00338 
00339   // Create Epetra map for parameter vector (only one since num_p always == 1)
00340   epetra_param_map = Teuchos::rcp(new Epetra_LocalMap(static_cast<GlobalIndex>(nParameters), 0, *comm));
00341 
00342   // Create Epetra map for (first) response vector
00343   epetra_response_map = Teuchos::rcp(new Epetra_LocalMap(static_cast<GlobalIndex>(nResponseDoubles), 0, *comm));
00344      //ANDY: if (nResponseDoubles > 0) needed ??
00345 
00346   // Get vector of initial parameter values
00347   epetra_param_vec = Teuchos::rcp(new Epetra_Vector(*(epetra_param_map)));
00348 
00349   // Take initial value from the first (if multiple) parameter 
00350   //    fns for each given parameter
00351   for(std::size_t i=0; i<nParameters; i++) {
00352     (*epetra_param_vec)[i] = paramFnVecs[i][0]->getInitialParam(subSolversData);
00353   }
00354 }
00355 
00356 QCAD::Solver::~Solver()
00357 {
00358 }
00359 
00360 
00361 Teuchos::RCP<Teuchos::ParameterList> 
00362 QCAD::Solver::createPoissonInputFile(const Teuchos::RCP<Teuchos::ParameterList>& appParams,
00363              int numDims, int nEigen, const std::string& specialProcessing,
00364              const std::string& xmlOutputFile, const std::string& exoOutputFile) const
00365 {
00366   Teuchos::ParameterList& problemParams = appParams->sublist("Problem");
00367   
00368   int vizDetail         = problemParams.get<int>("Phalanx Graph Visualization Detail",0);  
00369   double lenUnit        = problemParams.get<double>("Length Unit In Meters", 1e-6);
00370   double energyUnit     = problemParams.get<double>("Energy Unit In Electron Volts", 1.0);
00371   std::string matrlFile = problemParams.get<std::string>("MaterialDB Filename", "materials.xml");
00372 
00373   bool bXCPot  = problemParams.get<bool>("Include exchange-correlation potential",false);
00374   bool bQBOnly = problemParams.get<bool>("Only solve schrodinger in quantum blocks",true);
00375   bool bUsePCMethod = problemParams.get<bool>("Use predictor-corrector method",false);
00376   
00377 
00378   double Temp = -1;
00379   if(specialProcessing == "Coulomb" || specialProcessing == "Coulomb imaginary")
00380     Temp = problemParams.get<double>("Temperature",-1);  //Not required, but still uses Temp (for Poisson DBCs) TODO: what does default 300K mean for Schrodinger-CI DBCs? shift?
00381   else Temp = problemParams.get<double>("Temperature");  //Temperature required for all poisson cases except "Coulomb" modes
00382 
00383 
00384   // Get poisson & schrodinger problem sublists
00385   Teuchos::ParameterList& poisson_subList = problemParams.sublist("Poisson Problem", false);
00386   Teuchos::ParameterList& schro_subList = problemParams.sublist("Schrodinger Problem", false);
00387   
00388   // Create input parameter list for poission app which mimics a separate input file
00389   Teuchos::RCP<Teuchos::ParameterList> poisson_appParams = 
00390     Teuchos::createParameterList("Poisson Subapplication Parameters - " + specialProcessing);
00391   Teuchos::ParameterList& poisson_probParams = poisson_appParams->sublist("Problem",false);
00392   
00393   poisson_probParams.set("Name", QCAD::strdim("Poisson",numDims));
00394   poisson_probParams.set("Phalanx Graph Visualization Detail", vizDetail);
00395   poisson_probParams.set("Length Unit In Meters",lenUnit);
00396   poisson_probParams.set("Energy Unit In Electron Volts",energyUnit); 
00397   poisson_probParams.set("MaterialDB Filename", matrlFile);
00398   if(Temp >= 0) poisson_probParams.set("Temperature",Temp);
00399 
00400   // Poisson Source sublist processing
00401   {
00402     Teuchos::ParameterList auto_sourceList;
00403     auto_sourceList.set("Factor",1.0);
00404     auto_sourceList.set("Device","elementblocks");
00405 
00406     if(specialProcessing == "initial poisson") {
00407       auto_sourceList.set("Quantum Region Source", "semiclassical");
00408       auto_sourceList.set("Non Quantum Region Source", "semiclassical");
00409 
00410     } else if (specialProcessing == "couple to schrodinger") {
00411       auto_sourceList.set("Quantum Region Source", "schrodinger");
00412       auto_sourceList.set("Non Quantum Region Source", bQBOnly ? "semiclassical" : "schrodinger");
00413       auto_sourceList.set("Eigenvectors to Import", nEigen);
00414       auto_sourceList.set("Eigenvectors are Real", bRealEvecs);
00415       auto_sourceList.set("Use predictor-corrector method", bUsePCMethod);
00416       auto_sourceList.set("Include exchange-correlation potential", bXCPot);
00417       auto_sourceList.set("Fixed Quantum Occupation", fixedPSOcc);
00418 
00419     } else if (specialProcessing == "Coulomb") {
00420       auto_sourceList.set("Quantum Region Source", "coulomb");
00421       auto_sourceList.set("Non Quantum Region Source", "none");
00422       auto_sourceList.set("Imaginary Part of Coulomb Source", false);
00423       auto_sourceList.set("Eigenvectors to Import", nEigen);
00424       auto_sourceList.set("Eigenvectors are Real", bRealEvecs);
00425 
00426     } else if (specialProcessing == "Coulomb imaginary") {
00427       auto_sourceList.set("Quantum Region Source", "coulomb");
00428       auto_sourceList.set("Non Quantum Region Source", "none");
00429       auto_sourceList.set("Imaginary Part of Coulomb Source", true);
00430       auto_sourceList.set("Eigenvectors to Import", nEigen);
00431       auto_sourceList.set("Eigenvectors are Real", bRealEvecs);
00432 
00433     } else if (specialProcessing == "delta") {
00434       auto_sourceList.set("Quantum Region Source", "schrodinger");
00435       auto_sourceList.set("Non Quantum Region Source", "none");
00436       auto_sourceList.set("Eigenvectors to Import", nEigen);
00437       auto_sourceList.set("Eigenvectors are Real", bRealEvecs);
00438       auto_sourceList.set("Include exchange-correlation potential", bXCPot);
00439       auto_sourceList.set("Fixed Quantum Occupation", fixedPSOcc);
00440 
00441     } else if (specialProcessing == "no charge") {
00442       auto_sourceList.set("Quantum Region Source", "none");
00443       auto_sourceList.set("Non Quantum Region Source", "none");
00444       auto_sourceList.set("Eigenvectors to Import", nEigen); //needed for responses
00445       auto_sourceList.set("Eigenvectors are Real", bRealEvecs);
00446 
00447     } else if (specialProcessing == "CI") {
00448       auto_sourceList.set("Quantum Region Source", "ci");
00449       auto_sourceList.set("Non Quantum Region Source", "semiclassical");
00450       auto_sourceList.set("Eigenvectors to Import", nEigen);
00451       auto_sourceList.set("Eigenvectors are Real", bRealEvecs);
00452 
00453     } else if (specialProcessing != "none") 
00454       TEUCHOS_TEST_FOR_EXCEPTION( true, Teuchos::Exceptions::InvalidParameter, 
00455           "Invalid special processing for Poisson input: " << specialProcessing);
00456     
00457     Teuchos::ParameterList& sourceList = poisson_probParams.sublist("Poisson Source", false);
00458     if(poisson_subList.isSublist("Poisson Source"))
00459       sourceList.setParameters( poisson_subList.sublist("Poisson Source") );
00460     sourceList.setParametersNotAlreadySet( auto_sourceList );
00461   }
00462 
00463   // Permittivity sublist processing
00464   {
00465     Teuchos::ParameterList auto_permList;
00466     auto_permList.set("Permittivity Type","Block Dependent");
00467 
00468     Teuchos::ParameterList& permList = poisson_probParams.sublist("Permittivity", false);
00469     if(poisson_subList.isSublist("Permittivity"))
00470       permList.setParameters( poisson_subList.sublist("Permittivity") );
00471     permList.setParametersNotAlreadySet( auto_permList );
00472   }  
00473 
00474 
00475   // Dirichlet BC sublist processing
00476   if(poisson_subList.isSublist("Dirichlet BCs")) {
00477     Teuchos::ParameterList& poisson_dbcList = poisson_probParams.sublist("Dirichlet BCs", false);
00478     poisson_dbcList.setParameters(poisson_subList.sublist("Dirichlet BCs"));
00479   }
00480   else if(schro_subList.isSublist("Dirichlet BCs")) {
00481     Teuchos::ParameterList& poisson_dbcList = poisson_probParams.sublist("Dirichlet BCs", false);
00482     const Teuchos::ParameterList& schro_dbcList = schro_subList.sublist("Dirichlet BCs");
00483     Teuchos::ParameterList::ConstIterator it; double* dummy = NULL;
00484     for(it = schro_dbcList.begin(); it != schro_dbcList.end(); ++it) {
00485       std::string dbcName = schro_dbcList.name(it);
00486       std::size_t k = dbcName.find("psi");
00487       if( k != std::string::npos ) {
00488   dbcName.replace(k, 3 /* len("psi") */, "Phi");  // replace Phi -> psi
00489   poisson_dbcList.set( dbcName, schro_dbcList.entry(it).getValue(dummy) ); //copy all schrodinger DBCs
00490       }
00491     }
00492   }
00493 
00494 
00495 
00496   // Parameters sublist processing
00497   if( specialProcessing == "Coulomb" || specialProcessing == "Coulomb imaginary" ) {
00499     Teuchos::ParameterList& paramList = poisson_probParams.sublist("Parameters", false);
00500     if(poisson_subList.isSublist("Parameters"))
00501       paramList.setParameters(poisson_subList.sublist("Parameters"));
00502     
00503     int nParams = paramList.get<int>("Number", 0);
00504     paramList.set("Number", nParams + 2); //assumes Source Eigenvector X are not already params
00505     paramList.set(Albany::strint("Parameter",nParams), "Source Eigenvector 1");
00506     paramList.set(Albany::strint("Parameter",nParams+1), "Source Eigenvector 2");
00507   }
00508   else if(specialProcessing == "couple to schrodinger" ) {
00509     Teuchos::ParameterList& paramList = poisson_probParams.sublist("Parameters", false);
00510     if(poisson_subList.isSublist("Parameters"))
00511       paramList.setParameters(poisson_subList.sublist("Parameters"));
00512     
00513     int nParams = paramList.get<int>("Number", 0);
00514     paramList.set("Number", nParams + 1);
00515     paramList.set(Albany::strint("Parameter",nParams), "Previous Quantum Density Mixing Factor");
00516   }
00517   else {
00518     Teuchos::ParameterList& poisson_paramsList = poisson_probParams.sublist("Parameters", false);
00519     if(poisson_subList.isSublist("Parameters"))
00520       poisson_paramsList.setParameters(poisson_subList.sublist("Parameters"));
00521     else poisson_paramsList.set("Number", 0);
00522   }
00523 
00524 
00525   // Reponse Functions sublist processing
00526   bool addCoulombIntegralResponses = ((specialProcessing == "delta") ||
00527               (specialProcessing == "Coulomb") ||
00528               (specialProcessing == "Coulomb imaginary") ||
00529               (specialProcessing == "no charge") );
00530          
00531   if(addCoulombIntegralResponses) 
00532   {
00533     Teuchos::ParameterList& responseList = poisson_probParams.sublist("Response Functions", false);
00534     if(poisson_subList.isSublist("Response Functions"))
00535       responseList.setParameters(poisson_subList.sublist("Response Functions"));
00536 
00538     int initial_nResponses = responseList.get<int>("Number",0); //Shift existing responses
00539     int added_nResponses = nEigen * (nEigen + 1) / 2;
00540     if(!bRealEvecs) added_nResponses *= 2; //mult by 2 for real & imag parts
00541 
00542     char buf1[200], buf2[200], buf1i[200], buf2i[200];
00543     responseList.set("Number", initial_nResponses + added_nResponses);
00544 
00545     //shift response indices of existing responses by added_responses so added responses index from zero
00546     for(int i=initial_nResponses-1; i >= 0; i--) {       
00547       std::string respType = responseList.get<std::string>(Albany::strint("Response",i));
00548       responseList.set(Albany::strint("Response",i + added_nResponses), respType); //shift response index
00549       if(responseList.isSublist( Albany::strint("ResponseParams",i) )) {            //shift response params index (if applicable)
00550   responseList.sublist( Albany::strint("ResponseParams",i + added_nResponses) ) = 
00551     Teuchos::ParameterList(responseList.sublist( Albany::strint("ResponseParams",i) ) ); //create new copy of list
00552   responseList.sublist( Albany::strint("ResponseParams",i) ) = Teuchos::ParameterList(Albany::strint("ResponseParams",i)); //clear sublist i
00553       }
00554     }
00555 
00556     int iResponse = 0;
00557     for(int i=0; i<nEigenvectors; i++) {
00558       sprintf(buf1, "%s_Re%d", "Evec", i);
00559       sprintf(buf1i, "%s_Im%d", "Evec", i);
00560       for(int j=i; j<nEigenvectors; j++) {
00561   sprintf(buf2, "%s_Re%d", "Evec", j);
00562   sprintf(buf2i, "%s_Im%d", "Evec", j);
00563 
00564   responseList.set(Albany::strint("Response",iResponse), "Field Integral");
00565   Teuchos::ParameterList& responseParams = responseList.sublist(Albany::strint("ResponseParams",iResponse));
00566   responseParams.set("Field Name", "Electric Potential");  // same as solution, but must be at quad points
00567   responseParams.set("Field Name 1", buf1);
00568   responseParams.set("Field Name 2", buf2);  
00569   if(!bRealEvecs) {
00570     responseParams.set("Field Name Im 1", buf1i);
00571     responseParams.set("Field Name Im 2", buf2i);
00572     responseParams.set("Conjugate Field 1", true);
00573     responseParams.set("Conjugate Field 2", false);
00574   }
00575   responseParams.set("Integrand Length Unit", "mesh"); // same as mesh
00576   responseParams.set("Return Imaginary Part", false);
00577 
00578   iResponse++;
00579 
00580   if(!bRealEvecs) {
00581     responseList.set(Albany::strint("Response",iResponse), "Field Integral");
00582     Teuchos::ParameterList& responseParams2 = responseList.sublist(Albany::strint("ResponseParams",iResponse));
00583     responseParams2.set("Field Name", "Electric Potential");  // same as solution, but must be at quad points
00584     responseParams2.set("Field Name 1", buf1);  responseParams2.set("Field Name Im 1", buf1i);
00585     responseParams2.set("Field Name 2", buf2);  responseParams2.set("Field Name Im 2", buf2i);
00586     responseParams2.set("Conjugate Field 1", true);
00587     responseParams2.set("Conjugate Field 2", false);
00588     responseParams2.set("Integrand Length Unit", "mesh"); // same as mesh
00589     responseParams2.set("Return Imaginary Part", true);
00590 
00591     iResponse++;
00592   }
00593 
00594       }
00595     }
00596   }
00597   else if(specialProcessing == "couple to schrodinger" || specialProcessing == "initial poisson" || specialProcessing == "CI")
00598   {
00599     // Assume user has not already added the responses needed to couple with a schrodinger solver, so add them here
00600     Teuchos::ParameterList& responseList = poisson_probParams.sublist("Response Functions", false);
00601     if(poisson_subList.isSublist("Response Functions"))
00602       responseList.setParameters(poisson_subList.sublist("Response Functions"));
00603 
00604     int nResponses = responseList.get<int>("Number",0);
00605     int nAddedResponses = 7;
00606     responseList.set("Number", nResponses+nAddedResponses);
00607 
00608     int iResponse = nResponses;
00609     Teuchos::ParameterList* pResponseParams;
00610 
00611     // Add responses to save the Electric Potential, Conduction Band, and Potential for use in the P-S iterations
00612     responseList.set(Albany::strint("Response",iResponse), "Save Field");
00613     pResponseParams = &responseList.sublist(Albany::strint("ResponseParams",iResponse));
00614     pResponseParams->set("Field Name", "Electron Density");
00615     pResponseParams->set("State Name", "PS Saved Electron Density");
00616     pResponseParams->set("Output Cell Average", false);
00617     pResponseParams->set("Output to Exodus", false);
00618     iResponse++;
00619 
00620     responseList.set(Albany::strint("Response",iResponse), "Save Field");
00621     pResponseParams = &responseList.sublist(Albany::strint("ResponseParams",iResponse));
00622     pResponseParams->set("Field Name", "Conduction Band");
00623     pResponseParams->set("State Name", "PS Conduction Band");
00624     pResponseParams->set("Output Cell Average", false);
00625     pResponseParams->set("Output to Exodus", false);
00626     iResponse++;
00627 
00628     responseList.set(Albany::strint("Response",iResponse), "Save Field");
00629     pResponseParams = &responseList.sublist(Albany::strint("ResponseParams",iResponse));
00630     pResponseParams->set("Field Name", "Potential");
00631     pResponseParams->set("State Name", "PS Saved Solution");
00632     pResponseParams->set("Output Cell Average", false);
00633     pResponseParams->set("Output to Exodus", false);
00634     iResponse++;
00635 
00636     // Add dummy response to "save" into "PS Previous Poisson Potential" state so that memory is allocated 
00637     //  within the state manager for this state.
00638     responseList.set(Albany::strint("Response",iResponse), "Save Field");
00639     pResponseParams = &responseList.sublist(Albany::strint("ResponseParams",iResponse));
00640     pResponseParams->set("Field Name", "Potential");
00641     pResponseParams->set("State Name", "PS Previous Poisson Potential");
00642     pResponseParams->set("Output Cell Average", false);
00643     pResponseParams->set("Output to Exodus", false);
00644     pResponseParams->set("Memory Placeholder Only", true);
00645     iResponse++;
00646 
00647     // Add dummy response to "save" into "PS Previous Electron Density" state so that memory is allocated 
00648     //  within the state manager for this state.
00649     responseList.set(Albany::strint("Response",iResponse), "Save Field");
00650     pResponseParams = &responseList.sublist(Albany::strint("ResponseParams",iResponse));
00651     pResponseParams->set("Field Name", "Electron Density");
00652     pResponseParams->set("State Name", "PS Previous Electron Density");
00653     pResponseParams->set("Output Cell Average", false);
00654     pResponseParams->set("Output to Exodus", false);
00655     pResponseParams->set("Memory Placeholder Only", true);
00656     iResponse++;
00657 
00658 
00659     // SECOND TO LAST RESPONSE: compute the total number of electrons in quantum regions (used for CI runs) 
00660     responseList.set(Albany::strint("Response",iResponse), "Field Integral");
00661     pResponseParams = &responseList.sublist(Albany::strint("ResponseParams",iResponse));
00662     pResponseParams->set("Field Name", "Electron Density");
00663     pResponseParams->set("Quantum Element Blocks Only", true);
00664     iResponse++;
00665 
00666     // LAST added response: compute the minimum of conduction band in the quantum regions.  This MUST be
00667     //  the last response, since the P-S loop expects it there (and uses it to compute the shift for the schrodinger eigensolver)
00668     // Note: it used to be the first response, but this would require shifting user responses, and would mess with indices
00669     responseList.set(Albany::strint("Response",iResponse), "Field Value");
00670     pResponseParams = &responseList.sublist(Albany::strint("ResponseParams",iResponse));
00671     pResponseParams->set("Operation", "Minimize");
00672     pResponseParams->set("Operation Field Name", "Conduction Band");
00673     pResponseParams->set("Quantum Element Blocks Only", true);
00674 
00675   }
00676   else {
00677     Teuchos::ParameterList& poisson_respList = poisson_probParams.sublist("Response Functions", false);
00678     if(poisson_subList.isSublist("Response Functions"))
00679       poisson_respList.setParameters(poisson_subList.sublist("Response Functions"));
00680     else poisson_respList.set("Number", 0);
00681   }  
00682 
00683   // Initial Condition sublist processing: copy list from main problem if it exists
00684   if(problemParams.isSublist("Initial Condition"))
00685   {
00686     Teuchos::ParameterList& icList = poisson_probParams.sublist("Initial Condition", false);
00687     icList.setParameters( problemParams.sublist("Initial Condition") );
00688   }  
00689 
00690 
00691   // Discretization sublist processing
00692   Teuchos::ParameterList& discList = appParams->sublist("Discretization");
00693   Teuchos::ParameterList& poisson_discList = poisson_appParams->sublist("Discretization", false);
00694   poisson_discList.setParameters(discList);
00695   if(exoOutputFile.length() > 0) 
00696     poisson_discList.set("Exodus Output File Name",exoOutputFile);
00697   else poisson_discList.remove("Exodus Output File Name",false); 
00698 
00699   // Piro sublist processing
00700   Teuchos::ParameterList& poisson_piroList = poisson_appParams->sublist("Piro", false);
00701   poisson_piroList.setParameters( appParams->sublist("Piro") ); // copy Piro list from app
00702 
00703   if(specialProcessing != "none") // then don't compute sensitivities
00704     poisson_piroList.sublist("Analysis").sublist("Solve").set("Compute Sensitivities", false); //ANDY - does this work?
00705 
00706   if(xmlOutputFile.length() > 0 && solverComm->MyPID() == 0)
00707     Teuchos::writeParameterListToXmlFile(*poisson_appParams, xmlOutputFile);
00708 
00709   return poisson_appParams;
00710 }
00711 
00712 
00713 Teuchos::RCP<Teuchos::ParameterList> 
00714 QCAD::Solver::createSchrodingerInputFile(const Teuchos::RCP<Teuchos::ParameterList>& appParams,
00715            int numDims, int nEigen, const std::string& specialProcessing,
00716            const std::string& xmlOutputFile, const std::string& exoOutputFile) const
00717 {
00718   Teuchos::ParameterList& problemParams = appParams->sublist("Problem");
00719   
00720   int vizDetail         = problemParams.get<int>("Phalanx Graph Visualization Detail",0);  
00721   double lenUnit        = problemParams.get<double>("Length Unit In Meters", 1e-6);
00722   double energyUnit     = problemParams.get<double>("Energy Unit In Electron Volts", 1.0);
00723   std::string matrlFile = problemParams.get<std::string>("MaterialDB Filename", "materials.xml");
00724 
00725   // Only used by schrodinger, poisson-schrodinger, and poisson-schroinger-ci solvers, but has default
00726   bool bQBOnly = problemParams.get<bool>("Only solve schrodinger in quantum blocks",true);
00727 
00728   // Get poisson and schrodinger problem sublists
00729   Teuchos::ParameterList& schro_subList = problemParams.sublist("Schrodinger Problem", false);
00730   Teuchos::ParameterList& poisson_subList = problemParams.sublist("Poisson Problem", false);
00731 
00732 
00733   Teuchos::RCP<Teuchos::ParameterList> schro_appParams = 
00734     Teuchos::createParameterList("Schrodinger Subapplication Parameters");
00735   Teuchos::ParameterList& schro_probParams = schro_appParams->sublist("Problem",false);
00736 
00737   schro_probParams.set("Name", QCAD::strdim("Schrodinger",numDims));
00738   if(eigensolverName == "LOBPCG") 
00739     schro_probParams.set("Solution Method", "Eigensolve");
00740   else if(eigensolverName == "LOCA")
00741     schro_probParams.set("Solution Method", "Continuation");
00742   else
00743     TEUCHOS_TEST_FOR_EXCEPTION( true, Teuchos::Exceptions::InvalidParameter, 
00744   "Invalid eigensolver name for Schrodinger input: " << eigensolverName);
00745 
00746   schro_probParams.set("Phalanx Graph Visualization Detail", vizDetail);
00747   schro_probParams.set("Energy Unit In Electron Volts",energyUnit);
00748   schro_probParams.set("Length Unit In Meters",lenUnit);
00749   schro_probParams.set("MaterialDB Filename", matrlFile);
00750   schro_probParams.set("Only solve in quantum blocks", bQBOnly);
00751 
00752   // Potential sublist processing
00753   if(specialProcessing == "couple to poisson")
00754   {
00755     Teuchos::ParameterList auto_potList;
00756     auto_potList.set("Scaling Factor",1.0);
00757     auto_potList.set("Type","From State");
00758     auto_potList.set("State Name", "PS Conduction Band"); 
00759     
00760     Teuchos::ParameterList& potList = schro_probParams.sublist("Potential", false);
00761     if(schro_subList.isSublist("Potential"))
00762       potList.setParameters(schro_subList.sublist("Potential"));
00763     potList.setParametersNotAlreadySet(auto_potList);
00764   }
00765   else if(schro_subList.isSublist("Potential")) {
00766     schro_probParams.sublist("Potential", false).setParameters(schro_subList.sublist("Potential"));
00767   }
00768 
00769 
00770   // Dirichlet BC sublist processing
00771   if(eigensolverName == "LOCA") {
00772     if(schro_subList.isSublist("Dirichlet BCs")) {
00773       Teuchos::ParameterList& schro_dbcList = schro_probParams.sublist("Dirichlet BCs", false);
00774       schro_dbcList.setParameters(schro_subList.sublist("Dirichlet BCs"));
00775     }
00776     else if(poisson_subList.isSublist("Dirichlet BCs")) {
00777       Teuchos::ParameterList& schro_dbcList = schro_probParams.sublist("Dirichlet BCs", false);
00778       const Teuchos::ParameterList& poisson_dbcList = poisson_subList.sublist("Dirichlet BCs");
00779       Teuchos::ParameterList::ConstIterator it;
00780       for(it = poisson_dbcList.begin(); it != poisson_dbcList.end(); ++it) {
00781   std::string dbcName = poisson_dbcList.name(it);
00782   std::size_t k = dbcName.find("Phi");
00783   if( k != std::string::npos ) {
00784     dbcName.replace(k, 3, "psi");  // replace Phi -> psi ( 3 == len("Phi") )
00785     schro_dbcList.set( dbcName, 0.0 ); //copy all poisson DBCs but set to zero
00786   }
00787       }
00788     }
00789   }
00790 
00791   // Parameters sublist processing -- ensure "Schrodinger Potential Scaling Factor" 
00792   //   appears in list, since this is needed by LOCA continuation analysis
00793   {
00794     Teuchos::ParameterList& paramsList = schro_probParams.sublist("Parameters", false);    
00795     if(schro_subList.isSublist("Parameters"))
00796       paramsList.setParameters(schro_subList.sublist("Parameters"));
00797 
00798     bool bAddScalingFactor = true;
00799     Teuchos::ParameterList::ConstIterator it; std::string* dummy = NULL;
00800     for(it = paramsList.begin(); it != paramsList.end(); ++it) {
00801       if(paramsList.entry(it).isType<std::string>() &&
00802    paramsList.entry(it).getValue(dummy) == "Schrodinger Potential Scaling Factor") { 
00803   bAddScalingFactor = false; break; 
00804       }
00805     }
00806 
00807     if(bAddScalingFactor) {
00808       int nParams = paramsList.get<int>("Number",0);
00809       paramsList.set("Number", nParams+1);
00810       paramsList.set( Albany::strint("Parameter",nParams), "Schrodinger Potential Scaling Factor" );
00811     }
00812   }
00813 
00814   // Response Functions sublist processing
00815   if(specialProcessing == "couple to poisson")
00816   {
00817     // Assume user has not already added the responses needed to couple with a poisson solver, so add them here
00818     Teuchos::ParameterList& responseList = schro_probParams.sublist("Response Functions", false);
00819     if(schro_subList.isSublist("Response Functions"))
00820       responseList.setParameters(schro_subList.sublist("Response Functions"));
00821 
00822     int nResponses = responseList.get<int>("Number",0);
00823     responseList.set("Number", nResponses+3);
00824 
00825       // First added response: dummy "save" into "PS Conduction Band" state so memory is allocated
00826       //  within the state manager for this state.  The state gets filled prior to solving the schrodinger problem
00827       //  via the state manager's importing it, and then the state is used as the potential energy in the schro. eqn.
00828     responseList.set(Albany::strint("Response",nResponses), "Save Field");
00829     Teuchos::ParameterList& responseParams1 = responseList.sublist(Albany::strint("ResponseParams",nResponses));
00830     responseParams1.set("Field Name", "V"); //Fixed field name of schrodinger potential
00831     responseParams1.set("State Name", "PS Conduction Band");
00832     responseParams1.set("Output Cell Average", false);
00833     responseParams1.set("Output to Exodus", false);
00834     responseParams1.set("Memory Placeholder Only", true);
00835 
00836       // Second added response: dummy "save" into "PS Previous Poisson Potential" state so that memory is allocated 
00837       //  within the state manager for this state. (see Poisson-Schrodigner iteration code)
00838     responseList.set(Albany::strint("Response",nResponses+1), "Save Field");
00839     Teuchos::ParameterList& responseParams2 = responseList.sublist(Albany::strint("ResponseParams",nResponses+1));
00840     responseParams2.set("Field Name", "V");
00841     responseParams2.set("State Name", "PS Previous Poisson Potential");
00842     responseParams2.set("Output Cell Average", false);
00843     responseParams2.set("Output to Exodus", false);
00844     responseParams2.set("Memory Placeholder Only", true);
00845 
00846 
00847       // Third added response: dummy "save" into "PS Previous Electron Density" state so that memory is allocated 
00848       //  within the state manager for this state. (see Poisson-Schrodigner iteration code)
00849     responseList.set(Albany::strint("Response",nResponses+2), "Save Field");
00850     Teuchos::ParameterList& responseParams3 = responseList.sublist(Albany::strint("ResponseParams",nResponses+2));
00851     responseParams3.set("Field Name", "V");
00852     responseParams3.set("State Name", "PS Previous Electron Density");
00853     responseParams3.set("Output Cell Average", false);
00854     responseParams3.set("Output to Exodus", false);
00855     responseParams3.set("Memory Placeholder Only", true);
00856 
00857   }
00858   else {
00859     Teuchos::ParameterList& schro_respList = schro_probParams.sublist("Response Functions", false);
00860     if(schro_subList.isSublist("Response Functions"))
00861       schro_respList.setParameters(schro_subList.sublist("Response Functions"));
00862     else schro_respList.set("Number", 0);
00863   }
00864 
00865     // Discretization sublist processing
00866   Teuchos::ParameterList& discList = appParams->sublist("Discretization");
00867   Teuchos::ParameterList& schro_discList = schro_appParams->sublist("Discretization", false);
00868   schro_discList.setParameters(discList);
00869   if(exoOutputFile.length() > 0) 
00870     schro_discList.set("Exodus Output File Name",exoOutputFile);
00871   else schro_discList.remove("Exodus Output File Name",false); 
00872 
00873     // Piro sublist processing
00874   Teuchos::ParameterList& schro_piroList = schro_appParams->sublist("Piro", false);
00875   schro_piroList.setParameters( appParams->sublist("Piro") ); // copy Piro list from app
00876   schro_piroList.sublist("Analysis").sublist("Solve").set("Compute Sensitivities", false); // don't compute sensitivities
00877 
00878   // setup computation of correct number of eigenvalues
00879   schro_piroList.sublist("LOCA").sublist("Stepper").set("Compute Eigenvalues", true);
00880   schro_piroList.sublist("LOCA").sublist("Stepper").sublist("Eigensolver").set("Num Eigenvalues", nEigen);
00881   schro_piroList.sublist("LOCA").sublist("Stepper").sublist("Eigensolver").set("Save Eigenvectors", nEigen);
00882 
00883   if(xmlOutputFile.length() > 0 && solverComm->MyPID() == 0)
00884     Teuchos::writeParameterListToXmlFile(*schro_appParams, xmlOutputFile);
00885 
00886   return schro_appParams;
00887 }
00888 
00889 
00890 Teuchos::RCP<Teuchos::ParameterList> 
00891 QCAD::Solver::createPoissonSchrodingerInputFile(const Teuchos::RCP<Teuchos::ParameterList>& appParams,
00892             int numDims, int nEigen, const std::string& xmlOutputFile,
00893             const std::string& exoOutputFile) const
00894 {
00895   Teuchos::ParameterList& problemParams = appParams->sublist("Problem");
00896   
00897   int vizDetail         = problemParams.get<int>("Phalanx Graph Visualization Detail");  
00898   double lenUnit        = problemParams.get<double>("Length Unit In Meters", 1e-6);
00899   double energyUnit     = problemParams.get<double>("Energy Unit In Electron Volts", 1.0);
00900   std::string matrlFile = problemParams.get<std::string>("MaterialDB Filename", "materials.xml");
00901 
00902   bool bXCPot  = problemParams.get<bool>("Include exchange-correlation potential",false);
00903   bool bQBOnly = problemParams.get<bool>("Only solve schrodinger in quantum blocks",true);
00904 
00905   double Temp = problemParams.get<double>("Temperature");
00906 
00907   // Get poisson & schodinger problem sublists
00908   Teuchos::ParameterList& poisson_subList = problemParams.sublist("Poisson Problem", false);
00909   Teuchos::ParameterList& schro_subList   = problemParams.sublist("Schrodinger Problem", false);
00910   
00911   // Create input parameter list for poission app which mimics a separate input file
00912   Teuchos::RCP<Teuchos::ParameterList> ps_appParams = 
00913     Teuchos::createParameterList("Poisson-Schrodinger Subapplication Parameters");
00914   Teuchos::ParameterList& ps_probParams = ps_appParams->sublist("Problem",false);
00915 
00916   ps_probParams.set("Solution Method", "QCAD Poisson-Schrodinger");  
00917   ps_probParams.set("Name", QCAD::strdim("Poisson Schrodinger",numDims));
00918   ps_probParams.set("Phalanx Graph Visualization Detail", vizDetail);
00919   ps_probParams.set("Length Unit In Meters",lenUnit);
00920   ps_probParams.set("Energy Unit In Electron Volts",energyUnit);
00921   ps_probParams.set("MaterialDB Filename", matrlFile);
00922   ps_probParams.set("Temperature",Temp);
00923   ps_probParams.set("Number of Eigenvalues",nEigen);
00924   ps_probParams.set("Verbose Output", true);
00925 
00926   ps_probParams.set("Include exchange-correlation potential", bXCPot);
00927   ps_probParams.set("Only solve schrodinger in quantum blocks", bQBOnly);
00928 
00929   // Poisson Problem sublist processing
00930   Teuchos::ParameterList& ps_poissonParams = ps_probParams.sublist("Poisson Problem",false);
00931 
00932   if(poisson_subList.isSublist("Poisson Source")) {
00933     Teuchos::ParameterList& tmp = ps_poissonParams.sublist("Poisson Source", false);
00934     tmp.setParameters(poisson_subList.sublist("Poisson Source"));
00935   }
00936 
00937   if(poisson_subList.isSublist("Dirichlet BCs")) {
00938     Teuchos::ParameterList& tmp = ps_poissonParams.sublist("Dirichlet BCs", false);
00939     tmp.setParameters(poisson_subList.sublist("Dirichlet BCs"));
00940   }
00941 
00942   if(poisson_subList.isSublist("Parameters")) {
00943     Teuchos::ParameterList& tmp = ps_poissonParams.sublist("Parameters", false);
00944     tmp.setParameters(poisson_subList.sublist("Parameters"));
00945   }
00946 
00947   if(poisson_subList.isSublist("Response Functions")) {
00948     Teuchos::ParameterList& tmp = ps_poissonParams.sublist("Response Functions", false);
00949     tmp.setParameters(poisson_subList.sublist("Response Functions"));
00950   }
00951 
00952 
00953   // Schrodinger Problem sublist processing
00954   Teuchos::ParameterList& ps_schroParams = ps_probParams.sublist("Schrodinger Problem",false);
00955 
00956   // copy Parameters, Dirichlet BCs, and Responses sublists from schro_subList if they're present
00957   if(schro_subList.isSublist("Dirichlet BCs")) {
00958     Teuchos::ParameterList& tmp = ps_schroParams.sublist("Dirichlet BCs", false);
00959     tmp.setParameters(schro_subList.sublist("Dirichlet BCs"));
00960   }
00961 
00962   if(schro_subList.isSublist("Parameters")) {
00963     Teuchos::ParameterList& tmp = ps_schroParams.sublist("Parameters", false);
00964     tmp.setParameters(schro_subList.sublist("Parameters"));
00965   }
00966 
00967   if(schro_subList.isSublist("Response Functions")) {
00968     Teuchos::ParameterList& tmp = ps_schroParams.sublist("Response Functions", false);
00969     tmp.setParameters(schro_subList.sublist("Response Functions"));
00970   }
00971 
00972 
00973   // Debug Output sublist processing
00974   if(appParams->isSublist("Debug Output")) {
00975     Teuchos::ParameterList& debugParams = appParams->sublist("Debug Output");
00976     std::string poissonXML = debugParams.get<std::string>("PS Poisson XML Input","");
00977     std::string schroXML   = debugParams.get<std::string>("PS Schrodinger XML Input","");
00978     std::string poissonExo = debugParams.get<std::string>("PS Poisson Exodus Output","");
00979     std::string schroExo   = debugParams.get<std::string>("PS Schrodinger Exodus Output","");
00980 
00981     Teuchos::ParameterList& ps_debugParams = ps_appParams->sublist("Debug Output");
00982     if(poissonXML.length() > 0) ps_debugParams.set("Poisson XML Input", poissonXML);
00983     if(schroXML.length() > 0)   ps_debugParams.set("Schrodinger XML Input", schroXML);
00984     if(poissonExo.length() > 0) ps_debugParams.set("Poisson Exodus Output", poissonExo);
00985     if(schroExo.length() > 0)   ps_debugParams.set("Schrodinger Exodus Output", schroExo);
00986   }
00987   
00988   // Discretization sublist processing
00989   Teuchos::ParameterList& discList = appParams->sublist("Discretization");
00990   Teuchos::ParameterList& ps_discList = ps_appParams->sublist("Discretization", false);
00991   ps_discList.setParameters(discList);
00992   if(exoOutputFile.length() > 0) 
00993     ps_discList.set("Exodus Output File Name",exoOutputFile);
00994   else ps_discList.remove("Exodus Output File Name",false); 
00995 
00996   
00997   // Piro sublist processing
00998   Teuchos::ParameterList& ps_piroList = ps_appParams->sublist("Piro", false);
00999   ps_piroList.setParameters( appParams->sublist("Piro") ); // copy Piro list from app
01000   ps_piroList.set("Solver Type", "NOX");  //note: not automatically filled in by SolverFactory
01001 
01002   //Use additional "CPS" parameters, which the user may specify under the NOX piro lists, to
01003   //  override the parameters NOX actually uses.  This, for example, allows the coupled poisson-schrodinger
01004   //  solver to use a large minimum step, as it is prone to getting "stuck" using tiny steps
01005   Teuchos::ParameterList& ps_backtrackList = ps_piroList.sublist("NOX").sublist("Line Search").sublist("Backtrack");
01006   const Teuchos::ParameterList& backtrackList =
01007     appParams->sublist("Piro").sublist("NOX").sublist("Line Search").sublist("Backtrack");
01008 
01009   if(backtrackList.isType<double>("CPS Default Step"))
01010     ps_backtrackList.set("Default Step", backtrackList.get<double>("CPS Default Step"));
01011   if(backtrackList.isType<double>("CPS Minimum Step"))
01012     ps_backtrackList.set("Minimum Step", backtrackList.get<double>("CPS Minimum Step"));
01013   if(backtrackList.isType<double>("CPS Recovery Step"))
01014     ps_backtrackList.set("Recovery Step", backtrackList.get<double>("CPS Recovery Step"));
01015 
01016   //DEBUG - put schrodinger-poisson solver in matrix free mode with no preconditioner
01017   //ps_piroList.set("Jacobian Operator","Matrix-Free");
01018   //ps_piroList.set("Matrix-Free Perturbation",1e-7);
01019   //ps_piroList.sublist("NOX").sublist("Direction").sublist("Newton").sublist("Stratimikos Linear Solver")
01020   //  .sublist("Stratimikos").set("Preconditioner Type", "None");
01021 
01022   if(xmlOutputFile.length() > 0 && solverComm->MyPID() == 0)
01023     Teuchos::writeParameterListToXmlFile(*ps_appParams, xmlOutputFile);
01024 
01025   return ps_appParams;
01026 }
01027 
01028 Teuchos::RCP<const Epetra_Map> QCAD::Solver::get_x_map() const
01029 {
01030   Teuchos::RCP<const Epetra_Map> neverused;
01031   return neverused;
01032 }
01033 
01034 Teuchos::RCP<const Epetra_Map> QCAD::Solver::get_f_map() const
01035 {
01036   Teuchos::RCP<const Epetra_Map> neverused;
01037   return neverused;
01038 }
01039 
01040 Teuchos::RCP<const Epetra_Map> QCAD::Solver::get_p_map(int l) const
01041 {
01042   TEUCHOS_TEST_FOR_EXCEPTION(l >= num_p || l < 0, Teuchos::Exceptions::InvalidParameter,
01043                      std::endl <<
01044                      "Error in QCAD::Solver::get_p_map():  " <<
01045                      "Invalid parameter index l = " <<
01046                      l << std::endl);
01047 
01048   return epetra_param_map;  //no index because num_p == 1 so l must be zero
01049 }
01050 
01051 Teuchos::RCP<const Epetra_Map> QCAD::Solver::get_g_map(int j) const
01052 {
01053   TEUCHOS_TEST_FOR_EXCEPTION(j > num_g || j < 0, Teuchos::Exceptions::InvalidParameter,
01054                      std::endl <<
01055                      "Error in QCAD::Solver::get_g_map():  " <<
01056                      "Invalid response index j = " <<
01057                      j << std::endl);
01058 
01059   if      (j < num_g) return epetra_response_map;  //no index because num_g == 1 so j must be zero
01060   else if (j == num_g) return epetra_x_map;
01061   return Teuchos::null;
01062 }
01063 
01064 Teuchos::RCP<const Epetra_Vector> QCAD::Solver::get_x_init() const
01065 {
01066   TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter, 
01067            "QCAD::Solver get_x_init() called but it shouldn't be");
01068   return saved_initial_guess;
01069 }
01070 
01071 Teuchos::RCP<const Epetra_Vector> QCAD::Solver::get_p_init(int l) const
01072 {
01073   TEUCHOS_TEST_FOR_EXCEPTION(l >= num_p || l < 0, Teuchos::Exceptions::InvalidParameter,
01074                      std::endl <<
01075                      "Error in QCAD::Solver::get_p_init():  " <<
01076                      "Invalid parameter index l = " <<
01077                      l << std::endl);
01078 
01079   //Just copy the vector of initial parameters we've already computed
01080   Teuchos::RCP<Epetra_Vector> p_init = 
01081     Teuchos::rcp(new Epetra_Vector(*(epetra_param_vec)));
01082 
01083   return p_init;
01084 }
01085 
01086 EpetraExt::ModelEvaluator::InArgs QCAD::Solver::createInArgs() const
01087 {
01088   EpetraExt::ModelEvaluator::InArgsSetup inArgs;
01089   inArgs.setModelEvalDescription("QCAD Solver Model Evaluator Description");
01090   inArgs.set_Np(num_p);
01091   return inArgs;
01092 }
01093 
01094 EpetraExt::ModelEvaluator::OutArgs QCAD::Solver::createOutArgs() const
01095 {
01096   //Based on Piro_Epetra_NOXSolver.cpp implementation
01097   EpetraExt::ModelEvaluator::OutArgsSetup outArgs;
01098   outArgs.setModelEvalDescription("QCAD Solver Multipurpose Model Evaluator");
01099   // Ng is 1 bigger then model-Ng so that the solution vector can be an outarg
01100   outArgs.set_Np_Ng(num_p, num_g+1);  //TODO: is the +1 necessary still??
01101 
01102   //Derivative info 
01103   if(bSupportDpDg) {
01104     for (int i=0; i<num_g; i++) {
01105       for (int j=0; j<num_p; j++)
01106   outArgs.setSupports(OUT_ARG_DgDp, i, j, deriv_support);
01107     }
01108   }
01109 
01110   return outArgs;
01111 }
01112 
01113 void 
01114 QCAD::Solver::evalModel(const InArgs& inArgs,
01115       const OutArgs& outArgs ) const
01116 {
01117   std::vector<double> eigenvalueResponses;
01118   std::map<std::string, SolverSubSolver> subSolvers;
01119   Teuchos::RCP<Teuchos::FancyOStream> out(Teuchos::VerboseObjectBase::getDefaultOStream());
01120 
01121   if(bVerbose) {
01122     if(num_p > 0) {   // or could use: (inArgs.Np() > 0)
01123       Teuchos::RCP<const Epetra_Vector> p = inArgs.get_p(0); //only use *first* param vector
01124       *out << "BEGIN QCAD Solver Parameters:" << std::endl;
01125       for(std::size_t i=0; i<nParameters; i++)
01126   *out << "  Parameter " << i << " = " << (*p)[i] << std::endl;
01127       *out << "END QCAD Solver Parameters" << std::endl;
01128     }
01129   }
01130    
01131   if( problemNameBase == "Poisson" ) {
01132       if(bVerbose) *out << "QCAD Solve: Simple Poisson solve" << std::endl;
01133 
01134       //Create Poisson solver & fill its parameters
01135       subSolvers[ "Poisson" ] = CreateSubSolver( getSubSolverParams("Poisson") , *solverComm, saved_initial_guess);
01136       fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "Poisson" ]);
01137 
01138       QCAD::SolveModel(subSolvers["Poisson"]);
01139       eigenvalueResponses.resize(0); // no eigenvalues in the Poisson problem
01140   }
01141 
01142   else if( problemNameBase == "Schrodinger" ) {
01143       if(bVerbose) *out << "QCAD Solve: Simple Schrodinger solve" << std::endl;
01144 
01145       //Create Schrodinger solver & fill its parameters
01146       subSolvers[ "Schrodinger" ] = CreateSubSolver( getSubSolverParams("Schrodinger") , *solverComm); // no initial guess
01147       fillSingleSubSolverParams(inArgs, "Schrodinger", subSolvers[ "Schrodinger" ]);
01148 
01149       Teuchos::RCP<Albany::EigendataStruct> eigenData = Teuchos::null;
01150       Teuchos::RCP<Albany::EigendataStruct> eigenDataNull = Teuchos::null;
01151 
01152       QCAD::SolveModel(subSolvers["Schrodinger"], eigenDataNull, eigenData);
01153       eigenvalueResponses = *(eigenData->eigenvalueRe); // copy eigenvalues to member variable
01154       for(std::size_t i=0; i<eigenvalueResponses.size(); ++i) eigenvalueResponses[i] *= -1; //apply minus sign (b/c of eigenval convention)
01155 
01156       // Create final observer to output evecs and solution
01157       Teuchos::RCP<Epetra_Vector> solnVec = subSolvers["Schrodinger"].responses_out->get_g(1); //get the *first* response vector (solution)
01158       Teuchos::RCP<MultiSolution_Observer> final_obs = 
01159   Teuchos::rcp(new QCAD::MultiSolution_Observer(subSolvers["Schrodinger"].app, mainAppParams)); 
01160       final_obs->observeSolution(*solnVec, "ZeroSolution", eigenData, 0.0);
01161   }
01162 
01163   else if( problemNameBase == "Poisson Schrodinger" )
01164     evalPoissonSchrodingerModel(inArgs, outArgs, eigenvalueResponses, subSolvers);
01165 
01166   else if( problemNameBase == "Schrodinger CI" )
01167     evalCIModel(inArgs, outArgs, eigenvalueResponses, subSolvers);
01168 
01169   else if( problemNameBase == "Poisson Schrodinger CI" )
01170     evalPoissonCIModel(inArgs, outArgs, eigenvalueResponses, subSolvers);
01171 
01172   if(num_g > 0) {
01173     // update main solver's responses using sub-solver response values
01174     Teuchos::RCP<Epetra_Vector> g = outArgs.get_g(0); //only use *first* response vector
01175     Teuchos::RCP<Epetra_MultiVector> dgdp = Teuchos::null;
01176     
01177     if(num_p > 0 && !outArgs.supports(OUT_ARG_DgDp, 0, 0).none()) 
01178       dgdp = outArgs.get_DgDp(0,0).getMultiVector();
01179     
01180     int offset = 0;
01181     std::vector<Teuchos::RCP<QCAD::SolverResponseFn> >::const_iterator rit;
01182     
01183     for(rit = responseFns.begin(); rit != responseFns.end(); rit++) {
01184       (*rit)->fillSolverResponses( *g, dgdp, offset, subSolvers, paramFnVecs, bSupportDpDg, eigenvalueResponses);
01185       offset += (*rit)->getNumDoubles();
01186     }
01187     
01188     if(bVerbose) {
01189       *out << "BEGIN QCAD Solver Responses:" << std::endl;
01190       for(int i=0; i< g->MyLength(); i++)
01191   *out << "  Response " << i << " = " << (*g)[i] << std::endl;
01192       *out << "END QCAD Solver Responses" << std::endl;
01193       
01194       //Seems to be a problem with print and MPI calls...
01195       /*if(!outArgs.supports(OUT_ARG_DgDp, 0, 0).none()) {
01196        *out << "BEGIN QCAD Solver Sensitivities:" << std::endl;
01197        dgdp->Print(*out);
01198        *out << "END QCAD Solver Sensitivities" << std::endl;
01199        }*/
01200     }
01201   }
01202 }
01203 
01204 
01205 void 
01206 QCAD::Solver::evalPoissonSchrodingerModel(const InArgs& inArgs,
01207             const OutArgs& outArgs,
01208             std::vector<double>& eigenvalueResponses,
01209             std::map<std::string, SolverSubSolver>& subSolvers) const
01210 {
01211   Teuchos::RCP<Teuchos::FancyOStream> out(Teuchos::VerboseObjectBase::getDefaultOStream());
01212   Teuchos::RCP<Albany::EigendataStruct> eigenDataToPass = Teuchos::null;
01213 
01214   //bool bConverged = doPSLoop("damping", inArgs, subSolvers, eigenDataToPass, true);
01215   bool bConverged = doPSLoop("mix", inArgs, subSolvers, eigenDataToPass, true);
01216 
01217   eigenvalueResponses = *(eigenDataToPass->eigenvalueRe); // copy eigenvalues to member variable
01218   for(std::size_t i=0; i<eigenvalueResponses.size(); ++i) eigenvalueResponses[i] *= -1; //apply minus sign (b/c of eigenval convention)
01219 
01220   if(!bUseIntegratedPS) {
01221     if(bConverged) {
01222       // Create final observer to output evecs and Poisson solution
01223       Teuchos::RCP<Epetra_Vector> solnVec = subSolvers["Poisson"].responses_out->get_g(1); //get the *first* response vector (solution)
01224       Teuchos::RCP<MultiSolution_Observer> final_obs = 
01225   Teuchos::rcp(new QCAD::MultiSolution_Observer(subSolvers["Poisson"].app, mainAppParams)); 
01226       final_obs->observeSolution(*solnVec, "solution", eigenDataToPass, 0.0);
01227     }
01228   }
01229 
01230   else { 
01231 
01232     // Use integrated poisson-schrodinger to further converge the Poisson-Schrodinger system.  (Always
01233     // run Integrated solver, regardless of whether iterative solver has converged to it's specified tolerance.)
01234 
01235     if(bVerbose) *out << "QCAD Solve: Integrated Poisson-Schrodinger solve started." << std::endl; 
01236 
01237     // get combined S-P map -- utilize a dummy CoupledPoissonSchrodinger object to do this for us...
01238     const Teuchos::RCP<Teuchos::ParameterList>& ps_paramList = getSubSolverParams("PoissonSchrodinger");
01239     const Teuchos::RCP<QCAD::CoupledPoissonSchrodinger> ps_dummy = 
01240       Teuchos::rcp(new QCAD::CoupledPoissonSchrodinger( ps_paramList, solverComm, Teuchos::null));
01241 
01242     Teuchos::RCP<const Epetra_Map> combinedMap = ps_dummy->get_x_map();
01243 
01244     // build initial guess from poisson potential, eigenvectors, and eigenvalues
01245     Teuchos::RCP<Epetra_Vector> initial_guess = Teuchos::rcp(new Epetra_Vector(*combinedMap));
01246     Teuchos::RCP<Epetra_Vector> initial_poisson, initial_evals;
01247     Teuchos::RCP<Epetra_MultiVector> initial_schrodinger;
01248     ps_dummy->separateCombinedVector(initial_guess, initial_poisson, initial_schrodinger, initial_evals);
01249     
01250     Teuchos::RCP<Epetra_Vector> poisson_soln = subSolvers["Poisson"].responses_out->get_g(1); // get the solution vector 
01251     initial_poisson->Scale(1.0, *poisson_soln); // initial_poisson = poisson_soln
01252 
01253 
01254     // Exporter to non-overlapped data (eigenvectors in EigendataInfo are stored in overlapped distribution)
01255     Teuchos::RCP<Albany::AbstractDiscretization> disc = subSolvers["Poisson"].app->getDiscretization();
01256     Teuchos::RCP<const Epetra_Map> disc_map = disc->getMap();
01257     Teuchos::RCP<const Epetra_Map> disc_overlap_map = disc->getOverlapMap();
01258     Teuchos::RCP<Epetra_Export> overlap_exporter = Teuchos::rcp(new Epetra_Export(*disc_overlap_map, *disc_map));
01259 
01260     int nEigenvals = initial_schrodinger->NumVectors();
01261     for(int k=0; k < nEigenvals; k++) 
01262       (*initial_schrodinger)(k)->Export( *((*(eigenDataToPass->eigenvectorRe))(k)), *overlap_exporter, Insert);
01263 
01264     double ignored_norm;
01265     std::vector<int> myGlobalEls( initial_evals->MyLength() );
01266     initial_evals->Map().MyGlobalElements(&myGlobalEls[0]);
01267     for(std::size_t k=0; k < myGlobalEls.size(); k++) {
01268       (*initial_evals)[k] = (*(eigenDataToPass->eigenvalueRe))[ myGlobalEls[k] ];
01269       
01270       (*(eigenDataToPass->eigenvectorIm))(myGlobalEls[k])->Norm2(&ignored_norm);
01271       if(ignored_norm > 1e-6) {
01272   std::cout << "QCAD::Solver Warning: ignored imaginary part with norm = "
01273       << ignored_norm << std::endl;
01274       }
01275     }
01276     
01277     subSolvers[ "PoissonSchrodinger" ] = CreateSubSolver( getSubSolverParams("PoissonSchrodinger") , *solverComm, initial_guess);
01278     fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "PoissonSchrodinger" ], 1); //Fills (first) param vec with Poisson parameters
01279     QCAD::SolveModel(subSolvers["PoissonSchrodinger"]);
01280     
01281     //Pull eigenvalues out of solution into eigenvalueResponses
01282     Teuchos::RCP<Epetra_Vector> solutionVec = outArgs.get_g(1); // 2nd response vector == solution?? -- really should be *last* response vector
01283     Teuchos::RCP<Epetra_Vector> soln_poisson, soln_evals;
01284     Teuchos::RCP<Epetra_MultiVector> soln_schrodinger;
01285     ps_dummy->separateCombinedVector(solutionVec, soln_poisson, soln_schrodinger, soln_evals);
01286     
01287     Epetra_LocalMap local_eigenval_map(nEigenvals, 0, *solverComm);
01288     Epetra_Import eigenval_importer(local_eigenval_map, soln_evals->Map());
01289 
01290     Teuchos::RCP<Epetra_Vector> eigenvals =  Teuchos::rcp(new Epetra_Vector(local_eigenval_map));
01291     eigenvals->Import(*soln_evals, eigenval_importer, Insert);
01292     eigenvalueResponses = std::vector<double>(&(*eigenvals)[0], &(*eigenvals)[0] + nEigenvals);
01293     for(std::size_t i=0; i<eigenvalueResponses.size(); ++i) eigenvalueResponses[i] *= -1; //apply minus sign (b/c of eigenval convention)
01294   }
01295 
01296   // TODO: why is this here??  for iQCAD parsing??
01297   /*if(bConverged) {
01298     // LATER: perhaps run a separate Poisson solve (as above) but have it compute all the responses we want
01299     //  (and don't have it compute them in the in-loop call above).
01300       
01301 
01302     //Write parameters and responses of final Poisson solve
01303     // Don't worry about sensitivities yet - just output vectors
01304     
01305     const QCAD::SolverSubSolver& ss = subSolvers["Poisson"];
01306     int poisson_num_p = ss.params_in->Np();     // Number of *vectors* of parameters
01307     int poisson_num_g = ss.responses_out->Ng(); // Number of *vectors* of responses
01308     
01309     for (int i=0; i<poisson_num_p; i++)
01310       ss.params_in->get_p(i)->Print(*out << "\nPoisson Parameter vector " << i << ":\n");
01311     
01312     for (int i=0; i<poisson_num_g-1; i++) {
01313       Teuchos::RCP<Epetra_Vector> g = ss.responses_out->get_g(i);
01314       bool is_scalar = true;
01315       
01316       if (ss.app != Teuchos::null)
01317   is_scalar = ss.app->getResponse(i)->isScalarResponse();
01318       
01319       if (is_scalar) {
01320   g->Print(*out << "\nPoisson Response vector " << i << ":\n");
01321   *out << "\n";  //add blank line after vector is printed - needed for proper post-processing
01322   // see Main_Solve.cpp for how to print sensitivities here
01323       }
01324     }
01325     }*/
01326 }
01327 
01328 
01329 void 
01330 QCAD::Solver::evalCIModel(const InArgs& inArgs,
01331         const OutArgs& outArgs,
01332         std::vector<double>& eigenvalueResponses,
01333         std::map<std::string, SolverSubSolver>& subSolvers) const
01334 {
01335 #ifdef ALBANY_CI
01336   Teuchos::RCP<Teuchos::FancyOStream> out(Teuchos::VerboseObjectBase::getDefaultOStream());
01337 
01338   //state variables
01339   Albany::StateArrays* pStatesToPass = NULL;
01340   Albany::StateArrays* pStatesToLoop = NULL; 
01341   Teuchos::RCP<Albany::EigendataStruct> eigenDataToPass = Teuchos::null;
01342   Teuchos::RCP<Albany::EigendataStruct> eigenDataNull = Teuchos::null;
01343 
01344   //create sub solvers (no initial guesses)
01345   std::map<std::string, Teuchos::RCP<Teuchos::ParameterList> >::const_iterator itp;
01346   for(itp = subProblemAppParams.begin(); itp != subProblemAppParams.end(); ++itp) {
01347     const std::string& name = itp->first;
01348     const Teuchos::RCP<Teuchos::ParameterList>& param_list = itp->second;
01349     subSolvers[ name ] = CreateSubSolver( param_list , *solverComm);
01350   }
01351   // NOTE: this loop creates CoulombPoissonIm even if bRealEvecs is true, which is unnecessary.  Fix
01352   //       later to reduce memory consumption.
01353 
01354   
01355 
01356   //NOTE: Parameters are not supported in Schrodinger CI mode -- we don't take the QCAD::Solver parameteters
01357   //  and fill any of the sub-solver parameters.  If we did, this would be the place to do it.
01358 
01359   //Initialize CI solver
01360   int n1PperBlock = nEigenvectors;
01361   QCAD::CISolver ciSolver(n1PperBlock, solverComm, out);
01362   Teuchos::RCP<Teuchos::ParameterList> ciParams = ciSolver.getDefaultParameterList();
01363   ciParams->set("Num Excitations", nCIExcitations);
01364   ciParams->set("Subbasis Particles 0", nCIParticles);    
01365 
01366   if(bUseTotalSpinSymmetry) {  //default is just Sz-symmetry
01367     ciParams->set("Num Symmetries", 2);
01368     ciParams->set("Symmetry 0", "Sz");
01369     ciParams->set("Symmetry 1", "S2");
01370   }
01371 
01372   if(bVerbose) *out << "QCAD Solve: CI solve" << std::endl;
01373 
01374   // Schrodinger Solve -> eigenstates
01375   if(bVerbose) *out << "QCAD Solve: Schrodinger solve" << std::endl;
01376 
01377   QCAD::SolveModel(subSolvers["Schrodinger"], pStatesToLoop, pStatesToPass,
01378        eigenDataNull, eigenDataToPass);
01379 
01380   Teuchos::RCP<Epetra_Vector> rcp_nullvec = Teuchos::null;
01381 
01382   // Construct CI matrices:
01383   ciSolver.fill1Pmx(eigenDataToPass);
01384   if(!bRealEvecs) {
01385     ciSolver.fill2Pmx(eigenDataToPass, &subSolvers["CoulombPoisson"], 
01386           &subSolvers["CoulombPoissonIm"], rcp_nullvec, bRealEvecs, bVerbose);
01387   }
01388   else {
01389     ciSolver.fill2Pmx(eigenDataToPass, &subSolvers["CoulombPoisson"],
01390           NULL, rcp_nullvec, bRealEvecs, bVerbose);
01391   }
01392           
01393   //Now should have H1P and H2P - run CI:
01394   if(bVerbose) *out << "QCAD Solve: CI solve" << std::endl;
01395   
01396   Teuchos::RCP<AlbanyCI::Solution> soln;
01397   soln = ciSolver.Solve(ciParams);
01398   //*out << std::endl << "Solution:"; soln->print(out); //DEBUG
01399 
01400   std::vector<double> eigenvalues = soln->getEigenvalues();
01401   eigenvalueResponses = eigenvalues; // save CI eigenvalues in member variable for responses
01402   int nCIevals = std::min(eigenvalues.size(),(std::size_t)nEigenvectors); //only save as many CI eigenvectors as 1P eigenvectors (later let this be specified separately?)
01403     
01404   // Compute the total electron density for each CI eigenstate and put them
01405   //  into eigenDataToPass (as the eigenvector real parts)
01406   //  (just for good measure duplicate in re and im multivecs so they're the same size - probably unecessary)
01407   Teuchos::RCP<Epetra_MultiVector> mbStateDensities = ciSolver.ComputeStateDensities(eigenDataToPass, soln);
01408   eigenDataToPass->eigenvectorRe = mbStateDensities;
01409   eigenDataToPass->eigenvectorIm = mbStateDensities; 
01410 
01411   // put CI eigenvalues into eigenDataToPass
01412   eigenDataToPass->eigenvalueRe->resize(nCIevals);
01413   for(int k=0; k < nCIevals; k++)
01414     (*(eigenDataToPass->eigenvalueRe))[k] = eigenvalues[k];
01415 
01416   if(eigenDataToPass->eigenvalueIm != Teuchos::null) {
01417     eigenDataToPass->eigenvalueIm->resize(nCIevals);
01418     for(int k=0; k < nCIevals; k++)
01419       (*(eigenDataToPass->eigenvalueIm))[k] = 0.0; //evals are real
01420   }
01421 
01422   if(bVerbose) *out << "-----> CI solve finished." << std::endl;
01423 
01424   // Create final observer to output evecs (MB densities) and solution
01425   Teuchos::RCP<Epetra_Vector> solnVec = subSolvers["Schrodinger"].responses_out->get_g(1); //get the *first* response vector (solution)
01426   Teuchos::RCP<MultiSolution_Observer> final_obs = 
01427     Teuchos::rcp(new QCAD::MultiSolution_Observer(subSolvers["Schrodinger"].app, mainAppParams)); 
01428   final_obs->observeSolution(*solnVec, "ZeroSolution", eigenDataToPass, 0.0);
01429 
01430 #else
01431   
01432   TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
01433        "Albany must be built with ALBANY_CI enabled in order to perform CI solutions." << std::endl);
01434 
01435 #endif
01436 }
01437 
01438   
01439 
01440 void 
01441 QCAD::Solver::evalPoissonCIModel(const InArgs& inArgs,
01442          const OutArgs& outArgs,
01443          std::vector<double>& eigenvalueResponses,
01444          std::map<std::string, SolverSubSolver>& subSolvers) const
01445 {
01446 #ifdef ALBANY_CI
01447   Teuchos::RCP<Teuchos::FancyOStream> out(Teuchos::VerboseObjectBase::getDefaultOStream());
01448   Teuchos::RCP<Albany::EigendataStruct> eigenDataNull = Teuchos::null;
01449   Teuchos::RCP<Albany::EigendataStruct> eigenDataToPass = Teuchos::null;
01450 
01451   //Initialize CI solver
01452   int n1PperBlock = nEigenvectors;
01453   QCAD::CISolver ciSolver(n1PperBlock, solverComm, out);
01454   Teuchos::RCP<Teuchos::ParameterList> ciParams = ciSolver.getDefaultParameterList();
01455 
01456   if(bUseTotalSpinSymmetry) {  //default is just Sz-symmetry
01457     ciParams->set("Num Symmetries", 2);
01458     ciParams->set("Symmetry 0", "Sz");
01459     ciParams->set("Symmetry 1", "S2");
01460   }
01461 
01462   eigenvalueResponses.resize(0);
01463 
01464   //Steps: 
01465   // 1) converge Schrodinger-Poisson as in evalPoissonSchrodingerModel
01466   // 2) get the number electrons in the quantum regions
01467   // 3) loop with CI included
01468   bool bPoissonSchrodingerConverged = doPSLoop("mix", inArgs, subSolvers, eigenDataToPass, true);
01469   bool bCIConverged = false;
01470   std::size_t iter = 0;
01471 
01472   // Run the CI only if the P-S loop converged
01473   if(bPoissonSchrodingerConverged) {
01474 
01475     //Get the number of particles converged upon by the Poisson-Schrodinger loop to use at the number of particles for the CI
01476     Teuchos::RCP<Epetra_Vector> g = subSolvers["Poisson"].responses_out->get_g(0); //Get poisson solver responses
01477 
01478     double nParticlesInQR, deltaFactor;
01479     int nParticles, nExcitations;
01480     int totalQuantumElectronsResponseIndex = g->GlobalLength() - 6; // 6th from end, assuming response ordering (see above)
01481 
01482     nParticlesInQR = (*g)[totalQuantumElectronsResponseIndex];
01483     nParticles = std::max((int)round(nParticlesInQR), minCIParticles); 
01484     nParticles = std::min(nParticles, maxCIParticles); 
01485     if(nParticlesInQR > 0.001) // if there are very few electrons in the QR, the (delta-nochg) "delta" will be very small, so don't bother 
01486       deltaFactor = ((double)nParticles) / nParticlesInQR;  //if electrons in QR != what CI uses, need to adjust delta values
01487     else deltaFactor = 1.0;
01488 
01489     nExcitations = std::min(nParticles,4); //four excitations at most? HARDCODED
01490     ciParams->set("Num Excitations", nExcitations);
01491     ciParams->set("Subbasis Particles 0", nParticles);    
01492 
01493     if(nParticles <= 0) {
01494       if(bVerbose) *out << "QCAD Solve: SP Converged.  " << nParticlesInQR << " electrons in QR. "
01495       << "Not starting CI since there are no particles (electrons)." << std::endl;
01496     }
01497     else {
01498       if(bVerbose) *out << "QCAD Solve: SP Converged.  " << nParticlesInQR << " electrons in QR. Starting CI with " 
01499       << nParticles << " particles, " << nExcitations << " excitations" << std::endl;
01500     }
01501 
01502     Albany::StateArrays* pStatesToPass = NULL;
01503     Albany::StateArrays* pStatesToLoop = NULL; 
01504     Teuchos::RCP<Teuchos::ParameterList> eigList; //used to hold memory I think - maybe unneeded?
01505     double newShift;
01506 
01507     while(!bCIConverged) {
01508     
01509       if(nParticles > 0) {
01510 
01511   // Construct CI 1P-matrix:
01512 
01513   // Solve for "Delta" and "NoCharge" quantities (no initial guesses)
01514   // Note: any Poisson[x] parameters should get set in these Poisson-type solvers as well
01515   // Note2: assume all values of interest are returned in *first* response vector    
01516   Teuchos::RCP<Albany::EigendataStruct> eigenDataNull = Teuchos::null; // dummy
01517   
01518   // Poisson Solve without any source charge: this gives terms that are due to environment charges that
01519   //   occur due to boundary conditions (e.g. charge on surface of conductors due to DBCs) that we must subtract
01520   //   from terms below to get effect of *just* the quantum electron charges and their image charges. 
01521   if(bVerbose) *out << "QCAD Solve: No-charge Poisson computation" << std::endl;
01522   subSolvers[ "NoChargePoisson" ] = CreateSubSolver( getSubSolverParams("NoChargePoisson") , *solverComm);
01523   fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "NoChargePoisson" ]);
01524   QCAD::SolveModel(subSolvers[ "NoChargePoisson" ], eigenDataToPass, eigenDataNull);
01525   Teuchos::RCP<Epetra_Vector> g_noCharge = subSolvers[ "NoChargePoisson" ].responses_out->get_g(0); 
01526   subSolvers[ "NoChargePoisson" ].freeUp();
01527   
01528   // Delta Poisson Solve - get delta_ij in reponse vector
01529   if(bVerbose) *out << "QCAD Solve: Delta Poisson computation" << std::endl;
01530   subSolvers[ "DeltaPoisson" ] = CreateSubSolver( getSubSolverParams("DeltaPoisson") , *solverComm);
01531   fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "DeltaPoisson" ]);
01532   QCAD::SolveModel(subSolvers[ "DeltaPoisson" ], eigenDataToPass, eigenDataNull);
01533   Teuchos::RCP<Epetra_Vector> g_delta =  subSolvers[ "DeltaPoisson" ].responses_out->get_g(0);
01534   subSolvers[ "DeltaPoisson" ].freeUp();
01535   
01536   ciSolver.fill1Pmx(eigenDataToPass, g_noCharge, g_delta, deltaFactor, bRealEvecs, bVerbose);
01537 
01538   // Construct CI 2P-matrix:
01539   subSolvers[ "CoulombPoisson" ] = CreateSubSolver( getSubSolverParams("CoulombPoisson") , *solverComm);
01540   fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "CoulombPoisson" ]);
01541       
01542   if(!bRealEvecs) {
01543     subSolvers[ "CoulombPoissonIm" ] = CreateSubSolver( getSubSolverParams("CoulombPoissonIm") , *solverComm);
01544     fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "CoulombPoissonIm" ]);
01545     ciSolver.fill2Pmx(eigenDataToPass, &subSolvers["CoulombPoisson"], &subSolvers["CoulombPoissonIm"], 
01546           g_noCharge, bRealEvecs, bVerbose); 
01547     subSolvers[ "CoulombPoissonIm" ].freeUp();
01548   }
01549   else {
01550     ciSolver.fill2Pmx(eigenDataToPass, &subSolvers["CoulombPoisson"], NULL, 
01551           g_noCharge, bRealEvecs, bVerbose); 
01552   }
01553   subSolvers[ "CoulombPoisson" ].freeUp();
01554 
01555   //Now should have H1P and H2P - run CI:
01556   if(bVerbose) *out << "QCAD Solve: CI solve" << std::endl;
01557   
01558   Teuchos::RCP<AlbanyCI::Solution> soln;
01559   soln = ciSolver.Solve(ciParams);
01560   //*out << std::endl << "Solution:"; soln->print(out); //DEBUG
01561 
01562   if(bVerbose) *out << "-----> CI solve finished." << std::endl;
01563   
01564   std::vector<double> eigenvalues = soln->getEigenvalues();
01565   eigenvalueResponses = eigenvalues; // save CI eigenvalues in member variable for responses
01566   int nCIevals = std::min(eigenvalues.size(),(std::size_t)nEigenvectors); //only save as many CI eigenvectors as 1P eigenvectors (later let this be specified separately?)
01567   
01568   // Compute the total electron density for each CI eigenstate and put them
01569   //  into eigenDataToPass (as the eigenvector real parts)
01570   //  (just for good measure duplicate in re and im multivecs so they're the same size - probably unecessary)
01571   Teuchos::RCP<Epetra_MultiVector> mbStateDensities = ciSolver.ComputeStateDensities(eigenDataToPass, soln);
01572   eigenDataToPass->eigenvectorRe = mbStateDensities;
01573   eigenDataToPass->eigenvectorIm = mbStateDensities; 
01574   
01575   // put CI eigenvalues into eigenDataToPass
01576   eigenDataToPass->eigenvalueRe->resize(nCIevals);
01577   for(int k=0; k < nCIevals; k++)
01578     (*(eigenDataToPass->eigenvalueRe))[k] = eigenvalues[k];
01579   
01580   if(eigenDataToPass->eigenvalueIm != Teuchos::null) {
01581     eigenDataToPass->eigenvalueIm->resize(nCIevals);
01582     for(int k=0; k < nCIevals; k++)
01583       (*(eigenDataToPass->eigenvalueIm))[k] = 0.0; //evals are real
01584   }
01585        
01586       }
01587       else { 
01588   //don't run the CI if there are no particles; just 
01589   // zero out what would be the many body electron densities
01590   if(bVerbose) *out << "QCAD Solve: Skipping CI solve (no particles)" << std::endl;
01591   eigenDataToPass->eigenvalueRe->resize(0);
01592   if(eigenDataToPass->eigenvalueIm != Teuchos::null)
01593     eigenDataToPass->eigenvalueIm->resize(0);
01594       }
01595 
01596       // Poisson Solve which uses CI MB state density and eigenvalues to get quantum electron density
01597       //   Initialize with the solution from the last Poisson iteration
01598       Teuchos::RCP<Epetra_Vector> last_solnVec = subSolvers["Poisson"].responses_out->get_g(1); //get the *first* response vector (solution)
01599       subSolvers[ "CIPoisson" ] = CreateSubSolver( getSubSolverParams("CIPoisson") , *solverComm,  last_solnVec);
01600       fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "CIPoisson" ]);
01601       
01602       if(bVerbose) *out << "QCAD Solve: CI Poisson iteration " << iter << std::endl;
01603       QCAD::SolveModel(subSolvers["CIPoisson"], pStatesToPass, pStatesToLoop,
01604            eigenDataToPass, eigenDataNull);
01605 
01606       //TODO - convergence criterion for Poisson-CI Loop -- now don't loop at all, just converge right away
01607       bCIConverged = true;
01608 
01609       if(!bCIConverged) { //need to do a Schrodinger solve to update the eigenvectors based on the new potential
01610   if(eigensolverName == "LOCA") {
01611     newShift = QCAD::GetEigensolverShift(subSolvers["CIPoisson"], shiftPercentBelowMin); //TODO: does CIPoisson have this repsonse?
01612     QCAD::ResetEigensolverShift(subSolvers["Schrodinger"].model, newShift, eigList);
01613   }
01614 
01615   // Schrodinger Solve -> eigenstates
01616   if(bVerbose) *out << "QCAD Solve: Schrodinger iteration " << iter << std::endl;
01617   QCAD::SolveModel(subSolvers["Schrodinger"], pStatesToLoop, pStatesToPass,
01618        eigenDataNull, eigenDataToPass);
01619      
01620   // Save solution for predictor-corrector outer iterations -- use in Poisson-CI loop? check if CIPoisson use predictor-corrector?
01621   //QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Solution", tmpContainer);
01622   //QCAD::CopyContainerToState(tmpContainer, *pStatesToPass, "PS Previous Poisson Potential");
01623       }
01624 
01625     } // end of CI loop
01626   }
01627     
01628   if(bVerbose) {
01629     if(bCIConverged)
01630       *out << "QCAD Solve: Converged Poisson-CI solve loop after " << iter << " iterations." << std::endl;
01631     else
01632       *out << "QCAD Solve: Maximum iterations (" << maxIter << ") reached." << std::endl;
01633   }
01634 
01635   if(bCIConverged) {
01636 
01637     // LATER: perhaps run a separate Poisson CI solve (as above) but have it compute all the responses we want
01638     //  (and don't have it compute them in the in-loop call above).
01639 
01640     printResponses(subSolvers["CIPoisson"], "CIPoisson", out);
01641   
01642     // Create final observer to output evecs (MB densities) and Poisson solution
01643     Teuchos::RCP<Epetra_Vector> solnVec = subSolvers["CIPoisson"].responses_out->get_g(1); //get the *first* response vector (solution)
01644     Teuchos::RCP<MultiSolution_Observer> final_obs = 
01645       Teuchos::rcp(new QCAD::MultiSolution_Observer(subSolvers["CIPoisson"].app, mainAppParams)); 
01646     final_obs->observeSolution(*solnVec, "solution", eigenDataToPass, 0.0);
01647   }
01648 
01649 
01650   #else
01651   
01652   TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
01653        "Albany must be built with ALBANY_CI enabled in order to perform Poisson-CI iterative solutions." << std::endl);
01654 
01655   #endif
01656 }
01657 
01658 bool QCAD::Solver::doPSLoop(const std::string& mode, const InArgs& inArgs,
01659           std::map<std::string, SolverSubSolver>& subSolvers, 
01660           Teuchos::RCP<Albany::EigendataStruct>& eigenDataResult,
01661           bool bPrintNumOfQuantumElectrons) const
01662 {
01663   Teuchos::RCP<Teuchos::FancyOStream> out(Teuchos::VerboseObjectBase::getDefaultOStream());
01664 
01665   //state variables
01666   Albany::StateArrays* pStatesToPass = NULL;
01667   Albany::StateArrays* pStatesToLoop = NULL; 
01668   Teuchos::RCP<Albany::EigendataStruct> eigenDataNull = Teuchos::null;
01669   eigenDataResult = Teuchos::null;
01670 
01671   //Field Containers to store states used in Poisson-Schrodinger loop
01672   std::vector<Intrepid::FieldContainer<RealType> > acceptedSolution;
01673   std::vector<Intrepid::FieldContainer<RealType> > acceptedDensity;
01674   std::vector<Intrepid::FieldContainer<RealType> > trialSolution;
01675   std::vector<Intrepid::FieldContainer<RealType> > trialDensity;
01676   std::vector<Intrepid::FieldContainer<RealType> > mixDensity;
01677   std::vector<Intrepid::FieldContainer<RealType> > prevConductionBand;
01678 
01679   //Create Initial Poisson solver & fill its parameters
01680   subSolvers[ "InitPoisson" ] = CreateSubSolver( getSubSolverParams("InitPoisson") , *solverComm, saved_initial_guess);
01681   fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "InitPoisson" ], 1); //any Poisson[x] parameters get set in initial poisson simulation too
01682 
01683   if(bVerbose) *out << "QCAD Solve: Initial Poisson solve (no quantum region) " << std::endl;
01684   QCAD::SolveModel(subSolvers["InitPoisson"], pStatesToPass, pStatesToLoop);
01685   
01686   //Create Schrodinger solver & fill its parameters
01687   subSolvers[ "Schrodinger" ] = CreateSubSolver( getSubSolverParams("Schrodinger") , *solverComm); // no initial guess
01688   fillSingleSubSolverParams(inArgs, "Schrodinger", subSolvers[ "Schrodinger" ]);
01689   
01690   //Create Poisson solver & fill its parameters.  Initialize with the solution from the InitPoisson solver
01691   Teuchos::RCP<Epetra_Vector> initial_solnVec = subSolvers["InitPoisson"].responses_out->get_g(1); //get the *first* response vector (solution)
01692   subSolvers[ "Poisson" ] = CreateSubSolver( getSubSolverParams("Poisson") , *solverComm,  initial_solnVec);
01693   fillSingleSubSolverParams(inArgs, "Poisson", subSolvers[ "Poisson" ]);  
01694 
01695   if(bVerbose) *out << "QCAD Solve: Beginning Poisson-Schrodinger solve loop" << std::endl;
01696   bool bConverged = false; 
01697   std::size_t iter = 0;
01698   double newShift;
01699   double local_maxDiff, global_maxDiff, best_global_maxDiff = 1e100;
01700   std::string ssForShift = "InitPoisson"; //which sub-solver to extract eigensolver shift from
01701 
01702   bool stepAccepted;
01703   double stepSize = 1.0, minStep = 0.001; //hardcoded min step: LATER set this via parameters
01704   double damping = 0;
01705   int consecutiveAccepts = 0;
01706   const int MIN_ITER = 2;
01707     
01708   Teuchos::RCP<Teuchos::ParameterList> eigList; //used to hold memory I think - maybe unneeded?
01709 
01710   //save initial poisson quantities as first trial
01711   QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Electron Density", trialDensity);
01712   QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Solution", trialSolution);
01713   if(mode == "mix") QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Solution", acceptedSolution);
01714 
01715   while(!bConverged && iter < maxIter)
01716   {
01717     iter++;
01718     stepAccepted = false; //stepSize = 1.0;
01719     const double STEP_DIVISOR = 2.0;
01720 
01721     while(!stepAccepted) {
01722 
01723       // reset eigensolver shift for schrodinger solve
01724       if(eigensolverName == "LOCA") {
01725   newShift = QCAD::GetEigensolverShift(subSolvers[ssForShift], shiftPercentBelowMin);
01726   QCAD::ResetEigensolverShift(subSolvers["Schrodinger"].model, newShift, eigList);
01727       }
01728 
01729       if(mode == "damping" && iter > 1 && damping > 0) { //mix in damping * last_CB to current conduction band and divide by (1+damping)
01730   if(bVerbose) *out << "QCAD Solve: Damping of " << damping << " applied before Schrodinger iteration " << iter << std::endl;
01731   QCAD::AddContainerToState(prevConductionBand, *pStatesToLoop, "PS Conduction Band", damping, 1-damping);
01732       }
01733       
01734       // Schrodinger Solve -> eigenstates
01735       if(bVerbose) *out << "QCAD Solve: Schrodinger iteration " << iter << std::endl;
01736       QCAD::SolveModel(subSolvers["Schrodinger"], pStatesToLoop, pStatesToPass,
01737            eigenDataNull, eigenDataResult);
01738 
01739       // Inject last poisson solution into the states passed to the Poisson step (for use in predictor-corrector method)
01740       QCAD::CopyContainerToState(trialSolution, *pStatesToPass, "PS Previous Poisson Potential");
01741 
01742       if(mode == "damping") {
01743   // Save conduction band to be used for damping on the next schrodinger iteration
01744   QCAD::CopyStateToContainer(*pStatesToLoop, "PS Conduction Band", prevConductionBand);
01745       }
01746 
01747       //We're all done with the initial poisson part, so free it up (this should only free the solver, not the responses or params)
01748       if(iter == 1) subSolvers[ "InitPoisson" ].freeUp();  // free up memory
01749 
01750       // Poisson Solve (no mixing)
01751       if(bVerbose) *out << "QCAD Solve: Poisson iteration " << iter << std::endl;
01752       QCAD::SetPreviousDensityMixing(subSolvers["Poisson"].params_in, 0.0);
01753       QCAD::SolveModel(subSolvers["Poisson"], pStatesToPass, pStatesToLoop,
01754            eigenDataResult, eigenDataNull);
01755 
01756       if(bPrintNumOfQuantumElectrons) {
01757   //assume that a Field Value and then a Field Integral response that computes the total number of electrons in 
01758   // the quantum regions are the last "component" response functions comprising the aggregated response function
01759   // that fills response vector 0.  A Field Value response computes 5 doubles, and a Field Integral response
01760   // computes 1, so the value of the field integral is the 6th element from the end.
01761   Teuchos::RCP<Epetra_Vector> g = subSolvers["Poisson"].responses_out->get_g(0); //Get poisson solver responses
01762   int totalQuantumElectronsResponseIndex = g->GlobalLength() - 6;
01763     
01764   if(bVerbose) *out << "QCAD Solve: Poisson iteration has " << (*g)[totalQuantumElectronsResponseIndex] 
01765           << " electrons in the quantum region" << std::endl;
01766       }
01767 
01768       eigenDataNull = Teuchos::null;
01769       ssForShift = "Poisson"; //from now on, get eigensolver shift from Poisson sub-solver
01770 
01771       //Get maximum difference (OLD)
01772       //local_maxDiff = QCAD::getMaxDifference(*pStatesToLoop, trialSolution, "PS Saved Solution");
01773       //solverComm->MaxAll(&local_maxDiff, &global_maxDiff, 1);
01774 
01775       //Get average difference (Norm2 / num of elements) TODO: condense into a function
01776       local_maxDiff = QCAD::getNorm2Difference(*pStatesToLoop, trialSolution, "PS Saved Solution");
01777       solverComm->SumAll(&local_maxDiff, &global_maxDiff, 1);
01778       int global_nEls, local_nEls = QCAD::getElementCount(trialSolution);
01779       solverComm->SumAll(&local_nEls, &global_nEls, 1);
01780       global_maxDiff /= global_nEls;
01781 
01782       //if we don't progress toward convergence
01783       if(best_global_maxDiff < global_maxDiff && iter > MIN_ITER) {
01784   
01785   if(mode == "damping" && damping < 0.9) { // (1-damping) => (1-damping)/2
01786     damping = (1+damping)/2.0;
01787     stepAccepted = true;
01788   }
01789 
01790   else if(mode == "mix" && stepSize/STEP_DIVISOR >= minStep) {
01791     //reduce step size -> new trial density based on mixing with last accepted step
01792     if(bVerbose) *out << "QCAD Solve: Step size " << stepSize << " rejected.  Diff=" 
01793           << global_maxDiff << " (tol=" << ps_converge_tol << ")" << std::endl;
01794 
01795     //if( fabs(stepSize-1.0) < 1e-6 ) //if this is the first step, mix trial density with mixDensity, since trial is 2nd after last accepted density
01796     //  QCAD::AddContainerToContainer(trialDensity, mixDensity, stepSize, 1-stepSize);
01797 
01798     stepSize /= STEP_DIVISOR;
01799     stepAccepted = false;
01800   }
01801 
01802   else stepAccepted = true; //if we don't do anything, accept a step even if it makes the convergence worse
01803       }
01804       else stepAccepted = true; // we've made progress, so accept step
01805     
01806       if(stepAccepted) {
01807   if(bVerbose) *out << "QCAD Solve: Step size " << stepSize << " accepted. Diff=" 
01808         << global_maxDiff << " (tol=" << ps_converge_tol << ")" << std::endl;
01809 
01810   if(mode == "mix") {
01811     // Trial quantities are "good" --> save and use for mixing into next step
01812     CopyContainer(trialDensity, acceptedDensity);
01813     CopyContainer(trialSolution, acceptedSolution);
01814 
01815     // save the density one P-S iteration after the accepted density for mixing
01816     QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Electron Density", mixDensity);
01817 
01818     consecutiveAccepts++;
01819     if(consecutiveAccepts == 3 && stepSize+1e-6 < 1.0) stepSize *= STEP_DIVISOR;
01820   } 
01821       }
01822       else consecutiveAccepts = 0;
01823 
01824 
01825       if(mode == "mix" && stepSize+1e-6 < 1.0) {
01826 
01827     // Set mixing based on step size: new trial density = (1-2*stepSize)*acceptedDensity + 2*stepSize * ( trialDensity + currentDensity )/2.0
01828     //  (factors of 2 so that first mixing step (stepSize = 0.5) just gives average of initial trial density and it's resulting density)
01829     //QCAD::SetPreviousDensityMixing(subSolvers["Poisson"].params_in, 1.0 - stepSize);
01830     //QCAD::SetPreviousDensityMixing(subSolvers["Poisson"].params_in, 1.0);
01831 
01832     //double debug_norm2 = QCAD::getNorm2(acceptedDensity, solverComm);
01833     //if(bVerbose) *out << "QCAD Solve: DEBUG accepted density norm = " << debug_norm2 << std::endl;
01834     QCAD::CopyContainerToState(acceptedDensity, *pStatesToPass, "PS Previous Electron Density");
01835     //QCAD::AddContainerToState(trialDensity, *pStatesToPass, "PS Previous Electron Density", stepSize/(1.-stepSize), (1.-2*stepSize)/(1.-stepSize) );
01836     //QCAD::SetPreviousDensityMixing(subSolvers["Poisson"].params_in, 1 - stepSize);
01837     QCAD::AddContainerToState(mixDensity, *pStatesToPass, "PS Previous Electron Density", stepSize, 1-stepSize); //mix last accepted density and the density following it
01838     QCAD::SetPreviousDensityMixing(subSolvers["Poisson"].params_in, 1.0);
01839   
01840     // Poisson Solve, mixing densities from last Schrodinger solve ("bad" evecs) and last accepted density (acceptedDensity)
01841     if(bVerbose) *out << "QCAD Solve: Poisson re-mix iteration " << iter << " (step = " << stepSize << ")" << std::endl;
01842     QCAD::SolveModel(subSolvers["Poisson"], pStatesToPass, pStatesToLoop,
01843          eigenDataResult, eigenDataNull);
01844       }
01845 
01846 
01847 
01848       //Quantities from last Poisson step become new trial
01849       QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Electron Density", trialDensity);
01850       QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Solution", trialSolution);
01851       //now run Schro then Poisson to check this new trial...
01852 
01853     } // end of while(!stepAccepted)
01854 
01855     bConverged = (global_maxDiff < ps_converge_tol*(1.0-damping));
01856     if(global_maxDiff < best_global_maxDiff && iter > MIN_ITER) best_global_maxDiff = global_maxDiff;
01857   } 
01858 
01859   // Done with iterative P-S loop
01860   if(bVerbose) {
01861     if(bConverged)
01862       *out << "QCAD Solve: Converged Poisson-Schrodinger solve loop after " << iter << " iterations." << std::endl;
01863     else
01864       *out << "QCAD Solve: Maximum iterations (" << maxIter << ") reached." << std::endl;
01865   }
01866 
01867   return bConverged;
01868 
01869   /*OLD: mix **potential (CB) ** -- was easier than mixing density...
01870   while(!bConverged && iter < maxIter)
01871   {
01872     iter++;
01873 
01874     //A step has been accepted.  Save the conduction band and solution:
01875       // Save conduction band to be used to "mix down" the next step
01876     QCAD::CopyStateToContainer(*pStatesToLoop, "PS Conduction Band", savedConductionBand);
01877 
01878       // Save solution for predictor-corrector outer iterations      
01879     QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Solution", savedSolution);
01880 
01881     stepAccepted = false; stepSize = 1.0;
01882     while(!stepAccepted) {
01883 
01884       // Compute trial conduction band based on that last accepted conduction band (from last accepted Poisson step), which 
01885       //   is in savedConductionBand container, and the current "trial" conduction band (depends on step size)
01886       if(fabs(stepSize-1.0) > 1e-8) { //if this isn't the first (unity) step, mix with the last accepted conduction band
01887   if(bVerbose) *out << "QCAD Solve: Stepsize of " << stepSize << " applied before Schrodinger iteration " << iter << std::endl;
01888   //QCAD::CopyContainerToState(unityStepTrialConductionBand, *pStatesToLoop, "PS Conduction Band");
01889   //QCAD::AddContainerToState(savedConductionBand, *pStatesToLoop, "PS Conduction Band", 1-stepSize, stepSize);
01890   QCAD::AddContainerToState(savedConductionBand, *pStatesToLoop, "PS Conduction Band", 0.5, 0.5);
01891       }
01892 
01893       // Save the conduction band that will be used by the schrodinger step (for later comparison with what comes out of the poisson step)
01894       QCAD::CopyStateToContainer(*pStatesToLoop, "PS Conduction Band", trialConductionBand);
01895 
01896       // reset eigensolver shift for schrodinger solve
01897       if(eigensolverName == "LOCA") {
01898   newShift = QCAD::GetEigensolverShift(subSolvers[ssForShift], shiftPercentBelowMin);
01899   QCAD::ResetEigensolverShift(subSolvers["Schrodinger"].model, newShift, eigList);
01900       }
01901 
01902       // Schrodinger Solve -> eigenstates
01903       if(bVerbose) *out << "QCAD Solve: Schrodinger iteration " << iter << std::endl;
01904       QCAD::SolveModel(subSolvers["Schrodinger"], pStatesToLoop, pStatesToPass,
01905            eigenDataNull, eigenDataToPass);
01906 
01907       // Inject saved solution (of last accepted poisson step) into states given to Poisson step (for use in predictor-corrector method)
01908       QCAD::CopyContainerToState(savedSolution, *pStatesToPass, "PS Previous Poisson Potential");
01909 
01910       // Poisson Solve
01911       if(bVerbose) *out << "QCAD Solve: Poisson iteration " << iter << std::endl;
01912       QCAD::SolveModel(subSolvers["Poisson"], pStatesToPass, pStatesToLoop,
01913            eigenDataToPass, eigenDataNull);
01914 
01915       // Housekeeping...
01916       eigenDataNull = Teuchos::null;
01917       ssForShift = "Poisson"; //from now on, get eigensolver shift from Poisson sub-solver
01918       if(fabs(stepSize-1.0) <= 1e-8) //record first trial step which uses stepSize == 1.0
01919   QCAD::CopyStateToContainer(*pStatesToLoop, "PS Conduction Band", unityStepTrialConductionBand);
01920 
01921       QCAD::CopyStateToContainer(*pStatesToLoop, "PS Saved Solution", savedSolution); //TRYING OUT... HERE
01922 
01923       // eval whether difference in potential (i.e. conduction band) has decreased
01924       local_maxDiff = QCAD::getMaxDifference(*pStatesToLoop, trialConductionBand, "PS Conduction Band");
01925       solverComm->MaxAll(&local_maxDiff, &global_maxDiff, 1);
01926       if(last_global_maxDiff > global_maxDiff || stepSize/2 < minStep) {
01927   stepAccepted = true;
01928   if(bVerbose) *out << "QCAD Solve: Step size " << stepSize << " accepted.  Potential (CB) max diff=" 
01929         << global_maxDiff << " (tol=" << ps_converge_tol << ")" << std::endl;
01930       }
01931       else {
01932   if(bVerbose) *out << "QCAD Solve: Step size " << stepSize << " rejected.  Potential (CB) max diff=" 
01933         << global_maxDiff << " (tol=" << ps_converge_tol << ")" << std::endl;
01934   stepSize /= 2.0;
01935       }
01936     }
01937 
01938     last_global_maxDiff = global_maxDiff;
01939     bConverged = (global_maxDiff < ps_converge_tol);
01940   } 
01941   */
01942 }
01943 
01944 
01945 void QCAD::Solver::setupParameterMapping(const Teuchos::ParameterList& list, const std::string& defaultSubSolver,
01946            const std::map<std::string, SolverSubSolverData>& subSolversData )
01947 {
01948   std::string s;
01949   std::vector<std::string> fnStrings;
01950 
01951   if( list.isType<int>("Number") ) {
01952     nParameters = list.get<int>("Number");
01953 
01954     for(std::size_t i=0; i<nParameters; i++) {
01955       s = list.get<std::string>(Albany::strint("Parameter",i));
01956       s = QCAD::string_remove_whitespace(s);
01957       fnStrings = QCAD::string_split(s,';',true);
01958     
01959       std::vector<Teuchos::RCP<QCAD::SolverParamFn> > fnVec;
01960       for(std::size_t j=0; j<fnStrings.size(); j++)
01961   fnVec.push_back( Teuchos::rcp(new QCAD::SolverParamFn(fnStrings[j], subSolversData)) );
01962 
01963       paramFnVecs.push_back( fnVec );
01964     }
01965   }
01966 
01967   else {  // When "Number" is not given, expose all the parameters of the default SubSolver
01968 
01969     if( (subSolversData.find(defaultSubSolver)->second).Np > 0 )
01970       nParameters = (subSolversData.find(defaultSubSolver)->second).pLength[0]; //just use param vec 0
01971     else nParameters = 0; //if the solver has no parameter vectors, then there are no parameters
01972 
01973     for(std::size_t i=0; i<nParameters; i++) {
01974       std::ostringstream ss;
01975       std::vector<Teuchos::RCP<QCAD::SolverParamFn> > fnVec;
01976 
01977       ss << defaultSubSolver << "[" << i << "]";  // "defaultSubSolver[i]"
01978       fnVec.push_back( Teuchos::rcp(new QCAD::SolverParamFn( ss.str(), subSolversData)) );
01979       paramFnVecs.push_back( fnVec );
01980     }
01981   }
01982 }
01983 
01984 
01985 void QCAD::Solver::setupResponseMapping(const Teuchos::ParameterList& list, const std::string& defaultSubSolver, int nEigenvalues,
01986           const std::map<std::string, SolverSubSolverData>& subSolversData )
01987 {
01988   Teuchos::RCP<QCAD::SolverResponseFn> fn;
01989 
01990   if( list.isType<int>("Number") ) {
01991     int number = list.get<int>("Number");
01992     std::string s;
01993 
01994     nResponseDoubles = 0;
01995     for(int i=0; i<number; i++) {
01996       s = list.get<std::string>(Albany::strint("Response",i));
01997       fn = Teuchos::rcp(new QCAD::SolverResponseFn(s, subSolversData, nEigenvalues));
01998       nResponseDoubles += fn->getNumDoubles();
01999       responseFns.push_back( fn );
02000     }
02001   }
02002 
02003   else {   // When "Number" is not given, echo all of the responses of the default SubSolver
02004     std::ostringstream ss;
02005     ss << defaultSubSolver << "[:]"; // all responses of defaultSubSolver
02006     fn = Teuchos::rcp(new QCAD::SolverResponseFn(ss.str(), subSolversData, nEigenvalues));
02007     nResponseDoubles = fn->getNumDoubles();
02008     responseFns.push_back( fn );
02009   }
02010 }
02011 
02012 void QCAD::Solver::fillSingleSubSolverParams(const InArgs& inArgs, const std::string& name,
02013                QCAD::SolverSubSolver& subSolver, int nLeaveOffEnd) const
02014 {
02015   if(num_p > 0) {   // or could use: (inArgs.Np() > 0)
02016     Teuchos::RCP<const Epetra_Vector> p = inArgs.get_p(0); //only use *first* param vector
02017     std::vector<Teuchos::RCP<QCAD::SolverParamFn> >::const_iterator pit;
02018     for(std::size_t i=0; i<nParameters; i++) {
02019       for(pit = paramFnVecs[i].begin(); pit != paramFnVecs[i].end()-nLeaveOffEnd; pit++) {
02020   (*pit)->fillSingleSubSolverParams((*p)[i], name, subSolver);
02021       }
02022     }
02023   }
02024 }
02025 
02026 
02027 const Teuchos::RCP<Teuchos::ParameterList>& QCAD::Solver::getSubSolverParams(const std::string& name) const
02028 {
02029   return subProblemAppParams.find(name)->second;
02030 }
02031 
02032 
02033 QCAD::SolverSubSolver 
02034 QCAD::Solver::CreateSubSolver(const Teuchos::RCP<Teuchos::ParameterList> appParams, const Epetra_Comm& comm,
02035             const Teuchos::RCP<const Epetra_Vector>& initial_guess) const
02036 {
02037   using Teuchos::RCP;
02038   using Teuchos::rcp;
02039 
02040   QCAD::SolverSubSolver ret; //value to return
02041 
02042   const Albany_MPI_Comm mpiComm = Albany::getMpiCommFromEpetraComm(comm);
02043 
02044   RCP<Teuchos::FancyOStream> out(Teuchos::VerboseObjectBase::getDefaultOStream());
02045   *out << "QCAD Solver creating solver from " << appParams->name() 
02046        << " parameter list" << std::endl;
02047  
02049   Albany::SolverFactory slvrfctry(appParams, mpiComm);
02050     
02052   RCP<Epetra_Comm> appComm = Albany::createEpetraCommFromMpiComm(mpiComm);
02053   ret.model = slvrfctry.createAndGetAlbanyApp(ret.app, appComm, appComm, initial_guess);
02054 
02055   ret.params_in = rcp(new EpetraExt::ModelEvaluator::InArgs);
02056   ret.responses_out = rcp(new EpetraExt::ModelEvaluator::OutArgs);  
02057 
02058   *(ret.params_in) = ret.model->createInArgs();
02059   *(ret.responses_out) = ret.model->createOutArgs();
02060   int ss_num_p = ret.params_in->Np();     // Number of *vectors* of parameters
02061   int ss_num_g = ret.responses_out->Ng(); // Number of *vectors* of responses
02062   RCP<Epetra_Vector> p1;
02063   RCP<Epetra_Vector> g1;
02064   
02065   if (ss_num_p > 0)
02066     p1 = rcp(new Epetra_Vector(*(ret.model->get_p_init(0))));
02067   if (ss_num_g > 1)
02068     g1 = rcp(new Epetra_Vector(*(ret.model->get_g_map(0))));
02069   RCP<Epetra_Vector> xfinal =
02070     rcp(new Epetra_Vector(*(ret.model->get_g_map(ss_num_g-1)),true) );
02071   
02072   // Sensitivity Analysis stuff
02073   bool supportsSensitivities = false;
02074   RCP<Epetra_MultiVector> dgdp;
02075   
02076   if (ss_num_p>0 && ss_num_g>1) {
02077     supportsSensitivities =
02078       !ret.responses_out->supports(EpetraExt::ModelEvaluator::OUT_ARG_DgDp, 0, 0).none();
02079     
02080     if (supportsSensitivities) {
02081       if (p1->GlobalLength() > 0)
02082         dgdp = rcp(new Epetra_MultiVector(g1->Map(), p1->GlobalLength() ));
02083       else
02084         supportsSensitivities = false;
02085     }
02086   }
02087   
02088   if (ss_num_p > 0)  ret.params_in->set_p(0,p1);
02089   if (ss_num_g > 1)  ret.responses_out->set_g(0,g1);
02090   ret.responses_out->set_g(ss_num_g-1,xfinal);
02091   
02092   if (supportsSensitivities) ret.responses_out->set_DgDp(0,0,dgdp);
02093   
02094   return ret;
02095 }
02096 
02097 QCAD::SolverSubSolverData
02098 QCAD::Solver::CreateSubSolverData(const QCAD::SolverSubSolver& sub) const
02099 {
02100   QCAD::SolverSubSolverData ret;
02101   if( sub.params_in->Np() > 0 && sub.responses_out->Ng() > 0 ) {
02102     ret.deriv_support = sub.model->createOutArgs().supports(OUT_ARG_DgDp, 0, 0);
02103   }
02104   else ret.deriv_support = EpetraExt::ModelEvaluator::DerivativeSupport();
02105 
02106   ret.Np = sub.params_in->Np();
02107   ret.pLength = std::vector<int>(ret.Np);
02108   for(int i=0; i<ret.Np; i++) {
02109     Teuchos::RCP<const Epetra_Vector> solver_p = sub.params_in->get_p(i);
02110     if(solver_p != Teuchos::null) ret.pLength[i] = solver_p->MyLength();  //uses local length (need to modify to work with distributed params)
02111     else ret.pLength[i] = 0;
02112   }
02113 
02114   ret.Ng = sub.responses_out->Ng();
02115   ret.gLength = std::vector<int>(ret.Ng);
02116   for(int i=0; i<ret.Ng; i++) {
02117     Teuchos::RCP<const Epetra_Vector> solver_g = sub.responses_out->get_g(i);
02118     if(solver_g != Teuchos::null) ret.gLength[i] = solver_g->MyLength(); //uses local length (need to modify to work with distributed responses)
02119     else ret.gLength[i] = 0;
02120   }
02121 
02122   if(ret.Np > 0) {
02123     Teuchos::RCP<const Epetra_Vector> p_init = 
02124       sub.model->get_p_init(0); //only first p vector used - in the future could make ret.p_init an array of Np vectors
02125     if(p_init != Teuchos::null) ret.p_init = Teuchos::rcp(new const Epetra_Vector(*p_init)); //copy
02126     else ret.p_init = Teuchos::null;
02127   }
02128   else ret.p_init = Teuchos::null;
02129 
02130   return ret;
02131 }
02132 
02133 
02134 void QCAD::Solver::printResponses(const SolverSubSolver& solver, const std::string& solverName, Teuchos::RCP<Teuchos::FancyOStream> out) const
02135 {
02136   int solver_num_p = solver.params_in->Np();     // Number of *vectors* of parameters
02137   int solver_num_g = solver.responses_out->Ng(); // Number of *vectors* of responses
02138   
02139   for (int i=0; i<solver_num_p; i++)
02140     solver.params_in->get_p(i)->Print(*out << "\n" << solverName << " Parameter vector " << i << ":\n");
02141   
02142   for (int i=0; i<solver_num_g-1; i++) {
02143     Teuchos::RCP<Epetra_Vector> g = solver.responses_out->get_g(i);
02144     bool is_scalar = true;
02145     
02146     if (solver.app != Teuchos::null)
02147       is_scalar = solver.app->getResponse(i)->isScalarResponse();
02148     
02149     if (is_scalar) {
02150       g->Print(*out << "\n" << solverName << " Response vector " << i << ":\n");
02151       *out << "\n";  //add blank line after vector is printed - needed for proper post-processing
02152     }
02153     
02154     // LATER: see Main_Solve.cpp for how to print sensitivities here
02155   }
02156 }
02157 
02158 
02159 Teuchos::RCP<const Teuchos::ParameterList>
02160 QCAD::Solver::getValidProblemParameters() const
02161 {
02162   Teuchos::RCP<Teuchos::ParameterList> validPL = Teuchos::createParameterList("ValidPoissonSchrodingerProblemParams");
02163 
02164   validPL->set<std::string>("Name", "", "String to designate Problem");
02165   validPL->set<int>("Phalanx Graph Visualization Detail", 0,
02166                     "Flag to select output of Phalanx Graph and level of detail");
02167   validPL->set<bool>("Verbose Output",false,"Enable detailed output mode");
02168   validPL->set<std::string>("Piro Defaults Filename","","An xml file containing a Piro parameterlist and its sublists to use as a default Piro list for this problem");
02169 
02170   validPL->set<double>("Length Unit In Meters",1e-6,"Length unit in meters");
02171   validPL->set<double>("Energy Unit In Electron Volts",1.0,"Energy (voltage) unit in electron volts (volts)");
02172   validPL->set<double>("Temperature",300,"Temperature in Kelvin");
02173   validPL->set<std::string>("MaterialDB Filename","materials.xml","Filename of material database xml file");
02174 
02175   validPL->set<std::string>("Schrodinger Eigensolver", "LOBPCG", "Name of eigensolver to use in schrodinger solve.  Can be LOCA or LOBPCG");
02176   validPL->set<bool>("Eigenvectors are Real",false,"Whether Schrodinger eigenvectors are known to have no imaginary part.");
02177 
02178   validPL->set<bool>("Use Integrated Poisson Schrodinger",true,"After converging iterative P-S, run integrated P-S solver");
02179   validPL->set<int>("Number of Eigenvalues",0,"The number of eigenvalue-eigenvector pairs");
02180   validPL->set<int>("Maximum PS Iterations",100,"The maximum number of P-S iterations");
02181   validPL->set<double>("Eigensolver Percent Shift Below Potential Min", 1.0, "Percentage of energy range of potential to subtract from the potential's minimum to obtain the eigensolver's shift");
02182   validPL->set<double>("Iterative PS Convergence Tolerance", 1e-6, "Convergence criterion for iterative PS solver (max potential difference across mesh)");
02183   validPL->set<double>("Iterative PS Fixed Occupation", -1.0, "Fixed quantum orbital occupation for iterative PS solver (non equilibrium condition)");
02184 
02185   validPL->set<int>("Minimum CI Particles", 0, "Poisson Schrodinger CI mode only: the minimum number of particles to use in the CI phase");
02186   validPL->set<int>("Maximum CI Particles", 0, "Poisson Schrodinger CI mode only: the maximum number of particles to use in the CI phase");
02187   validPL->set<int>("CI Particles", 0, "Schrodinger CI mode only: the number of particles to use in the CI phase");
02188   validPL->set<int>("CI Excitations", 0, "Schrodinger CI mode only: the number of excitations with which to truncate the CI phase");
02189   validPL->set<bool>("Use S2 Symmetry in CI",false,"Use total spin symmetry in the CI part of a problem");
02190 
02191   validPL->set<bool>("Include exchange-correlation potential",false,"Include exchange-correlation potential in poisson source term");
02192   validPL->set<bool>("Only solve schrodinger in quantum blocks",true,"Limit schrodinger solution to elements blocks labeled as quantum in the materials DB");
02193 
02194   validPL->set<bool>("Use predictor-corrector method",false,"Enable the predictor corrector algorithm for P-S iteration (otherwise use Picard)");
02195 
02196   validPL->sublist("Poisson Problem", false, "");
02197   validPL->sublist("Schrodinger Problem", false, "");
02198   validPL->sublist("Initial Condition", false, "");
02199 
02200   // Validate Parameters
02201   const int maxParameters = 100;
02202   Teuchos::ParameterList& validParamPL = validPL->sublist("Parameters", false, "");
02203   validParamPL.set<int>("Number", 0);
02204   for (int i=0; i<maxParameters; i++) {
02205     validParamPL.set<std::string>(Albany::strint("Parameter",i), "");
02206   }
02207 
02208   // Validate Responses
02209   const int maxResponses = 100;
02210   Teuchos::ParameterList& validResponsePL = validPL->sublist("Response Functions", false, "");
02211   validResponsePL.set<int>("Number of Response Vectors", 0);
02212   validResponsePL.set<int>("Number", 0);
02213   validResponsePL.set<int>("Equation", 0);
02214   for (int i=0; i<maxResponses; i++) {
02215     validResponsePL.set<std::string>(Albany::strint("Response",i), "");
02216     validResponsePL.sublist(Albany::strint("ResponseParams",i));
02217     validResponsePL.sublist(Albany::strint("Response Vector",i));
02218   }
02219 
02220   // Candidates for deprecation. Pertain to the solution rather than the problem definition.
02221   validPL->set<std::string>("Solution Method", "Steady", "Flag for Steady, Transient, or Continuation");
02222   
02223   return validPL;
02224 }
02225 
02226 
02227 
02228 
02230 //  QCAD::SolverParamFn  (parameter function object) ////////////////////////////////////////////////////////////////////
02232 
02233 // Function string can be of form:
02234 // "fn1(a,b)>fn2(c,d) ... >SolverName[X:Y]  OR
02235 // "SolverName[X:Y]"
02236 QCAD::SolverParamFn::SolverParamFn(const std::string& fnString, 
02237            const std::map<std::string, QCAD::SolverSubSolverData>& subSolversData)
02238 {
02239   std::vector<std::string> fnsAndTarget = QCAD::string_split(fnString,'>',true);
02240   std::vector<std::string>::const_iterator it;  
02241   std::map<std::string,std::string> target;
02242 
02243   if( fnsAndTarget.begin() != fnsAndTarget.end() ) {
02244     for(it=fnsAndTarget.begin(); it != fnsAndTarget.end()-1; it++) {
02245       filters.push_back( QCAD::string_parse_function( *it ) );
02246     }
02247     it = fnsAndTarget.end()-1;
02248     target = QCAD::string_parse_arrayref( *it );
02249     targetName = target["name"];
02250     
02251     targetIndices = QCAD::string_expand_compoundindex(target["index"], 0, 
02252                   (subSolversData.find(targetName)->second).pLength[0]);
02253   } 
02254 }
02255 
02256 
02257 void QCAD::SolverParamFn::fillSingleSubSolverParams(double parameterValue, const std::string& subSolverName,
02258                 QCAD::SolverSubSolver& subSolver) const
02259 {
02260   if(subSolverName != targetName) return;
02261 
02262   std::vector< std::vector<std::string> >::const_iterator fit;
02263   for(fit = filters.begin(); fit != filters.end(); ++fit) {
02264 
02265     //perform function operation
02266     std::string fnName = (*fit)[0];
02267     if( fnName == "scale" ) {
02268       TEUCHOS_TEST_FOR_EXCEPT( fit->size() != 1+1 ); // "scale" should have 1 parameter
02269       parameterValue *= atof( (*fit)[1].c_str() );
02270     }
02271     else TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
02272         "Unknown function " << (*fit)[0] << " for given type." << std::endl);
02273   }
02274 
02275   Teuchos::RCP<EpetraExt::ModelEvaluator::InArgs> inArgs = subSolver.params_in;
02276   Teuchos::RCP<const Epetra_Vector> p_ro = inArgs->get_p(0); //only use *first* param vector now
02277   Teuchos::RCP<Epetra_Vector> p = Teuchos::rcp( new Epetra_Vector( *p_ro ) );
02278   
02279   // copy parameterValue into sub-solver parameter vector where appropriate
02280   std::vector<int>::const_iterator it;
02281   for(it = targetIndices.begin(); it != targetIndices.end(); ++it)
02282     (*p)[ (*it) ] = parameterValue;
02283 
02284   inArgs->set_p(0, p);
02285 }
02286 
02287 
02288 
02289 void QCAD::SolverParamFn::fillSubSolverParams(double parameterValue, 
02290    const std::map<std::string, QCAD::SolverSubSolver>& subSolvers) const
02291 {
02292   std::vector< std::vector<std::string> >::const_iterator fit;
02293   for(fit = filters.begin(); fit != filters.end(); ++fit) {
02294 
02295     //perform function operation
02296     std::string fnName = (*fit)[0];
02297     if( fnName == "scale" ) {
02298       TEUCHOS_TEST_FOR_EXCEPT( fit->size() != 1+1 ); // "scale" should have 1 parameter
02299       parameterValue *= atof( (*fit)[1].c_str() );
02300     }
02301     else TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
02302         "Unknown function " << (*fit)[0] << " for given type." << std::endl);
02303   }
02304 
02305   Teuchos::RCP<EpetraExt::ModelEvaluator::InArgs> inArgs = (subSolvers.find(targetName)->second).params_in;
02306   Teuchos::RCP<const Epetra_Vector> p_ro = inArgs->get_p(0); //only use *first* param vector now
02307   Teuchos::RCP<Epetra_Vector> p = Teuchos::rcp( new Epetra_Vector( *p_ro ) );
02308   
02309   // copy parameterValue into sub-solver parameter vector where appropriate
02310   std::vector<int>::const_iterator it;
02311   for(it = targetIndices.begin(); it != targetIndices.end(); ++it)
02312     (*p)[ (*it) ] = parameterValue;
02313 
02314   inArgs->set_p(0, p);
02315 }
02316 
02317 double QCAD::SolverParamFn::
02318 getInitialParam(const std::map<std::string, QCAD::SolverSubSolverData>& subSolversData) const
02319 {
02320   //get first target parameter's initial value
02321   double initVal;
02322 
02323   Teuchos::RCP<const Epetra_Vector> p_init = 
02324     (subSolversData.find(targetName)->second).p_init; //only one p vector used
02325 
02326   TEUCHOS_TEST_FOR_EXCEPT(targetIndices.size() == 0);
02327   initVal = (*p_init)[ targetIndices[0] ];
02328 
02329   std::vector< std::vector<std::string> >::const_iterator fit;
02330   for(fit = filters.end(); fit != filters.begin(); --fit) {
02331 
02332     //perform INVERSE function operation to back out initial value
02333     std::string fnName = (*fit)[0];
02334     if( fnName == "scale" ) {
02335       TEUCHOS_TEST_FOR_EXCEPT( fit->size() != 1+1 ); // "scale" should have 1 parameter
02336       initVal /= atof( (*fit)[1].c_str() );
02337     }
02338     else TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
02339         "Unknown function " << (*fit)[0] << " for given type." << std::endl);
02340   }
02341 
02342   return initVal;
02343 }
02344 
02345 double QCAD::SolverParamFn::
02346 getFilterScaling() const
02347 {
02348   double scaling = 1.0;
02349 
02350   std::vector< std::vector<std::string> >::const_iterator fit;
02351   for(fit = filters.end(); fit != filters.begin(); --fit) {
02352 
02353     //perform INVERSE function operation to back out initial value
02354     std::string fnName = (*fit)[0];
02355     if( fnName == "scale" ) {
02356       TEUCHOS_TEST_FOR_EXCEPT( fit->size() != 1+1 ); // "scale" should have 1 parameter
02357       scaling *= atof( (*fit)[1].c_str() );
02358     }
02359   }
02360   return scaling;
02361 }
02362 
02363 
02364 
02366 //  QCAD::SolverResponseFn  (response function object) //////////////////////////////////////////////////////////////////
02368 
02369 // Function string can be of form:
02370 // "fn1(a,SolverName[X:Y],b)  OR
02371 // "SolverName[X:Y]"
02372 QCAD::SolverResponseFn::SolverResponseFn(const std::string& fnString, 
02373            const std::map<std::string, QCAD::SolverSubSolverData>& subSolversData,
02374            int nEigenvalues)
02375 {
02376   using std::string;
02377   std::vector<std::string> fnsAndTarget = QCAD::string_split(fnString,'>',true);
02378   std::vector<std::string>::const_iterator it;  
02379   std::map<std::string,std::string> arrayRef;
02380   ArrayRef ar;
02381   int nParams = 0;    
02382 
02383   //Case: no function name given
02384   if( fnString.find_first_of('(') == string::npos ) { 
02385     fnName = "nop";
02386     arrayRef = QCAD::string_parse_arrayref( fnString );
02387     ar.name = arrayRef["name"];
02388 
02389     if(ar.name == "Eigenvalue") {
02390       ar.indices = QCAD::string_expand_compoundindex(arrayRef["index"], 0, nEigenvalues);
02391     }
02392     else {
02393       ar.indices = QCAD::string_expand_compoundindex(arrayRef["index"], 0, (subSolversData.find(ar.name)->second).gLength[0]);
02394     }
02395     params.push_back(ar);
02396     nParams += ar.indices.size();
02397   }
02398 
02399   //Case: function name given
02400   else {
02401     std::vector<std::string> fnAndParams = QCAD::string_parse_function( fnString );
02402     fnName = fnAndParams[0];
02403 
02404     for(std::size_t i=1; i<fnAndParams.size(); i++) {
02405 
02406       //if contains [ treat as solver array reference, otherwise as a string param
02407       if( fnAndParams[i].find_first_of('[') == string::npos ) {
02408   ar.name = fnAndParams[i];  
02409   ar.indices.clear();
02410   params.push_back(ar);
02411   nParams += 1;
02412       }
02413       else {
02414   arrayRef = QCAD::string_parse_arrayref( fnAndParams[i] );
02415   ar.name = arrayRef["name"];
02416   ar.indices = QCAD::string_expand_compoundindex(arrayRef["index"], 0, (subSolversData.find(ar.name)->second).gLength[0]);
02417 
02418   params.push_back(ar);
02419   nParams += ar.indices.size();
02420       }      
02421     }
02422   }
02423 
02424   // validate: check number of params and set numDoubles
02425   if( fnName == "min" || fnName == "max") {
02426     TEUCHOS_TEST_FOR_EXCEPT(nParams != 2);
02427     numDoubles = 1;
02428   }
02429   else if( fnName == "dist") {
02430     TEUCHOS_TEST_FOR_EXCEPT( !(nParams == 2 || nParams == 4 || nParams == 6));
02431     numDoubles = 1;
02432   }
02433   else if( fnName == "scale") {
02434     TEUCHOS_TEST_FOR_EXCEPT(nParams != 2);
02435     numDoubles = 1;
02436   }
02437   else if( fnName == "divide") {
02438     TEUCHOS_TEST_FOR_EXCEPT(nParams != 2);
02439     numDoubles = 1;
02440   }
02441   else if( fnName == "nop") {
02442     numDoubles = nParams; 
02443   }
02444   else if( fnName == "DgDp") {  //params = subSolverName, pIndex, gIndex
02445     TEUCHOS_TEST_FOR_EXCEPT(nParams != 3);
02446     numDoubles = 1; 
02447   }
02448   else TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
02449      "Unknown function " << fnName << " for QCAD solver response." << std::endl);
02450 
02451 }
02452 
02453 
02454 void QCAD::SolverResponseFn::fillSolverResponses(Epetra_Vector& g, Teuchos::RCP<Epetra_MultiVector>& dgdp, int offset,
02455          const std::map<std::string, QCAD::SolverSubSolver>& subSolvers,
02456          const std::vector<std::vector<Teuchos::RCP<QCAD::SolverParamFn> > >& paramFnVecs,
02457          bool bSupportDpDg, const std::vector<double>& eigenvalueResponses) const
02458 {
02459   std::size_t nParameters = paramFnVecs.size();
02460 
02461   //Note: for now assume vectors use a local map (later fix using import to local map)
02462   TEUCHOS_TEST_FOR_EXCEPTION(g.DistributedGlobal(), Teuchos::Exceptions::InvalidParameter,
02463              "Error! Solvers's g response vector is distributed.  No implementation for this yet."
02464              << std::endl);
02465   if(dgdp != Teuchos::null)
02466     TEUCHOS_TEST_FOR_EXCEPTION(dgdp->DistributedGlobal(), Teuchos::Exceptions::InvalidParameter,
02467              "Error! Solvers's DgDp multivector is distributed.  No implementation for this yet."
02468              << std::endl);
02469 
02470   //Collect values and derivatives (wrt parameters) of function arguments
02471   std::vector< double > arg_vals; // argument values
02472   std::vector< std::vector<double> > arg_DgDps; // derivs of arguments (w/possibly mulitple parts) wrt params    
02473 
02474   // Note: arg_DgDps is transposed from Multivector dgdp objects:  
02475   //   arg_DgDps[ responseIndex ][ paramIndex ]  where dgdp_MultiVector(vectorIndex=paramIndex) [ rowIndex=responseIndex ] 
02476 
02477   std::vector< ArrayRef >::const_iterator arg_it;
02478   std::string dgdpName;
02479 
02480   //Note "params" == arguments to response function
02481   for(arg_it = params.begin(); arg_it != params.end(); ++arg_it) {
02482 
02483     if( fnName == "DgDp" && arg_it == params.begin() ) {  //special: string as first parameter to dgdp function
02484       dgdpName = arg_it->name; continue;
02485     }
02486 
02487     if( arg_it->indices.size() == 0 ){     //if no indices, "array name" is a double param value
02488 
02489       //single response with double value and zero deriviative  
02490       arg_vals.push_back( atof(arg_it->name.c_str()) ); 
02491 
02492       if(dgdp != Teuchos::null) {
02493   std::vector<double> dgdp_accum(nParameters,0.0);
02494   arg_DgDps.push_back( dgdp_accum );
02495       }
02496     }
02497     else {  // indices => this argument is of form subSolverName[possiblyCompoundIndex]
02498 
02499       std::string solverName = arg_it->name;
02500       Teuchos::RCP<Epetra_Vector> sub_g;
02501       Teuchos::RCP<Epetra_MultiVector> sub_dgdp;
02502 
02503 
02504       if(solverName == "Eigenvalue") {   //special case of "Eigenvalue[i]"
02505   std::vector<int>::const_iterator it;
02506   for(it = arg_it->indices.begin(); it != arg_it->indices.end(); ++it) {
02507     arg_vals.push_back( eigenvalueResponses[ *it ]); // append eigenvalue response value
02508     if(dgdp != Teuchos::null) {
02509       std::vector<double> dgdp_accum(nParameters,0.0); // no sensitivities wrt eigenvalues yet... (all zero)
02510       arg_DgDps.push_back( dgdp_accum );
02511     }
02512   }
02513       }
02514 
02515       else {
02516   const QCAD::SolverSubSolver& solver = subSolvers.find(solverName)->second;
02517   sub_g = solver.responses_out->get_g(0); // only use first g vector
02518   if(dgdp != Teuchos::null)
02519     sub_dgdp = solver.responses_out->get_DgDp(0,0).getMultiVector(); // only use first g & p vectors
02520 
02521   // for each index (i.e. double response value) 
02522   std::vector<int>::const_iterator it; int iIndx;
02523   for(it = arg_it->indices.begin(), iIndx=0; it != arg_it->indices.end(); ++it, ++iIndx) {
02524     int gIndex = *it;
02525     arg_vals.push_back( (*sub_g)[ gIndex ]); // append response value
02526   
02527     // append response derivative wrt to each parameter
02528     if(dgdp != Teuchos::null) {
02529       std::vector<double> dgdp_accum(nParameters,0.0);
02530       for(std::size_t i=0; i<nParameters; i++) {
02531         const std::vector<Teuchos::RCP<QCAD::SolverParamFn> >& paramFnVec = paramFnVecs[i];
02532         for(std::size_t j=0; j<paramFnVec.size(); j++) {
02533     const QCAD::SolverParamFn& paramFn = *(paramFnVec[j]);
02534     
02535     std::string paramTargetName = paramFn.getTargetName();
02536     const std::vector<int>& paramTargetIndices = paramFn.getTargetIndices();
02537     double scaling = paramFn.getFilterScaling(); // Later: something more general?
02538     
02539     if(paramTargetName != solverName) continue; 
02540     
02541     //for each index of the jth element of the ith parameter
02542     std::vector<int>::const_iterator vit;
02543     for(vit = paramTargetIndices.begin(); vit != paramTargetIndices.end(); ++vit)
02544       dgdp_accum[ i ] += (*((*sub_dgdp)( *vit )))[ gIndex ] * scaling;
02545         }
02546       }
02547       arg_DgDps.push_back( dgdp_accum );
02548     }
02549   }
02550       }
02551     }
02552   } //end of loop over arguments
02553 
02554   // Loop (Pseudocode)
02555   //for(i=0; i<nParameters; i++) {
02556   //  dgdp_vecs[iArgument][ i ] = targetSubSolver.DgDp(0,0)[argument_gIndex][ paramIndices[argumentTargetName][i] ];
02557   //}
02558 
02559 
02561   std::size_t nArgs = arg_vals.size();
02562 
02563   // minimum
02564   if( fnName == "min" ) {
02565     int winIndex = (arg_vals[0] <= arg_vals[1]) ? 0 : 1;      
02566     g[offset] = arg_vals[winIndex]; //set value
02567 
02568     if(dgdp != Teuchos::null) { //set derivative
02569       for(std::size_t i=0; i < arg_DgDps[winIndex].size(); i++) 
02570   dgdp->ReplaceGlobalValue(offset, i, arg_DgDps[winIndex][i]);
02571     }
02572   }
02573 
02574   // maximum
02575   else if(fnName == "max") {
02576     int winIndex = (arg_vals[0] >= arg_vals[1]) ? 0 : 1;      
02577     g[offset] = arg_vals[winIndex]; //set value
02578 
02579     if(dgdp != Teuchos::null) { //set derivative
02580       for(std::size_t i=0; i < arg_DgDps[winIndex].size(); i++) 
02581   dgdp->ReplaceGlobalValue(offset, i, arg_DgDps[winIndex][i]);
02582     }
02583   }
02584 
02585   // distance btwn 1D, 2D or 3D points (params ordered as (x1,y1,z1,x2,y2,z2) )
02586   else if( fnName == "dist") { 
02587     if(nArgs == 2) 
02588       g[offset] = abs(arg_vals[0]-arg_vals[1]);
02589     else if(nArgs == 4) 
02590       g[offset] = sqrt( pow(arg_vals[0]-arg_vals[2],2) + pow(arg_vals[1]-arg_vals[3],2));
02591     else if(nArgs == 6) 
02592       g[offset] = sqrt( pow(arg_vals[0]-arg_vals[3],2) + 
02593       pow(arg_vals[1]-arg_vals[4],2) + pow(arg_vals[2]-arg_vals[5],2) );
02594 
02595     if(dgdp != Teuchos::null) {
02596       TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
02597              "Error! No implementation of derivatives for distance function yet." << std::endl);
02598     }
02599   }
02600 
02601   // multiplicative scaling
02602   else if( fnName == "scale") {
02603     g[offset] = arg_vals[0] * arg_vals[1]; //set value
02604 
02605     if(dgdp != Teuchos::null) { //set derivative (muliplication rule)
02606       for(std::size_t i=0; i < nParameters; i++) { 
02607   // g = f1(p) * f2(p) ==> dgdpi = f1(p) * d(f2)/dpi + d(f1)/dpi * f2(p)
02608   dgdp->ReplaceGlobalValue(offset, i, arg_vals[0] * arg_DgDps[1][i] + arg_DgDps[0][i] * arg_vals[1]);
02609       }
02610     }
02611   }
02612 
02613   // multiplicative scaling
02614   else if( fnName == "divide") {
02615     g[offset] = arg_vals[0] / arg_vals[1]; //set value
02616 
02617     if(dgdp != Teuchos::null) { //set derivative (quotient rule)
02618       for(std::size_t i=0; i < nParameters; i++) { 
02619   // g = f1(p) / f2(p)  ==> dgdpi =  [ d(f1)/dpi * f2(p) - f1(p) * d(f2)/dpi ] / f2(p)^2
02620   dgdp->ReplaceGlobalValue(offset, i, (arg_DgDps[0][i] * arg_vals[1] - arg_vals[0] * arg_DgDps[1][i]) / pow(arg_vals[1],2) );
02621       }
02622     }
02623   }
02624 
02625   // no op (but can pass through multiple doubles)
02626   else if( fnName == "nop") {
02627     for(std::size_t i=0; i < nArgs; i++) {
02628       g[offset+i] = arg_vals[i]; //set value
02629 
02630       if(dgdp != Teuchos::null) { //set derivative
02631         for(std::size_t k=0; k < arg_DgDps[i].size(); k++) { //set derivative
02632 #ifndef EPETRA_NO_32BIT_GLOBAL_INDICES
02633           typedef int GlobalIndex;
02634 #else
02635           typedef long long GlobalIndex;
02636 #endif
02637           dgdp->ReplaceGlobalValue(static_cast<GlobalIndex>(offset+i), k, arg_DgDps[i][k]);
02638         }
02639       }
02640     }
02641   }
02642 
02643   // sensitivity element: DgDp( SolverName, pIndex, gIndex )
02644   else if( fnName == "DgDp") {
02645 
02646     if(bSupportDpDg) {
02647       int pIndex = (int)arg_vals[0], gIndex = (int)arg_vals[1];
02648       Teuchos::RCP<Epetra_MultiVector> sub_dgdp = 
02649   (subSolvers.find(dgdpName)->second).responses_out->get_DgDp(0,0).getMultiVector(); // only use first g & p vectors
02650 
02651     
02652       //Note: this assumes vectors use a local map so [pIndex] element exists on all procs (later fix using import to local map)
02653       TEUCHOS_TEST_FOR_EXCEPTION(sub_dgdp->DistributedGlobal(), Teuchos::Exceptions::InvalidParameter,
02654          "Error! sub-solvers's DgDp multivector is distributed.  No implementation for this yet."
02655          << std::endl);
02656 
02657       g[offset] = (*((*sub_dgdp)(pIndex)))[gIndex]; 
02658 
02659       if(dgdp != Teuchos::null) { //set QCAD::Solver derivative to zero (no derivatives of derivatives)
02660   for(std::size_t k=0; k < nParameters; k++)
02661     dgdp->ReplaceGlobalValue(offset,k, 0.0); 
02662       }
02663     }
02664     else g[offset] = 0.0; // just set response as zero if dgdp isn't supported
02665   }
02666  
02667   else TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
02668         "Unknown function " << fnName << " for QCAD solver response." << std::endl);
02669 }
02670 
02671 
02672 
02673 
02674 
02675 #ifdef ALBANY_CI
02676 
02678 //  QCAD::CISolver    ///////////////////////////////////////////////////////////////////////////////////////////////////
02680 
02681 QCAD::CISolver::CISolver(int n1PSpinlessStates, Teuchos::RCP<const Epetra_Comm> eComm,
02682        Teuchos::RCP<Teuchos::FancyOStream> outStream)
02683   :n1PperBlock(n1PSpinlessStates)
02684 {
02685   //Memory for CI Matrices
02686   //  - H1P matrix blocks (generate 2 blocks (up & down), each nEvecs x nEvecs)
02687   blockU = Teuchos::rcp(new AlbanyCI::Tensor<AlbanyCI::dcmplx>(n1PperBlock, n1PperBlock));
02688   blockD = Teuchos::rcp(new AlbanyCI::Tensor<AlbanyCI::dcmplx>(n1PperBlock, n1PperBlock));
02689   blocks1P.push_back(blockU); blocks1P.push_back(blockD);
02690 
02691   //  - H2P matrix blocks (generate 4 blocks, each nEvecs x nEvecs x nEvecs x nEvecs)
02692   blockUU = Teuchos::rcp(new AlbanyCI::Tensor<AlbanyCI::dcmplx>(n1PperBlock,n1PperBlock,n1PperBlock,n1PperBlock));
02693   blockUD = Teuchos::rcp(new AlbanyCI::Tensor<AlbanyCI::dcmplx>(n1PperBlock,n1PperBlock,n1PperBlock,n1PperBlock));
02694   blockDU = Teuchos::rcp(new AlbanyCI::Tensor<AlbanyCI::dcmplx>(n1PperBlock,n1PperBlock,n1PperBlock,n1PperBlock));
02695   blockDD = Teuchos::rcp(new AlbanyCI::Tensor<AlbanyCI::dcmplx>(n1PperBlock,n1PperBlock,n1PperBlock,n1PperBlock));
02696   blocks2P.push_back(blockUU);  blocks2P.push_back(blockUD);
02697   blocks2P.push_back(blockDU);  blocks2P.push_back(blockDD);
02698 
02699   // 1P basis - assume Sz-symmetry so N up sptl wfns, N dn sptl wfns
02700   basis1P = Teuchos::rcp(new AlbanyCI::SingleParticleBasis);
02701   basis1P->init(n1PperBlock /* nUpFns */ , n1PperBlock /* nDnFns */, true /*bMxsHaveBlkStructure*/ ); 
02702 
02703   // MPI communicator
02704   comm = Albany::createTeuchosCommFromMpiComm(Albany::getMpiCommFromEpetraComm(*eComm));
02705 
02706   //output stream
02707   out = outStream;
02708 
02709   //Unneeded - TODO: remove
02710   //std::vector<int> nParticlesInSubbases(1);
02711   //AlbanyCI::symmetrySet symmetries;
02712   //AlbanyCI::SymmetryFilters symmetryFilters;
02713 }
02714 
02715 
02716 Teuchos::RCP<Teuchos::ParameterList> 
02717 QCAD::CISolver::getDefaultParameterList() const
02718 {
02719   Teuchos::RCP<Teuchos::ParameterList> defaultPL = Teuchos::rcp( new Teuchos::ParameterList );
02720 
02721   //Set blockSize and numBlocks based on number of eigenvalues computed and the size of the problem
02722   int numEvals=n1PperBlock; //set the number of CI-eigenvectors equal to the number of spinless 1P eigenvectors by default
02723   int blockSize=n1PperBlock + 1; //better guess? - base on size of matrix?
02724   int numBlocks=8;  //better guess?
02725 
02726   int maxRestarts=100;
02727   double tol = 1e-8;
02728 
02729   //Defaults
02730   defaultPL->set("Num Excitations", 0);
02731   defaultPL->set("Num Subbases", 1);
02732   defaultPL->set("Subbasis Particles 0", 0);
02733   defaultPL->set("Num Symmetries", 1);
02734   defaultPL->set("Symmetry 0", "Sz");
02735   defaultPL->set("Num Symmetry Filters", 0);
02736 
02737   Teuchos::ParameterList& AnasaziList = defaultPL->sublist("Anasazi");
02738   std::string which("SR");
02739   AnasaziList.set( "Which", which );
02740   AnasaziList.set( "Num Eigenvalues", numEvals );
02741   AnasaziList.set( "Block Size", blockSize );
02742   AnasaziList.set( "Num Blocks", numBlocks );
02743   AnasaziList.set( "Maximum Restarts", maxRestarts );
02744   AnasaziList.set( "Convergence Tolerance", tol );
02745 
02746   return defaultPL;
02747 }
02748 
02749 
02750 // Construct CI matrices:
02751 // For N eigenvectors:
02752 //  1) a NxN matrix of the single particle hamiltonian: H1P. Derivation:
02753 //    H1P = diag(E) - delta, where delta_ij = int( [i(r)j(r)] F(r) dr)
02754 //    - no actual poisson solve needed, but need framework to integrate?
02755 //    - could we save a state containing weights and then integrate outside of NOX?
02756 //
02757 //  2) a matrix of all pair integrals.  Derivation:
02758 //    <12|1/r|34> = int( 1(r1) 2(r2) 1/(r1-r2) 3(r1) 4(r2) dr1 dr2)
02759 //                = int( 1(r1) 3(r1) [ int( 2(r2) 1/(r1-r2) 4(r2) dr2) ] dr1 )
02760 //                = int( 1(r1) 3(r1) [ soln of Poisson, rho(r1), with src = 2(r) 4(r) ] dr1 )
02761 //    - so use dummy poisson solve which has i(r)j(r) as only RHS term, for each pair <ij>,
02762 //       and as output of each Solve integrate wrt each other potential pair 1(r) 3(r)
02763 //       to generate all the elements.
02764 
02765 // What is F(r)?
02766 // evecs given are evecs of H = T + V + Vxc where V includes coulomb interaction with all charge
02767 //                            = T + V(src = other CI electrons) + V(src = classical charge) + Vxc
02768 // and we want matrix of H1P = T + V(src = classical charge)
02769 // So <i|H1P|j> = <i|H - V(src = other CI electrons) - Vxc|j> = E_i * Delta(i,j) - <i| V(src = other CI) + Vxc |j>
02770 //                = E_i * Delta(i,j) - delta_ij
02771 // and F(r) = [soln of Poisson, rho(r), with src = previous soln RHS restricted to quantum region] + Vxc(r)
02772 //     and the charge of just the quantum region is just determined by the eigenvectors/values, sum(state density * occupation)
02773 
02774 // Need to call Poisson in 
02775 // 1) "compute_delta" mode --> g[i*N+j] == delta_ij  (so N^2 responses)
02776 //    - set poisson source param specifying which reastricts RHS to quantum region
02777 //    - set poisson source param specifying quantum region charge == input from state (eigenstates & energies now - MB?)
02778 //    - solve with fixed charge in quantum region -- subregion of full coupled Poisson solve == source 
02779 //    - set responses to N^2 new "F(r)" responses which take "Weight State Name 1" and "2" params and integrate F(r) wrt them
02780 // 2) "compute_Coulomb" (i2,i4) mode --> g[i1*N+i3] == <i1,i2|1/r|i3,i4> (so N^2 responses)
02781 //    - set poisson source param which reastricts RHS to quantum region
02782 //    - set poisson source param specifying quantum region charge == product of eigenvectors & give i2,i4 indices
02783 //    - set responses to N^2 FieldIntegrals - ADD "Weight State Name 1" and "2" to possible params -- i1,i3
02784 // in both modes keep dbc's as they are - just change RHS of poisson.
02785 
02786 
02787 // fill 1P matrix without using delta_ij contribution (see above) -- appropriate when the potential energy
02788 //  used in Schrodinger solve that gives eigenData1P is fixed and not considered to be in part due to the 
02789 //  CI electrons themselves.
02790 void QCAD::CISolver::fill1Pmx(const Teuchos::RCP<Albany::EigendataStruct>& eigenData1P)
02791 {
02792   const double IMAG_TOL = 1e-8; //tolerace for imaginary parts of eigenvalues
02793 
02794   // transfer responses to H1P matrix (2 blocks (up & down), each nEvecs x nEvecs)
02795   for(int i=0; i<n1PperBlock; i++) {
02796     if(eigenData1P->eigenvalueIm != Teuchos::null) 
02797       assert( fabs((*(eigenData1P->eigenvalueIm))[i]) < IMAG_TOL );
02798 
02799     blockU->el(i,i) = -(*(eigenData1P->eigenvalueRe))[i]; // minus (-) sign b/c of 
02800     blockD->el(i,i) = -(*(eigenData1P->eigenvalueRe))[i]; //  eigenvalue convention
02801     *out << "DEBUG CI 1P Block El (" <<i<<","<<i<<") = " << -(*(eigenData1P->eigenvalueRe))[i] << std::endl;
02802     
02803     for(int j=i+1; j<n1PperBlock; j++) {
02804       blockU->el(i,j) = 0; blockU->el(j,i) = 0;
02805       blockD->el(i,j) = 0; blockD->el(j,i) = 0;
02806     }
02807   }
02808               
02809   mx1P = Teuchos::rcp(new AlbanyCI::BlockTensor<AlbanyCI::dcmplx>(basis1P, blocks1P, 1));
02810   //*out << std::endl << "DEBUG CI mx1P:"; mx1P->print(out); //DEBUG
02811 }
02812 
02813 
02814 // fill 1P matrix using delta_ij contribution (see above)
02815 void QCAD::CISolver::fill1Pmx(const Teuchos::RCP<Albany::EigendataStruct>& eigenData1P,
02816             const Teuchos::RCP<Epetra_Vector>& g_noCharge,
02817             const Teuchos::RCP<Epetra_Vector>& g_delta,
02818             double deltaScale, bool bRealEvecs, bool bVerbose)
02819 {
02820   const double IMAG_TOL = 1e-8; //tolerace for imaginary parts of eigenvalues
02821 
02822   // transfer responses to H1P matrix (2 blocks (up & down), each nEvecs x nEvecs)
02823   int rIndx = 0; // offset to the responses corresponding to delta_ij values == 0 by construction
02824   int rIndxInc = bRealEvecs ? 1 : 2; //increment by 2 indices if there are (real,imag) pairs of responses
02825   for(int i=0; i < n1PperBlock; i++) {
02826     assert(rIndx < g_delta->MyLength()); //make sure g-vector is long enough
02827 
02828     if(eigenData1P->eigenvalueIm != Teuchos::null) 
02829       assert( fabs((*(eigenData1P->eigenvalueIm))[i]) < IMAG_TOL );
02830 
02831     double delta_re = -( (*g_delta)[rIndx] - (*g_noCharge)[rIndx] );                      //Minus sign used because we use electric potential
02832     double delta_im = bRealEvecs ? 0 : -( (*g_delta)[rIndx+1] - (*g_noCharge)[rIndx+1] ); // in delta calcs, and e- sees negated potential    
02833     delta_re *= deltaScale; delta_im *= deltaScale; //scale delta values due to P-S loop <-> CI charge mismatch
02834 
02835     blockU->el(i,i) = -(*(eigenData1P->eigenvalueRe))[i] - delta_re; // first minus (-) sign b/c of 
02836     blockD->el(i,i) = -(*(eigenData1P->eigenvalueRe))[i] - delta_re; //  eigenvalue convention
02837     *out << "DEBUG CI 1P Block El (" <<i<<","<<i<<") = " << blockU->el(i,i) << "[no imag] = " 
02838    << -(*(eigenData1P->eigenvalueRe))[i] << " - " << "(" << delta_re << " + i*" << delta_im << ")" << std::endl;
02839     rIndx += rIndxInc;
02840     
02841     for(int j=i+1; j < n1PperBlock; j++) {
02842       assert(rIndx < g_delta->MyLength()); //make sure g-vector is long enough
02843       delta_re = -((*g_delta)[rIndx] - (*g_noCharge)[rIndx]);
02844       delta_im = bRealEvecs ? 0 : -((*g_delta)[rIndx+1] - (*g_noCharge)[rIndx+1]);
02845       blockU->el(i,j) = -delta_re; blockU->el(j,i) = -delta_re;
02846       blockD->el(i,j) = -delta_re; blockD->el(j,i) = -delta_re;
02847       *out << "DEBUG CI 1P Block El (" <<i<<","<<j<<") = - (" << delta_re << " + i*" << delta_im << ") [no imag]" << std::endl;
02848       rIndx += rIndxInc;
02849     }
02850   }
02851             
02852   mx1P = Teuchos::rcp(new AlbanyCI::BlockTensor<AlbanyCI::dcmplx>(basis1P, blocks1P, 1));
02853   //*out << std::endl << "DEBUG CI mx1P:"; mx1P->print(out); //DEBUG
02854 }
02855 
02856 
02857 void QCAD::CISolver::fill2Pmx(Teuchos::RCP<Albany::EigendataStruct> eigenData1P,
02858             const SolverSubSolver* coulombSolver, 
02859             const SolverSubSolver* coulombSolver_ImPart,
02860             const Teuchos::RCP<Epetra_Vector>& g_noCharge,
02861             bool bRealEvecs, bool bVerbose)
02862 {
02863   Teuchos::RCP<Albany::EigendataStruct> eigenDataNull = Teuchos::null; // dummy
02864   Teuchos::RCP<Epetra_Vector> g_reSrc, g_imSrc;
02865 
02866   // fill in mx2P (4 blocks, each n1PperBlock x n1PperBlock x n1PperBlock x n1PperBlock )
02867   for(int i2=0; i2<n1PperBlock; i2++) {
02868     for(int i4=i2; i4<n1PperBlock; i4++) {
02869       
02870       // Coulomb Poisson Solve - get coulomb els in reponse vector
02871       if(bVerbose) *out << "QCAD Solve: Coulomb " << i2 << "," << i4 << " Poisson" << std::endl;
02872       SetCoulombParams( coulombSolver->params_in, i2,i4 ); 
02873       QCAD::SolveModel(*coulombSolver, eigenData1P, eigenDataNull);
02874       g_reSrc = coulombSolver->responses_out->get_g(0); //only use *first* response vector    
02875 
02876       *out << "DEBUG: g_reSrc vector:" << std::endl; //DEBUG
02877       for(int i=0; i< g_reSrc->MyLength(); i++) *out << "  g_reSrc[" << i << "] = " << (*g_reSrc)[i] << std::endl;        
02878         
02879 
02880       if(!bRealEvecs) {
02881   // Coulomb Poisson Solve - get imaginary coulomb els in reponse vector
02882   if(bVerbose) *out << "QCAD Solve: Imaginary Coulomb " << i2 << "," << i4 << " Poisson" << std::endl;
02883   SetCoulombParams( coulombSolver_ImPart->params_in, i2,i4 ); 
02884   QCAD::SolveModel(*coulombSolver_ImPart, eigenData1P, eigenDataNull);
02885   g_imSrc = coulombSolver_ImPart->responses_out->get_g(0); //only use *first* response vector    
02886         
02887   *out << "DEBUG: g_imSrc vector:" << std::endl; //DEBUG
02888   for(int i=0; i< g_imSrc->MyLength(); i++) *out << "  g_imSrc[" << i << "] = " << (*g_imSrc)[i] << std::endl;
02889       }
02890 
02891       if(g_noCharge != Teuchos::null) {
02892   *out << "DEBUG: g_noCharge vector:" << std::endl; //DEBUG
02893   for(int i=0; i< g_noCharge->MyLength(); i++) *out << "  g_noCharge[" << i << "] = " << (*g_noCharge)[i] << std::endl;
02894       }
02895 
02896       int rIndx = 0 ;  // offset to the responses corresponding to Coulomb_ij values == 0 by construction
02897 
02898       if(!bRealEvecs) {
02899   for(int i1=0; i1<n1PperBlock; i1++) {
02900     for(int i3=i1; i3<n1PperBlock; i3++) {
02901       assert(rIndx < g_reSrc->MyLength()); //make sure g-vector is long enough
02902       double c_reSrc_re = -(*g_reSrc)[rIndx];
02903       double c_reSrc_im = -(*g_reSrc)[rIndx+1]; // rIndx + 1 == imag part
02904       double c_imSrc_re = -(*g_imSrc)[rIndx];
02905       double c_imSrc_im = -(*g_imSrc)[rIndx+1]; // rIndx + 1 == imag part
02906       
02907       if(g_noCharge != Teuchos::null) { // subtract out no-charge contribution, if necessary (only in Poisson-CI)
02908         // For example, we want c_reSrc_re == -((*g_reSrc)[rIndx] - (*g_noCharge)[rIndx]); //NOTE overall minus sign --> += here
02909         c_reSrc_re += (*g_noCharge)[rIndx];  
02910         c_reSrc_im += (*g_noCharge)[rIndx+1]; // rIndx + 1 == imag part
02911         c_imSrc_re += (*g_noCharge)[rIndx];
02912         c_imSrc_im += (*g_noCharge)[rIndx+1]; // rIndx + 1 == imag part
02913       }
02914       
02915       //Coulomb integral of interest (see above)
02916       double c_re = c_reSrc_re - c_imSrc_im;
02917       double c_im = c_reSrc_im + c_imSrc_re;
02918       
02919       *out << "DEBUG CI 2P Block El (" <<i1<<","<<i2<<","<<i3<<","<<i4<<") = " << c_re << " + i*" << c_im << std::endl;
02920       
02921       // Only use REAL parts here since we don't have complex support yet
02922       //  (Tpetra doesn't work).  Use c_re + i*c_im or conjugate where necessary.
02923       blockUU->el(i1,i2,i3,i4) = c_re;
02924       blockUU->el(i3,i2,i1,i4) = c_re;
02925       blockUU->el(i1,i4,i3,i2) = c_re;
02926       blockUU->el(i3,i4,i1,i2) = c_re;
02927       
02928       blockUD->el(i1,i2,i3,i4) = c_re;
02929       blockUD->el(i3,i2,i1,i4) = c_re;
02930       blockUD->el(i1,i4,i3,i2) = c_re;
02931       blockUD->el(i3,i4,i1,i2) = c_re;
02932       
02933       blockDU->el(i1,i2,i3,i4) = c_re;
02934       blockDU->el(i3,i2,i1,i4) = c_re;
02935       blockDU->el(i1,i4,i3,i2) = c_re;
02936       blockDU->el(i3,i4,i1,i2) = c_re;
02937       
02938       blockDD->el(i1,i2,i3,i4) = c_re;
02939       blockDD->el(i3,i2,i1,i4) = c_re;
02940       blockDD->el(i1,i4,i3,i2) = c_re;
02941       blockDD->el(i3,i4,i1,i2) = c_re;
02942       
02943       rIndx += 2;
02944     }
02945   }
02946       }
02947       else { //for all-real eigenvectors
02948   for(int i1=0; i1<n1PperBlock; i1++) {
02949     for(int i3=i1; i3<n1PperBlock; i3++) {
02950       assert(rIndx < g_reSrc->MyLength()); //make sure g-vector is long enough
02951       double c_reSrc_re = -(*g_reSrc)[rIndx];
02952 
02953       if(g_noCharge != Teuchos::null) c_reSrc_re += (*g_noCharge)[rIndx];  
02954       
02955       //Coulomb integral of interest (see above)
02956       double c_re = c_reSrc_re;
02957       
02958       *out << "DEBUG CI 2P Block El (" <<i1<<","<<i2<<","<<i3<<","<<i4<<") = " << c_re  << std::endl;
02959       
02960       // Only use REAL parts here since we don't have complex support yet
02961       //  (Tpetra doesn't work).  Use c_re + i*c_im or conjugate where necessary.
02962       blockUU->el(i1,i2,i3,i4) = c_re;
02963       blockUU->el(i3,i2,i1,i4) = c_re;
02964       blockUU->el(i1,i4,i3,i2) = c_re;
02965       blockUU->el(i3,i4,i1,i2) = c_re;
02966       
02967       blockUD->el(i1,i2,i3,i4) = c_re;
02968       blockUD->el(i3,i2,i1,i4) = c_re;
02969       blockUD->el(i1,i4,i3,i2) = c_re;
02970       blockUD->el(i3,i4,i1,i2) = c_re;
02971       
02972       blockDU->el(i1,i2,i3,i4) = c_re;
02973       blockDU->el(i3,i2,i1,i4) = c_re;
02974       blockDU->el(i1,i4,i3,i2) = c_re;
02975       blockDU->el(i3,i4,i1,i2) = c_re;
02976       
02977       blockDD->el(i1,i2,i3,i4) = c_re;
02978       blockDD->el(i3,i2,i1,i4) = c_re;
02979       blockDD->el(i1,i4,i3,i2) = c_re;
02980       blockDD->el(i3,i4,i1,i2) = c_re;
02981       
02982       rIndx += 1; // assume no imaginary parts are computed in responses
02983     }
02984   }
02985       }
02986     }
02987   }
02988   
02989   mx2P = Teuchos::rcp(new AlbanyCI::BlockTensor<AlbanyCI::dcmplx>(basis1P, blocks2P, 2));
02990   //*out << std::endl << "DEBUG CI mx2P:"; mx2P->print(out); //DEBUG
02991 }
02992 
02993 
02994 Teuchos::RCP<AlbanyCI::Solution> 
02995 QCAD::CISolver::Solve(Teuchos::RCP<Teuchos::ParameterList> AlbanyCIList) const
02996 {
02997   AlbanyCI::Solver solver;
02998   return solver.solve(AlbanyCIList, mx1P, mx2P, comm, out); //Note: out cannot be null
02999 }
03000 
03001 
03002 Teuchos::RCP<Epetra_MultiVector>
03003 QCAD::CISolver::ComputeStateDensities(Teuchos::RCP<Albany::EigendataStruct> eigenData1P, Teuchos::RCP<AlbanyCI::Solution> soln)
03004 {
03005   std::vector<double> eigenvalues = soln->getEigenvalues();
03006   int nCIevals = eigenvalues.size();
03007   std::vector< std::vector< AlbanyCI::dcmplx > > mxPx;
03008   bool bComplex = (eigenData1P->eigenvectorIm != Teuchos::null);
03009 
03010   Teuchos::RCP<Epetra_MultiVector> mbStateDensities = 
03011     Teuchos::rcp( new Epetra_MultiVector(eigenData1P->eigenvectorRe->Map(), nCIevals, true )); //zero out
03012       
03013   for(int k=0; k < nCIevals; k++) {
03014     soln->getEigenvectorPxMatrix(k, mxPx); // mxPx = n1P x n1P matrix of coeffs of 1P products
03015       
03016     //Note that CI's n1P is twice the number of eigenvalues in Albany eigendata due to spin degeneracy
03017     // and we must sum over both up and down parts.  For instance, the i-th spatial 1P eigenvector that
03018     // was given to the CI gets turned into a spin-down and spin-up with CI 1P-indices i and i+n1PperBlock
03019     //  -- LATER: get these indices from soln instead of assuming that the CI orders all the ups before all the downs...
03020     if(bComplex) {
03021       for(int i=0; i < 2*n1PperBlock; i++) {
03022   const Epetra_Vector& vi_real = *((*(eigenData1P->eigenvectorRe))(i % n1PperBlock));
03023   const Epetra_Vector& vi_imag = *((*(eigenData1P->eigenvectorIm))(i % n1PperBlock));
03024   
03025   for(int j=0; j < 2*n1PperBlock; j++) {
03026     const Epetra_Vector& vj_real = *((*(eigenData1P->eigenvectorRe))(j % n1PperBlock));
03027     const Epetra_Vector& vj_imag = *((*(eigenData1P->eigenvectorIm))(j % n1PperBlock));
03028     
03029     //std::cout << "DEBUG: State " << k << ": coeff of evec"<<i<<" x evec"<<j<<" = " << mxPx[i][j] << std::endl;
03030     (*mbStateDensities)(k)->Multiply( mxPx[i][j], vi_real, vj_real, 1.0); // mbDen(k) += mxPx_ij * elwise(Vi_r * Vj_r)
03031     (*mbStateDensities)(k)->Multiply( mxPx[i][j], vi_imag, vj_imag, 1.0); // mbDen(k) += mxPx_ij * elwise(Vi_i * Vj_i)
03032   }
03033       }
03034     }
03035     else {
03036       for(int i=0; i < 2*n1PperBlock; i++) {
03037   const Epetra_Vector& vi_real = *((*(eigenData1P->eigenvectorRe))(i % n1PperBlock));
03038   
03039   for(int j=0; j < 2*n1PperBlock; j++) {
03040     const Epetra_Vector& vj_real = *((*(eigenData1P->eigenvectorRe))(j % n1PperBlock));
03041     (*mbStateDensities)(k)->Multiply( mxPx[i][j], vi_real, vj_real, 1.0); // mbDen(k) += mxPx_ij * elwise(Vi_r * Vj_r)
03042   }
03043       }
03044     }
03045   }
03046   return mbStateDensities;
03047 }
03048 
03049 
03050 void QCAD::CISolver::SetCoulombParams(const Teuchos::RCP<EpetraExt::ModelEvaluator::InArgs> inArgs, int i2, int i4) const
03051 {
03052   TEUCHOS_TEST_FOR_EXCEPTION( inArgs->Np() < 1, Teuchos::Exceptions::InvalidParameter, 
03053             "Cannot set coulomb parameters because there are no parameter vectors.");
03054   Teuchos::RCP<const Epetra_Vector> p_ro = inArgs->get_p(0); //only use *first* param vector now
03055   Teuchos::RCP<Epetra_Vector> p = Teuchos::rcp( new Epetra_Vector( *p_ro ) );
03056   
03057   // assume the last two parameters are i2 and i4 -- indices for the coulomb element to be computed
03058   std::size_t nParams = p->GlobalLength();
03059   (*p)[ nParams-2 ] = (double) i2;
03060   (*p)[ nParams-1 ] = (double) i4;
03061 
03062   inArgs->set_p(0, p);
03063 }
03064 
03065 #endif  // ALBANY_CI
03066 
03067 
03068 
03069 // Helper Functions
03070 
03071 void QCAD::SetPreviousDensityMixing(const Teuchos::RCP<EpetraExt::ModelEvaluator::InArgs> inArgs, double mixingFactor)
03072 {
03073   TEUCHOS_TEST_FOR_EXCEPTION( inArgs->Np() < 1, Teuchos::Exceptions::InvalidParameter, 
03074             "Cannot set previous density mixing because there are no parameter vectors.");
03075   Teuchos::RCP<const Epetra_Vector> p_ro = inArgs->get_p(0); //only use *first* param vector now
03076   Teuchos::RCP<Epetra_Vector> p = Teuchos::rcp( new Epetra_Vector( *p_ro ) );
03077   
03078   // assume the last parameter is the mixing factor
03079   std::size_t nParams = p->GlobalLength();
03080   (*p)[ nParams-1 ] = mixingFactor;
03081   inArgs->set_p(0, p);
03082 }
03083 
03084 void QCAD::SolveModel(const QCAD::SolverSubSolver& ss)
03085 {
03086   ss.model->evalModel( (*ss.params_in), (*ss.responses_out) );
03087 }
03088 
03089 void QCAD::SolveModel(const QCAD::SolverSubSolver& ss, 
03090     Albany::StateArrays*& pInitialStates, Albany::StateArrays*& pFinalStates)
03091 {
03092   if(pInitialStates != NULL) 
03093     ss.app->getStateMgr().importStateData( *pInitialStates );
03094 
03095   ss.model->evalModel( (*ss.params_in), (*ss.responses_out) );
03096 
03097   pFinalStates = &(ss.app->getStateMgr().getStateArrays());
03098 }
03099 
03100 void QCAD::SolveModel(const QCAD::SolverSubSolver& ss, 
03101     Teuchos::RCP<Albany::EigendataStruct>& pInitialEData, 
03102     Teuchos::RCP<Albany::EigendataStruct>& pFinalEData)
03103 {
03104   if(pInitialEData != Teuchos::null) 
03105     ss.app->getStateMgr().setEigenData(pInitialEData);
03106 
03107   ss.model->evalModel( (*ss.params_in), (*ss.responses_out) );
03108 
03109   pFinalEData = ss.app->getStateMgr().getEigenData();
03110 }
03111 
03112 
03113 void QCAD::SolveModel(const QCAD::SolverSubSolver& ss, 
03114     Albany::StateArrays*& pInitialStates, Albany::StateArrays*& pFinalStates,
03115     Teuchos::RCP<Albany::EigendataStruct>& pInitialEData, 
03116     Teuchos::RCP<Albany::EigendataStruct>& pFinalEData)
03117 {
03118   if(pInitialStates != NULL) 
03119     ss.app->getStateMgr().importStateData( *pInitialStates );
03120 
03121   if(pInitialEData != Teuchos::null) 
03122     ss.app->getStateMgr().setEigenData(pInitialEData);
03123 
03124   ss.model->evalModel( (*ss.params_in), (*ss.responses_out) );
03125 
03126   pFinalStates = &(ss.app->getStateMgr().getStateArrays());
03127   pFinalEData = ss.app->getStateMgr().getEigenData();
03128 }
03129 
03130 
03131 void QCAD::CopyStateToContainer(Albany::StateArrays& state_arrays,
03132         std::string stateNameToCopy,
03133         std::vector<Intrepid::FieldContainer<RealType> >& dest)
03134 {
03135   Albany::StateArrayVec& src = state_arrays.elemStateArrays;
03136   int numWorksets = src.size();
03137   std::vector<int> dims;
03138 
03139   //allocate destination container if necessary
03140   if(dest.size() != (unsigned int)numWorksets) {
03141     dest.resize(numWorksets);    
03142     for (int ws = 0; ws < numWorksets; ws++) {
03143       src[ws][stateNameToCopy].dimensions(dims);
03144       dest[ws].resize(dims);
03145     }
03146   }
03147 
03148   for (int ws = 0; ws < numWorksets; ws++)
03149   {
03150     src[ws][stateNameToCopy].dimensions(dims);
03151     TEUCHOS_TEST_FOR_EXCEPT( dims.size() != 2 );
03152     
03153     for(int cell=0; cell < dims[0]; cell++)
03154       for(int qp=0; qp < dims[1]; qp++)
03155         dest[ws](cell,qp) = src[ws][stateNameToCopy](cell,qp);
03156   }
03157 }
03158 
03159 
03160 //Note: state must be allocated already
03161 void QCAD::CopyContainerToState(std::vector<Intrepid::FieldContainer<RealType> >& src,
03162         Albany::StateArrays& state_arrays,
03163         std::string stateNameOfCopy)
03164 {
03165   int numWorksets = src.size();
03166   Albany::StateArrayVec& dest = state_arrays.elemStateArrays;
03167   std::vector<int> dims;
03168 
03169   for (int ws = 0; ws < numWorksets; ws++)
03170   {
03171     dest[ws][stateNameOfCopy].dimensions(dims);
03172     TEUCHOS_TEST_FOR_EXCEPT( dims.size() != 2 );
03173     
03174     for(int cell=0; cell < dims[0]; cell++) {
03175       for(int qp=0; qp < dims[1]; qp++) {
03176   TEUCHOS_TEST_FOR_EXCEPT( std::isnan(src[ws](cell,qp)) );
03177         dest[ws][stateNameOfCopy](cell,qp) = src[ws](cell,qp);
03178       }
03179     }
03180   }
03181 }
03182 
03183 
03184 void QCAD::CopyContainer(std::vector<Intrepid::FieldContainer<RealType> >& src,
03185        std::vector<Intrepid::FieldContainer<RealType> >& dest)
03186 {
03187   int numWorksets = src.size();
03188 
03189   if(dest.size() != (unsigned int)numWorksets)
03190     dest.resize(numWorksets);    
03191   
03192   for (int ws = 0; ws < numWorksets; ws++)
03193   {
03194     dest[ws] = src[ws]; //assignment operator in Intrepid::FieldContainer
03195   }
03196 }
03197 
03198 // dest = thisFactor * dest + srcFactor * src
03199 void QCAD::AddContainerToContainer(std::vector<Intrepid::FieldContainer<RealType> >& src,
03200            std::vector<Intrepid::FieldContainer<RealType> >& dest,
03201            double srcFactor, double thisFactor)
03202 {
03203   int numWorksets = src.size();
03204   std::vector<int> dims;
03205 
03206   for (int ws = 0; ws < numWorksets; ws++)
03207   {
03208     dest[ws].dimensions(dims);
03209     TEUCHOS_TEST_FOR_EXCEPT( dims.size() != 2 );
03210     
03211     for(int cell=0; cell < dims[0]; cell++) {
03212       for(int qp=0; qp < dims[1]; qp++) {
03213   TEUCHOS_TEST_FOR_EXCEPT( std::isnan(src[ws](cell,qp)) );
03214         dest[ws](cell,qp) = thisFactor * dest[ws](cell,qp) + srcFactor * src[ws](cell,qp);
03215       }
03216     }
03217   }
03218 }
03219 
03220 
03221 
03222 // dest[stateName] = thisFactor * dest[stateName] + srcFactor * src
03223 //  Note: state must be allocated already
03224 void QCAD::AddContainerToState(std::vector<Intrepid::FieldContainer<RealType> >& src,
03225        Albany::StateArrays& state_arrays,
03226        std::string stateName, double srcFactor, double thisFactor)
03227 {
03228   int numWorksets = src.size();
03229   Albany::StateArrayVec& dest = state_arrays.elemStateArrays;
03230   std::vector<int> dims;
03231 
03232   for (int ws = 0; ws < numWorksets; ws++)
03233   {
03234     dest[ws][stateName].dimensions(dims);
03235     TEUCHOS_TEST_FOR_EXCEPT( dims.size() != 2 );
03236     
03237     for(int cell=0; cell < dims[0]; cell++) {
03238       for(int qp=0; qp < dims[1]; qp++) {
03239   TEUCHOS_TEST_FOR_EXCEPT( std::isnan(src[ws](cell,qp)) );
03240         dest[ws][stateName](cell,qp) = thisFactor * dest[ws][stateName](cell,qp) + srcFactor * src[ws](cell,qp);
03241       }
03242     }
03243   }
03244 }
03245 
03246 
03247 
03248 
03249 //Note: assumes src and dest have allocated states of <stateNameToCopy>
03250 void QCAD::CopyState(Albany::StateArrays& state_arrays,
03251          Albany::StateArrays& dest_arrays,
03252          std::string stateNameToCopy)
03253 {
03254   Albany::StateArrayVec& src = state_arrays.elemStateArrays;
03255   Albany::StateArrayVec& dest = dest_arrays.elemStateArrays;
03256   int numWorksets = src.size();
03257   int totalSize;
03258 
03259   for (int ws = 0; ws < numWorksets; ws++)
03260   {
03261     totalSize = src[ws][stateNameToCopy].size();
03262     for(int i=0; i<totalSize; ++i)
03263       dest[ws][stateNameToCopy][i] = src[ws][stateNameToCopy][i];
03264   }
03265 }
03266 
03267 
03268 void QCAD::AddStateToState(Albany::StateArrays& state_arrays,
03269          std::string srcStateNameToAdd, 
03270          Albany::StateArrays& dest_arrays,
03271          std::string destStateNameToAddTo)
03272 {
03273   Albany::StateArrayVec& src = state_arrays.elemStateArrays;
03274   Albany::StateArrayVec& dest = dest_arrays.elemStateArrays;
03275   int totalSize, numWorksets = src.size();
03276   TEUCHOS_TEST_FOR_EXCEPT( numWorksets != (int)dest.size() );
03277 
03278   for (int ws = 0; ws < numWorksets; ws++)
03279   {
03280     totalSize = src[ws][srcStateNameToAdd].size();
03281     
03282     for(int i=0; i<totalSize; ++i)
03283       dest[ws][destStateNameToAddTo][i] += src[ws][srcStateNameToAdd][i];
03284   }
03285 }
03286 
03287 
03288 void QCAD::SubtractStateFromState(Albany::StateArrays& state_arrays, 
03289           std::string srcStateNameToSubtract,
03290           Albany::StateArrays& dest_arrays,
03291           std::string destStateNameToSubtractFrom)
03292 {
03293   Albany::StateArrayVec& src = state_arrays.elemStateArrays;
03294   Albany::StateArrayVec& dest = dest_arrays.elemStateArrays;
03295   int totalSize, numWorksets = src.size();
03296   TEUCHOS_TEST_FOR_EXCEPT( numWorksets != (int)dest.size() );
03297 
03298   for (int ws = 0; ws < numWorksets; ws++)
03299   {
03300     totalSize = src[ws][srcStateNameToSubtract].size();
03301     
03302     for(int i=0; i<totalSize; ++i)
03303       dest[ws][destStateNameToSubtractFrom][i] -= src[ws][srcStateNameToSubtract][i];
03304   }
03305 }
03306 
03307 double QCAD::getMaxDifference(Albany::StateArrays& state_arrays, 
03308           std::vector<Intrepid::FieldContainer<RealType> >& prevState,
03309           std::string stateName)
03310 {
03311   double maxDiff = 0.0;
03312   Albany::StateArrayVec& states = state_arrays.elemStateArrays;
03313   int numWorksets = states.size();
03314   std::vector<int> dims;
03315 
03316   TEUCHOS_TEST_FOR_EXCEPT( ! (numWorksets == (int)prevState.size()) );
03317 
03318   for (int ws = 0; ws < numWorksets; ws++)
03319   {
03320     states[ws][stateName].dimensions(dims);
03321     TEUCHOS_TEST_FOR_EXCEPT( dims.size() != 2 );
03322     
03323     for(int cell=0; cell < dims[0]; cell++) 
03324     {
03325       for(int qp=0; qp < dims[1]; qp++) 
03326       {
03327         // std::cout << "prevState = " << prevState[ws](cell,qp) << std::endl;
03328         // std::cout << "currState = " << states[ws][stateName](cell,qp) << std::endl;
03329         if( fabs( states[ws][stateName](cell,qp) - prevState[ws](cell,qp) ) > maxDiff ) 
03330     maxDiff = fabs( states[ws][stateName](cell,qp) - prevState[ws](cell,qp) );
03331       }
03332     }
03333   }
03334   return maxDiff;
03335 }
03336 
03337 
03338 double QCAD::getNorm2Difference(Albany::StateArrays& state_arrays, 
03339         std::vector<Intrepid::FieldContainer<RealType> >& prevState,
03340         std::string stateName)
03341 {
03342   double norm2 = 0.0;
03343   Albany::StateArrayVec& states = state_arrays.elemStateArrays;
03344   int numWorksets = states.size();
03345   std::vector<int> dims;
03346 
03347   TEUCHOS_TEST_FOR_EXCEPT( ! (numWorksets == (int)prevState.size()) );
03348 
03349   for (int ws = 0; ws < numWorksets; ws++)
03350   {
03351     states[ws][stateName].dimensions(dims);
03352     TEUCHOS_TEST_FOR_EXCEPT( dims.size() != 2 );
03353     
03354     for(int cell=0; cell < dims[0]; cell++) 
03355     {
03356       for(int qp=0; qp < dims[1]; qp++) 
03357       {
03358   norm2 += pow( states[ws][stateName](cell,qp) - prevState[ws](cell,qp), 2);
03359       }
03360     }
03361   }
03362   return norm2;
03363 }
03364 
03365 
03366 double QCAD::getNorm2(std::vector<Intrepid::FieldContainer<RealType> >& container, const Teuchos::RCP<const Epetra_Comm>& comm)
03367 {
03368   double norm2 = 0.0;
03369   int numWorksets = container.size();
03370   std::vector<int> dims;
03371 
03372   for (int ws = 0; ws < numWorksets; ws++)
03373   {
03374     container[ws].dimensions(dims);
03375     TEUCHOS_TEST_FOR_EXCEPT( dims.size() != 2 );
03376 
03377     for(int cell=0; cell < dims[0]; cell++) 
03378     {
03379       for(int qp=0; qp < dims[1]; qp++) 
03380       {
03381   norm2 += pow( container[ws](cell,qp), 2);
03382       }
03383     }
03384   }
03385 
03386   double global_norm2;
03387   comm->SumAll(&norm2, &global_norm2, 1);
03388   return global_norm2;
03389 }
03390 
03391 int QCAD::getElementCount(std::vector<Intrepid::FieldContainer<RealType> >& container)
03392 {
03393   int cnt = 0;
03394   int numWorksets = container.size();
03395 
03396   for (int ws = 0; ws < numWorksets; ws++)
03397   {
03398     cnt += container[ws].size();
03399   }
03400   return cnt;
03401 }
03402 
03403 
03404 void QCAD::ResetEigensolverShift(const Teuchos::RCP<EpetraExt::ModelEvaluator>& Solver, double newShift, 
03405          Teuchos::RCP<Teuchos::ParameterList>& eigList) 
03406 {
03407   Teuchos::RCP<Piro::Epetra::LOCASolver> pels = Teuchos::rcp_dynamic_cast<Piro::Epetra::LOCASolver>(Solver);
03408   TEUCHOS_TEST_FOR_EXCEPT(pels == Teuchos::null);
03409 
03410   Teuchos::RCP<LOCA::Stepper> stepper =  pels->getLOCAStepperNonConst();
03411   const Teuchos::ParameterList& oldEigList = stepper->getList()->sublist("LOCA").sublist(
03412                "Stepper").sublist("Eigensolver");
03413 
03414   eigList = Teuchos::rcp(new Teuchos::ParameterList(oldEigList));
03415   eigList->set("Shift",newShift);
03416 
03417   //cout << " OLD Eigensolver list  " << oldEigList << std::endl;
03418   //cout << " NEW Eigensolver list  " << *eigList << std::endl;
03419   std::cout << "QCAD Solver setting eigensolver shift = " 
03420       << std::setprecision(5) << newShift << std::endl;
03421 
03422   stepper->eigensolverReset(eigList);
03423 }
03424 
03425 
03426 double QCAD::GetEigensolverShift(const QCAD::SolverSubSolver& ss, double pcBelowMinPotential)
03427 {
03428   int Ng = ss.responses_out->Ng();
03429   TEUCHOS_TEST_FOR_EXCEPT( Ng <= 0 );
03430 
03431   Teuchos::RCP<Epetra_Vector> gVector = ss.responses_out->get_g(0);
03432 
03433   //assume Field Value response that computes the minimum is the last "component" response function
03434   // comprising the aggregated response function that fills response vector 0.  A Field Value response
03435   // computes 5 doubles, and the value we're after is the first, so we want the element 5 from the end.
03436   int minPotentialResponseIndex = gVector->GlobalLength() - 5;
03437   double minVal = (*gVector)[minPotentialResponseIndex];
03438 
03439   //set shift to be slightly (5% of range) below minimum value
03440   //double shift = -(minVal - 0.05*(maxVal-minVal)); //minus sign b/c negative eigenvalue convention
03441   
03442   //double shift = -minVal*1.1;  // 10% below minimum value
03443   double shift = -minVal*(1.0 + pcBelowMinPotential/100.0);
03444   return shift;
03445 }
03446   
03447 
03448 
03449 
03450 // String functions
03451 std::vector<std::string> QCAD::string_split(const std::string& s, char delim, bool bProtect)
03452 {
03453   int last = 0;
03454   int parenLevel=0, bracketLevel=0, braceLevel=0;
03455 
03456   std::vector<std::string> ret(1);
03457   for(std::size_t i=0; i<s.size(); i++) {
03458     if(s[i] == delim && parenLevel==0 && bracketLevel==0 && braceLevel==0) {
03459       ret.push_back(""); 
03460       last++; 
03461     }
03462     else ret[last] += s[i];
03463 
03464     if(bProtect) {
03465       if(     s[i] == '(') parenLevel++;
03466       else if(s[i] == ')') parenLevel--;
03467       else if(s[i] == '[') bracketLevel++;
03468       else if(s[i] == ']') bracketLevel--;
03469       else if(s[i] == '{') braceLevel++;
03470       else if(s[i] == '}') braceLevel--;
03471     }
03472   }
03473   return ret;
03474 }
03475 
03476 std::string QCAD::string_remove_whitespace(const std::string& s)
03477 {
03478   std::string ret;
03479   for(std::size_t i=0; i<s.size(); i++) {
03480     if(s[i] == ' ') continue;
03481     ret += s[i];
03482   }
03483   return ret;
03484 }
03485 
03486 //given s="MyFunction(arg1,arg2,arg3)", 
03487 // returns vector { "MyFunction", "arg1", "arg2", "arg3" }
03488 std::vector<std::string> QCAD::string_parse_function(const std::string& s)
03489 {
03490   std::vector<std::string> ret;
03491   std::string fnName, fnArgString;
03492   std::size_t firstOpenParen, lastCloseParen;
03493 
03494   firstOpenParen = s.find_first_of('(');
03495   lastCloseParen = s.find_last_of(')');
03496 
03497   if(firstOpenParen == std::string::npos || lastCloseParen == std::string::npos) {
03498     TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
03499            "Malformed function string: " << s << std::endl);
03500   }
03501 
03502   fnName = s.substr(0,firstOpenParen);
03503   fnArgString = s.substr(firstOpenParen+1,lastCloseParen-firstOpenParen-1);
03504 
03505   ret = QCAD::string_split(fnArgString,',',true);
03506   ret.insert(ret.begin(),fnName);  //place function name at beginning of vector returned
03507   return ret;
03508 }
03509 
03510 std::map<std::string,std::string> QCAD::string_parse_arrayref(const std::string& s)
03511 {
03512   std::map<std::string,std::string> ret;
03513   std::string arName, indexString;
03514   std::size_t firstOpenBracket, lastCloseBracket;
03515 
03516   firstOpenBracket = s.find_first_of('[');
03517   lastCloseBracket = s.find_last_of(']');
03518 
03519   if(firstOpenBracket == std::string::npos || lastCloseBracket == std::string::npos) {
03520     TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
03521            "Malformed array string: " << s << std::endl);
03522   }
03523 
03524   arName = s.substr(0,firstOpenBracket);
03525   indexString = s.substr(firstOpenBracket+1,lastCloseBracket-firstOpenBracket-1);
03526 
03527   ret["name"] = arName;
03528   ret["index"] = indexString;
03529   return ret;
03530 }
03531 
03532 std::vector<int> QCAD::string_expand_compoundindex(const std::string& indexStr, int min_index, int max_index)
03533 {
03534   std::vector<int> ret;
03535   std::vector<std::string> simpleRanges = QCAD::string_split(indexStr,',',true);
03536   std::vector<std::string>::const_iterator it;
03537 
03538   for(it = simpleRanges.begin(); it != simpleRanges.end(); it++) {
03539     std::vector<std::string> endpts = QCAD::string_split( (*it), ':', true);
03540     if(endpts.size() == 1)
03541       ret.push_back( atoi(endpts[0].c_str()) );
03542     else if(endpts.size() == 2) {
03543       int a=min_index ,b=max_index;
03544       if(endpts[0] != "") a = atoi(endpts[0].c_str());
03545       if(endpts[1] != "") b = atoi(endpts[1].c_str());
03546 
03547       TEUCHOS_TEST_FOR_EXCEPTION(a < min_index || a > max_index || b < min_index || b > max_index,
03548          Teuchos::Exceptions::InvalidParameter, "Index '"<< indexStr 
03549          << "' is out of bounds (min="<<min_index<<", max="<<max_index<<")" << std::endl);
03550 
03551       for(int i=a; i<b; i++) ret.push_back(i);
03552     }
03553     else TEUCHOS_TEST_FOR_EXCEPTION(true, Teuchos::Exceptions::InvalidParameter,
03554            "Malformed array index: " << indexStr << std::endl);
03555   }
03556   return ret;
03557 }
03558 

Generated on Wed Mar 26 2014 18:36:44 for Albany: a Trilinos-based PDE code by  doxygen 1.7.1