#include "ionosphere.h" #include "ionosphere_functions.h" #include "DateTime.h" /** * \file ionosphere.C * \brief The main program file. * * \mainpage * \author Slava Merkin (vgm at bu.edu) * \since 2005 * \date 24 August 2007 * * The MIX code's primary function is to provide the inner boundary * condition for global MHD models, but it serves in fact a much more * diverse purpose. It can utilize existing empirical models of * ionospheric conductance to obtain the electrostatic potential * solution (solver mode), or it can receive conductances from an * external ionosphere-thermosphere model and then compute the * potential (solver/coupler mode). In the future, it is planned that * this coupler will mediate communication between other geospace * component models as well, i.e. Rice Convection Model (RCM) and LFM * (Lyon-Fedder-Mobarry). The major design consideration in developing * this code was to adopt the loose coupling concept, * i.e. plug-and-play should be made as simple as possible. Two * software packages are utilized in order to achieve this goal. The * first is InterComm * developed by Alan Sussman's group at the University of Maryland at * College Park. InterComm provides means for data communication (no * file dumps!) between different executables which can be * parallelized and run on different systems. The other sofware tool * is Overture, an * object-oriented framework for solving partial differential * equations on overset grids developed by Bill Henshaw at LLNL. Both * packages make extensive use of A++/P++, * a set of C++ classes allowing vector array operations in a * serial/parallel environment. * * The MIX code is not so much an Overture code as it is an A++ * code. Overture's functionality is used in two places only: to * interpolate between grids and to obtain the elliptic solve for the * ionospheric potential. The caveat is that Overture's overset grid * technology, something that the software was designed for, is not * very useful when one needs to interpolate between entire grids (the * problem that we are dealing with). The problem that Overture is * solving is, in a way, much more sofisticated - to compute * interpolation boundaries. Fortunately, Overture does have simpler * interpolation functions, i.e. to interpolate from a grid onto a set * of unstructured points, and this is exactly what we are using. Any * interpolation sofware could be used for this purpose and we could * in principle use our own interpolation functions too. The second * place where we use Overture is where its use is more justified. The * application of Overture to obtain the ionospheric elliptic solution * makes this code very flexible and potentially gives nice * opportunities for interesting physical experiments: one can play * all kinds of games with different boundary conditions, with the * location of the low-latitude boundary, the grid resolution, etc. In * addition, Overture can interact with a number of sparse matrix * solvers (Yale, Harwell, SLAP, and PETSc) and a variety of matrix * inversion algorithms in combination with different preconditioners * can be used. An option unexplored so far, but a very attractive * one, is the use of multigrid solvers which are also implemented in * Overture. * * \image html structure.png * \image latex structure.eps "" width=\textwidth * * The picture above demonstrates the general structure of MIX and its * communication with other components. The box on the left represents * an unspecified global MHD model. The P++ interface in the MHD code * is not really a part of the MIX code (maybe it should be such) but * it is what actually talks to the MHD model. This interface * calculates the field-aligned current, the density and the sound * speed of plasma at the MHD inner boundary and sends these via * InterComm communication channel to the MIX code. After the * ionospheric potential solution is obtained, it is sent back to the * MHD interface which calculates plasma transverse velocity and * electric field and gives it back to the MHD code. The only * requirement to the MHD code so far is that its data be defined on a * logically spherical grid, but it is anticipated that an Overture * module will be developed to reduce this condition to an arbitrary * grid. The interface is a fully parallel P++ code, and thanks to the * InterComm beauty, the MIX code does not care about it. The * structure of the MIX code (box on the right) is similar. It has an * interface (actually, more than one - see the rest of the * documentation) which talks to an MHD or an ITM model (and maybe * something like RCM too), does all the data massaging, conversion of * physical variables, interpolation between different grids, etc. If * needed the ionospheric conductances may be calculated locally using * an empirical model (the conventional LFM * faxed-memo-kinda-AMIE-model or the Moen-Brekke, 1993 can be * used). Then all the data required are passed to the elliptic solver * which does the solve, hands the solution back to the interface, the * data required is extracted and is sent back to the components that * need it. Simple as that... Enjoy! * * This code development was supported by the Center for * Integrated Space Weather Model (CISM) funded by the National * Science Foundation STC program under agreement ATM-012950. */ /********************************************************************//** * \author Slava Merkin (vgm at bu.edu) * \since 2005 * * \param[in] argv[1] The name of the parameter file * * This is the main program. There is nothing that should be brought * out from the source code to the manual; the comments in the code * are self-explanatory. ************************************************************************/ int main(int argc, char *argv[]) { DateTime currentUT; real tilt; clock_t startTime; Overture::start(argc,argv); // initialize Overture // ===== Read MIX input parameters ======================================================== char* paramFileName = argv[1]; if ( !paramFileName ) { cerr << "Name of parameter file not entered. Usage: ionosphere " << endl; exit(1); } else{ cout << "Name of parameter file " << paramFileName << endl; } /* Read the parameter file and store parameter values in class */ Params params(paramFileName); // MIX-ITM should start talking at DateTime itmCouplingStart = params.itmCouplingStart; // MIX-ITM should talk every "itmCouplingDelta" seconds; double itmCouplingDelta = params.itmCouplingDelta; // FIXME: Should spit out cerr warning message about LFM-MIX dump cadence versus itmCouplingDelta. // ===== Setup MHD & ITM interfaces ======================================================= // Setup the interface to MHD & ITM codes MHD_Interface *mhd = NULL; ITM_Interface *itm = NULL; IM_Interface *im = NULL; int rank = 0; int ic_err; IC_XJD* xjd; if (params.use_intercomm){ // Initialize InterComm xjd = IC_Initialize(params.localName,rank,params.xjdName,&ic_err); std::cout << "Initialized with " << params.localName << " " << params.xjdName << "\n"; mhd = new MHD_IC_Interface(¶ms, xjd); im = new IM_IC_Interface(xjd); itm = new ITM_IC_Interface(¶ms, xjd); // We commit IC regions here because this function commits all // regions created previously. In our case, the MHD, // Ionosphere-Theremosphere-Mesosphere and Inner-Magnetosphere // interfaces create IC_regions that should be commited. IC_Commit_region(xjd,&ic_err); // Inner Magnetosphere must initialize after Commit_region & before main loop im->Initialize(); } else{ // Initialize file exchanges mhd = new MHD_FE_Interface(¶ms, xjd); itm = new ITM_FE_Interface(¶ms,xjd); cerr << "***** ERROR ***** RCM coupling requires the InterComm interface, but you have selected file exchange!" << endl; } // ===== Initialize MIX Grids ============================================================= /* Call the Solver constructor The parameters are defined in const.h. See * comments in that file for requirements */ Solver ionSolver(params.mixGrid.getNLAT(), params.mixGrid.getLATITUDE(), params.mixGrid.getSOLVER_LON_GRID_STEP() ); // Are we coupling to an ITM model? CompositeGrid smGlobe; realCompositeGridFunction smGlobeFunctions; if (itmCouplingStart.getYear() < 9998){ /* * These are created on 2007-15-08 in order to test the following: * When stuff is interpolated from the solver grid onto the ITM * grid the points at mid- and low latitudes (those in between the * southern and northern solver grids) are extrapolated from the * boundary. My idea was to create a grid (theta, phi) covering the * entire globe in SM coordinates and set these points to zero (see * function Solver::mapToSMGlobe called below). After testing this * possibility, I am now not sure whether to go this way, because * it may create sharp gradients at the boundary. Need to check * with Wenbin and Mike. Meanwhile, I'm going to do it this way * because it really simplifies the ITM interface (interpolation is * now done in spherical angular coordinates and there is no * division into hemisphere. In fact, I have removed ITM_Interface * interpolation functions based on the cartesian grids. */ smGlobe = ionSolver.generateSMGlobeGrid(); // realCompositeGridFunction psi(smGlobe), averageEnergy(smGlobe),numberFlux(smGlobe); // smGlobeFunctions are stored as different components: // 0: potential; 1: average energy; 2: number flux (see functions.h) smGlobeFunctions = realCompositeGridFunction(smGlobe,all,all,all,3); } // Construct Solver grid CompositeGrid sgrid = ionSolver.generateCartesian2DGrid(); // Get the second shell MHD ionospheric grid from the MHD Interface CompositeGrid cg = mhd->getSecondShellIonCartesianGrid(); realCompositeGridFunction secondShellMHDFunctions(cg,all,all,all,8); // Get the first shell MHD ionospheric grid from the MHD interface CompositeGrid cg1 = mhd->getFirstShellIonCartesianGrid(); realCompositeGridFunction firstShellMHDFunctions(cg1,all,all,all,2); realCompositeGridFunction solverFunctions(sgrid,all,all,all,18); solverFunctions.setName("Solver grid functions"); solverFunctions.setName("Potential North [V]",SOLVER_POTENTIAL_NORTH); // MIX: phi solverFunctions.setName("Potential South [V]",SOLVER_POTENTIAL_SOUTH); // phi solverFunctions.setName("FAC North [A/m^2]",SOLVER_FAC_NORTH); // ITM: J_w solverFunctions.setName("FAC South [A/m^2]",SOLVER_FAC_SOUTH); // J_w solverFunctions.setName("Sound speed North [cm/s]",SOLVER_SOUND_SPEED_NORTH); // MHD: c_s solverFunctions.setName("Sound speed South [cm/s]",SOLVER_SOUND_SPEED_SOUTH); // c_s solverFunctions.setName("Density North [g/cm^3]",SOLVER_DENSITY_NORTH); // MHD: rho solverFunctions.setName("Density South [g/cm^3]",SOLVER_DENSITY_SOUTH); // rho solverFunctions.setName("Pedersen conductance North [S]",SOLVER_SIGMAP_NORTH); // ITM: sigma_p solverFunctions.setName("Pedersen conductance South [S]",SOLVER_SIGMAP_SOUTH); // sigma_p solverFunctions.setName("Hall conductance North [S]",SOLVER_SIGMAH_NORTH); // ITM: sigma_h solverFunctions.setName("Hall conductance South [S]",SOLVER_SIGMAH_SOUTH); // sigma_h solverFunctions.setName("Average energy North [keV]",SOLVER_AVG_ENG_NORTH); // MIX: E solverFunctions.setName("Average energy South [keV]",SOLVER_AVG_ENG_SOUTH); // E solverFunctions.setName("Number flux North [1/cm^2 s]",SOLVER_NUM_FLUX_NORTH); // MIX: F solverFunctions.setName("Number flux South [1/cm^2 s]",SOLVER_NUM_FLUX_SOUTH); // F solverFunctions.setName("Neutral wind speed North [m/s]",SOLVER_WIND_NORTH); // solverFunctions.setName("Neutral wind speed South [m/s]",SOLVER_WIND_SOUTH); // // Constructor for the conductance model // If an ITM is used to calculate the conductances, then // all the constants are just ignored: all we need is the solver info Conductance sigma(&ionSolver,¶ms); // ===== Setup IO/Overture visualization ================================================== #ifdef DX_OUT IO DXHDF(¶ms); #else Ogshow show(params.outputFilePrefix,".",1); show.setFlushFrequency(params.flushFrequency); #endif // ===== MHD Interface Helpers ============================================================ unsigned long int mhdTimeStep; unsigned long int mhdExchangeCount=1; // LFM starts counting loops from 1, so MIX should too. bool isLastStep; // ===== ITM Interface helpers ============================================================ DateTime itmCouplingNext = itmCouplingStart; unsigned long int itmExchangeCount = 0; // How many MIX-ITM exchanges? // ===== IM Interface helpers ============================================================= int exchangeWithIm = 0; unsigned long int imExchangeCount = 0; // Timekeeping things: double t_mhd2MIX, t_FandE, t_conduct, t_solve, t_MIX2mhd, t_MIX2itm, t_MIX2im, t_IO; // ===== MAIN LOOP ======================================================================== // The loop will continue until KILL signal received from the MHD code while ( receiveMHDscalars(mhd, currentUT, tilt, mhdTimeStep, isLastStep, exchangeWithIm, imExchangeCount) ) { std::cout << "LFM-MIX exchange # " << mhdExchangeCount << "; " << "MHD UT: " << currentUT.getDateTimeString() << endl; t_mhd2MIX = ionosphere_MHD_to_MIX(mhd,secondShellMHDFunctions, solverFunctions, ionSolver); t_FandE = ionosphere_get_Flux_and_Energy(exchangeWithIm, im, sigma, solverFunctions, tilt); t_conduct = ionosphere_get_Conductances(params, mhdExchangeCount, itmExchangeCount, currentUT, itmCouplingNext, sigma, solverFunctions, DXHDF, tilt, itm); t_solve = ionosphere_compute_Potential(ionSolver, solverFunctions); t_MIX2mhd = ionosphere_MIX_to_MHD(mhd,ionSolver, solverFunctions, firstShellMHDFunctions,secondShellMHDFunctions); t_MIX2itm = ionosphere_MIX_to_ITM(params, isLastStep, itmExchangeCount, currentUT, itmCouplingNext, itm, ionSolver, solverFunctions, smGlobeFunctions, DXHDF, tilt); t_MIX2im = ionosphere_MIX_to_IM(exchangeWithIm, im, solverFunctions); startTime = clock(); #ifdef DX_OUT if ( ( (mhdExchangeCount-1) % params.flushFrequency ) == 0 ){ DXHDF.Write(tilt, currentUT, mhdTimeStep, solverFunctions); } #else show.startFrame(); char label[STRLEN]; int nsym=sprintf(label,"%s%08d\n","MHD time step: ",mhdTimeStep); show.saveComment(0,label); nsym=sprintf(label,"%04d//%02d//%02d %02d:%02d:%02f\n", currentUT.getYear(), currentUT.getMonth(), currentUT.getDay(), currentUT.getHours(),currentUT.getMinutes(),currentUT.getSeconds()); show.saveComment(1,label); show.saveSolution(solverFunctions); show.endFrame(); #endif // DX_OUT t_IO = (float) (clock()-startTime)/CLOCKS_PER_SEC; // Spit out timing information for this step: std::cout << "\t================================================================================\n" << "\tdt = " << (t_mhd2MIX + t_FandE + t_conduct + t_solve + t_MIX2mhd + t_MIX2itm + t_MIX2im + t_IO) << "; " << " (MHD->MIX interp: " << t_mhd2MIX << ", Flux & Energy: " << t_FandE << ", Conductances: " << t_conduct << ", MIX solve: " << t_solve << ", MIX->MHD: " << t_MIX2mhd << ", MIX->ITM: " << t_MIX2itm << ", MIX->IM: " << t_MIX2im << ", IO: " << t_IO << ")\n" << "\t================================================================================" << endl; mhdExchangeCount+=1; } // ===== MAIN LOOP ENDS HERE ============================================================== // ======================================================================================== delete itm; itm = NULL; delete mhd; mhd = NULL; delete im; im = NULL; if (params.use_intercomm){ IC_Finalize(xjd,&ic_err); } Overture::finish(); return 0; }