Program Listing for File main.cpp

Return to documentation for file (main.cpp)

// ***********************************************************************************
// Idefix MHD astrophysical code
// Copyright(C) 2020-2022 Geoffroy R. J. Lesur <geoffroy.lesur@univ-grenoble-alpes.fr>
// and other code contributors
// Licensed under CeCILL 2.1 License, see COPYING for more information
// ***********************************************************************************

/*
//@HEADER
// ************************************************************************
//
//                        IDEFIX v 1.0-alpha
//
// ************************************************************************
//@HEADER
*/


#include <sys/time.h>

#include <stdlib.h>
#include <limits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <Kokkos_Core.hpp>

#include "idefix.hpp"
#include "profiler.hpp"
#include "input.hpp"
#include "grid.hpp"
#include "gridHost.hpp"
#include "hydro.hpp"
#include "dataBlock.hpp"
#include "timeIntegrator.hpp"
#include "setup.hpp"
#include "output.hpp"
#ifdef WITH_MPI
#include "mpi.hpp"
#endif




int main( int argc, char* argv[] ) {
  bool initKokkosBeforeMPI = false;

  // return code is zero if the simulation reached final time
  // >0 if a fatal error occured (too small timestep, Nans)
  // <0 if simulation was interrupted (max_runtime or user-triggered interruption
  int returnCode = 0;

  // When running on GPUS with Omnipath network,
  // Kokkos needs to be initialised *before* the MPI layer
#ifdef KOKKOS_ENABLE_CUDA
  if(std::getenv("PSM2_CUDA") != NULL) {
    initKokkosBeforeMPI = true;
  }
#endif

  if(initKokkosBeforeMPI)  Kokkos::initialize( argc, argv );

#ifdef WITH_MPI
  MPI_Init(&argc,&argv);
#endif

  if(!initKokkosBeforeMPI) Kokkos::initialize( argc, argv );


  {
    idfx::initialize();
    // Initialization

    Input input(argc, argv);
    input.PrintLogo();
    idfx::cout << "Main: Initialization stage." << std::endl;

    // Allocate the grid on device
    Grid grid(input);
    // Allocate the grid image on host
    GridHost gridHost(grid);

    // Actually make the grid on host and sync it on the device
    gridHost.MakeGrid(input);
    gridHost.SyncToDevice();

    // instantiate required objects.
    DataBlock data;
    data.InitFromGrid(grid, input);
    TimeIntegrator Tint(input,data);
    Output output(input, data);
    Setup mysetup(input, grid, data, output);
    idfx::cout << "Main: initialisation finished." << std::endl;

    // Show configuration
    if(initKokkosBeforeMPI) {
      idfx::cout << "Main: detected your configuration needed Kokkos to be initialised before MPI. "
                 << std::endl;
    }
    input.ShowConfig();
    grid.ShowConfig();
    data.ShowConfig();
    Tint.ShowConfig();

    // Initial conditions (or restart)
    // Are we restarting?
    if(input.restartRequested) {
      idfx::cout << "Main: Restarting from dump file."  << std::endl;
      output.RestartFromDump(data,input.restartFileNumber);
      data.SetBoundaries();
      output.CheckForWrites(data);
    } else {
      idfx::cout << "Main: Creating initial conditions." << std::endl;
      idfx::pushRegion("Setup::Initflow");
      mysetup.InitFlow(data);
      idfx::popRegion();
      #if MHD == YES && defined(EVOLVE_VECTOR_POTENTIAL)
        data.hydro.emf.ComputeMagFieldFromA(data.hydro.Ve, data.hydro.Vs);
      #endif
      data.SetBoundaries();
      output.CheckForWrites(data);
      if(data.CheckNan()) {
        IDEFIX_ERROR("Nans were found in your initial conditions.");
      }
    }

    // Main Loop
    idfx::cout << "Main: Cycling Time Integrator..." << std::endl;

    Kokkos::Timer timer;
    output.ResetTimer();

    real tstop = input.Get<real>("TimeIntegrator","tstop",0);

    while(data.t < tstop) {
      if(tstop-data.t < data.dt) data.dt = tstop-data.t;
      try {
        Tint.Cycle(data);
      } catch(std::exception &e) {
        idfx::cout << "Main: WARNING! Caught an exception in TimeIntegrator." << std::endl;
        #ifdef WITH_MPI
          if(!Mpi::CheckSync(5)) {
            std::stringstream message;
            message << "A non-synchronous exception was raised in TimeIntegrator:" << std::endl;
            message << e.what();
            message << std::endl << "No emergency output can be produced." << std::endl;
            IDEFIX_ERROR(message);
          }
        #endif
        idfx::cout << e.what() << std::endl;
        idfx::cout << "Main: attempting to save the current state for inspection." << std::endl;
        output.ForceWriteVtk(data);
        idfx::cout << "Main: Aborting current calculation." << std::endl;
        returnCode = 1;
        break;
      }
      output.CheckForWrites(data);
      if(input.CheckForAbort() || Tint.CheckForMaxRuntime() ) {
        idfx::cout << "Main: Saving current state and aborting calculation." << std::endl;
        output.ForceWriteDump(data);
        returnCode = -1;
        break;
      }
      if(input.maxCycles>=0) {
        if(Tint.GetNCycles() >= input.maxCycles) {
          idfx::cout << "Main: Reached maximum number of integration cycles." << std::endl;
          break;
        }
      }
    }

    int n_days{0}, n_hours{0}, n_minutes{0}, n_seconds{0};
    div_t divres;
    divres = div(timer.seconds(), 86400);
    n_days = divres.quot;
    divres = div(divres.rem, 3600);
    n_hours = divres.quot;
    divres = div(divres.rem, 60);
    n_minutes = divres.quot;
    n_seconds = divres.rem;

    double perfs = timer.seconds() / grid.np_int[IDIR] / grid.np_int[JDIR]
                            / grid.np_int[KDIR] / Tint.GetNCycles();

    idfx::cout << "Main: Reached t=" << data.t << std::endl;
    idfx::cout << "Main: Completed in ";
    if (n_days > 0) {
      idfx::cout << n_days << " day";
      if (n_days != 1) {
        idfx::cout << "s";
      }
      idfx::cout << " ";
    }
    if (n_hours > 0) {
      idfx::cout << n_hours << " hour";
      if (n_hours != 1) {
        idfx::cout << "s";
      }
      idfx::cout << " ";
    }
    if (n_minutes > 0) {
      idfx::cout << n_minutes << " minute";
      if (n_minutes != 1) {
        idfx::cout << "s";
      }
      idfx::cout << " ";
    }
    idfx::cout << n_seconds << " second";
    if (n_seconds != 1) {
      idfx::cout << "s";
    }
    idfx::cout << " ";
    idfx::cout << "and " << Tint.GetNCycles() << " cycle";
    if (Tint.GetNCycles() != 1) {
      idfx::cout << "s";
    }
    idfx::cout << std::endl;
    idfx::cout << "Main: ";
    idfx::cout << "Perfs are " << std::scientific << 1/perfs << " cell updates/second" << std::endl;
    #ifdef WITH_MPI
      idfx::cout << "MPI overhead represents "
                 << static_cast<int>(100.0*idfx::mpiCallsTimer/timer.seconds())
                 << "% of total run time." << std::endl;
    #endif

    idfx::cout << "Outputs represent "
               << static_cast<int>(100.0*output.GetTimer()/timer.seconds())
              << "% of total run time." << std::endl;
    // Show profiler output
    idfx::prof.Show();
  }

  if(returnCode<0) {
    idfx::cout << "Main: Job was interrupted before completion." << std::endl;
  } else if (returnCode>0) {
    idfx::cout << "Main: Job was aborted because of an unrecoverable error." << std::endl;
  } else {
    idfx::cout << "Main: Job completed successfully." << std::endl;
  }
  Kokkos::finalize();

#ifdef WITH_MPI
  MPI_Finalize();
#endif

  return(0);
}