HOW TO MODIFY ENZO (Franco Vazza, June 2014)
This short guideline is only meant to be an informal guidance for close collaborators, with a particular focus on the kind of code implementations relevant to our activities, with no attempt to be accurate, complete etc.. In the case of inconsistency with the information in the official "Developer Guide" by the ENZO collaboration (see also links below) it is extremely likely
that the latter is most trustable!

What follows is thought for the version 2.3 of ENZO (but should be general enough).

Preliminary steps:

http://enzo.readthedocs.org/en/latest/user_guide/FlowChart.html
http://enzo.readthedocs.org/en/latest/developer_guide/index.html
http://enzo.readthedocs.org/en/latest/developer_guide/ModificationIntro.html
http://enzo.readthedocs.org/en/latest/developer_guide/ProgrammingGuide.html#remember-that-other-programmers-will-read-your-code
http://enzo.readthedocs.org/en/latest/developer_guide/FineGrainedOutput.html
http://enzo.readthedocs.org/en/latest/developer_guide/NewLocalOperator.html


Then, we get started!


ADD A NEW PARAMETER.
 Many times we need to define new parameters for our new algorithms, and we need enzo to a) recognize them while compiling/exectuing; b) be able to read them from the initial parameter file; c) write them into data dumps and d) read them
again from the data dumps, since we might want to further change them in the course of the simulation. Better to have also a look here
  1. Add the new entry to global_data.h (make sure the name is different anything already existing...anyway the code will tell you), a good place would be shortly after the "hydro parameters". You can decide these parameters to exist only if certain parameters or compile-time option are set (using #ifdef) or just have them there in all next versions.
  2. Add the new entry to the routines that read the parameter files: ReadParameterFile.C . Again, you can chose to have the parameter read within an ifdef or not (consistent with what you did in step 1). Even if you don't do so, the program should have no problem if it reads the parameter find and does not find the new parameters defined, because they can (must) also be internally initialized to default values.
  3. Add the new entry to WriteParameterFile.C, CosmologySimulationInitialize.C and in case also to NestedCosmologySimulationInitialize.C To find the appropriate places, find the lines "GridData->CosmologySimulationInitializeGrid(gridnum)" (they come twice), as well as "fprintf(Outfptr,.." If your new entry has to run also with the initial conditions of test, you should do the same for them (e.g. ShockTubeInitialize.C etc...)
  4. Add the new entry to the very important Grid.h that defines the memer of the Grid class (see here for further info), in the appropriate place and consistent with what you did in step.3 (look for " int CosmologySimulationInitializeGrid(".. etc).
  5. Add the new entry in the function prototype within the relevan grid class, like Grid_CosmologySimulationInitialize.C in this case.
    -> only at this point we can compile for the first time and see if the first steps worked. If it succeded, we can move to the next step, otherwise be prepared for a nice rest of the day...
  6. Add the default value of your new parameters in SetDefaultGlobalValues.C  (we do it here just to realise the code can compile regardless of this step, good to know and very dangerous).


DO STUFF WITH THE NEW PARAMETERS.

    Two main approaches (but only one would be approved by the proper Enzo developer in most of cases): a) make your new parameters interact with already existing functions in Enzo, which means add the parameters to the grid definition of existing operators, modify them etc...) or b) define a new operator and member of a grid class and do everything here.
   Whatever you choose, you must consider the best tradeoff between: a) portability to future versions of Enzo (i.e. if you rely to much on the structure of an existing operator in enzo, yuo may discover it has totally changed in a next version); b) compatibility with version of the code of your collaborators (since we would love to merge each part if working); c) effort to speed ratio to have your results (also thiking in perspective). 

 Since the second is the best practice, let's try that.

  1. Have a second look here  (but pay attention, the modifications that are written there are actually meant to work in EvolveLevel.C and non in the EvolveHierarchy.C, as wrongly stated there)
  2. Now we suppose we need to add some local new operators that does stuff separately for each level of the grid (in AMR runs), and that we want to update some of fields based on our new algorithm (e.g. one agn doing theral feedback) outside of the hydro solver (you can decide if you want to make it before or after the solution to the hydro equation at this level and during this timestep. What' best? I would do it before in most of cases). So go to EvolveLevel.C  and insert a call to your new (still unexsiting at this stage) operator, by locating the sequence

 for (grid1 = 0; grid1 < NumberOfGrids; grid1++) {
#endif //SAB.
      /* Copy current fields (with their boundaries) to the old fields
          in preparation for the new step. */

      Grids[grid1]->GridData->CopyBaryonFieldToOldBaryonField();
     --> here?
      /* Call hydro solver and save fluxes around subgrids. */

      if( UseMHDCT && HydroMethod == MHD_Li && MHDCT_debug_flag == 1){
        //Temporarily allow the option of calling SolveMHDEqations
        //while I transition MHDCT to SolveHydroEquations.
        Grids[grid1]->GridData->SolveMHDEquations(LevelCycleCount[level],
                NumberOfSubgrids[grid1], SubgridFluxesEstimate[grid1], level ,grid1);

      }else{
        Grids[grid1]->GridData->SolveHydroEquations(LevelCycleCount[level],
            NumberOfSubgrids[grid1], SubgridFluxesEstimate[grid1], level);
      }
   --> or better here?

       The blue arrows suggest two possible places to inser the new operator.

    3. Insert a block like:

  //frenk operator to do AGN feedback
      if(agn_dens > 0){
        if( Grids[grid1]->GridData->AGNfeedback() == FAIL ){
          fprintf(stderr,"Error in grid->AGNfeedback\n");
          return FAIL;
        }}
   

    4. Now we have to create Grid_AGNfeedback.C, which will become a new function in Enzo. Let's say we are copying one already existing one which does similar stuff (better to be familiar with the code for that) and modifying all necessary parts. A useful function for many of these application is the simple Grid_CopyBaryonFieldToOldBaryonField.C that just copies all arrays from the new to the old timesteps.