cherab / solps

Other
6 stars 5 forks source link

Loading SOLPS-ITER simulation: refer to baserun for b2fgmtry #59

Closed jirakova closed 2 years ago

jirakova commented 3 years ago

When using load_solps_from_raw_output, the b2fgmtry file is expected to be in the run directory (which is passed as argument to load_solps_from_raw_output). However, usually one sets up the geometry using b2run b2ag in the baserun directory (usually ../baserun relative to the run directory), so b2fgmtry is stored there and SOLPS refers to it from every run directory. It would, therefore, be helpful if Cherab searched for b2fgmtry in baserun if the file isn't found in the run directory.

vsnever commented 3 years ago

Thanks for reporting this. We also need to specify in the docstring that simulation_path is a path to the run directory. Because for a SOLPS user, the simulation directory is the one with run and baserun subdirectories.

jacklovell commented 3 years ago

There are two use cases I regularly see used for load_solps_from_raw_output:

  1. The Cherab user is also a SOLPS user, has performed a SOLPS run themselves and wants to analyse it.
  2. The Cherab user has received some files from a SOLPS user and wants to analyse the output with Cherab.

The cherab-solps module was originally developed with option 2 in mind (the original authors did not run SOLPS). So the expectation was that you would receive all the files necessary from somebody else, stick them in a single directory and point Cherab at that directory.

Now more cherab-solps users are actually SOLPS users too, option 1 is important to support. But I don't think we should make any implicit, hard coded assumptions about the directory structure. It would be fine to add an additional argument, called something like baserun_path, so a user can direct the code to look in this additional directory for files it doesn't find in simulation_path. It could even default to simulation_path/../baserun. But this should be clearly documented rather than work like black magic under the hood.

Mateasek commented 3 years ago

What about having a separate method rather than an argument? The only difference is getting the correct paths and then the rest function is the same. Separating it in two methods might be clearer and it could have simple docs

vsnever commented 3 years ago

I think that baserun and run directories are hard coded into SOLPS, so there are only two possible cases: 1) all the files are in the same directory, 2) b2fgmtry is in the baserun and all other files are in the run.

@jirakova, can you comment on this?

vsnever commented 3 years ago

I don't think that we need another argument, especially if baserun and run are hard coded into SOLPS. However, I certainly agree with @jacklovell that the search paths should be documented.

"""
    :param str simulation_path: String path to simulation directory.
                                Example: '/home/user/solps5/runs/simulation_name/run'.
                                Note that 'b2fgmtry' will be searched for in
                                'simulation_path/../baserun', if it is not found in
                                'simulation_path'.
"""
jirakova commented 3 years ago

As far as I know, it is mandatory that "simulations" are stored in $SOLPSTOP/runs under an arbitrary name. Each "simulation" must contain exactly one baserun folder. Individual "runs" are usually stored in parallel to baserun, each under an arbitrary name within the simulation folder, but they can be grouped in subdirectories as well. Therefore, run_path/../baserun will usually point to the baserun directory, but not necessarily so.

In my experience, it would be best to search for any missing files in run_path/../baserun by default, but also to offer the user a possibility to define their own path to the baserun directory.

vsnever commented 3 years ago

Individual "runs" are usually stored in parallel to baserun, each under an arbitrary name within the simulation folder, but they can be grouped in subdirectories as well.

I see, thank you. This means that I was wrong about the directory structure.

It would be fine to add an additional argument, called something like baserun_path, so a user can direct the code to look in this additional directory for files it doesn't find in simulation_path. It could even default to simulation_path/../baserun.

Then it seems like the correct solution.

vsnever commented 3 years ago

I think that PR #62 now implements the solution suggested by @jacklovell and @jirakova.

Mateasek commented 3 years ago

I still think that the separation of the two @jacklovell described deserves 2 separate methods. There can be a method, which will get paths to the files as parameters (e.g. load_solps_raw_files) which should be loaded and could be called by other methods for loading files from a single directory (e.g. load_solps_from_raw_output) and from the run directory (e.g. load_solps_from_run_directory). The bonus is that any other directory structure users have can be loaded with the load_solps_raw_files after adding their own simple script. Also this would move any searching of dirs and other logic outside, separate it from the parsing and interpretation.

vsnever commented 3 years ago

I still think that the separation of the two @jacklovell described deserves 2 separate methods.

I think we can have very similar behaviour with a single method avoiding any assumptions about the directory structure:

def load_solps_from_raw_output(simulation_path='', debug=False, mesh_file_path=None,
                               b2_state_file_path=None, b2_plasma_file_path=None,
                               eirene_fort44_file_path=None):

    mesh_file_path = mesh_file_path or os.path.join(simulation_path, 'b2fgmtry')
    b2_state_file_path = b2_state_file_path or os.path.join(simulation_path, 'b2fstate')
    b2_plasma_file_path = b2_plasma_file_path or os.path.join(simulation_path, 'b2fplasmf')
    eirene_fort44_file_path = eirene_fort44_file_path or os.path.join(simulation_path, 'fort.44')

    if not os.path.isfile(mesh_file_path):
        raise RuntimeError("No B2 mesh description file ({}) found.".format(mesh_file_path))
    _, _, geom_data_dict = load_b2f_file(mesh_file_path, debug=debug) 

    if not os.path.isfile(b2_state_file_path):
        raise RuntimeError("No B2 plasma state file ({}) found.".format(b2_state_file_path))
    header_dict, sim_info_dict, mesh_data_dict = load_b2f_file(b2_state_file_path, debug=debug)

    if not os.path.isfile(b2_plasma_file_path):
        print("Warning! No B2 plasma solution formatted file ({}) found.".format(b2_plasma_file_path),
              "No total_radiation data will be available.")
        have_b2plasmf = False
    else:
        _, _, plasma_solution_dict = load_b2f_file(b2_plasma_file_path, debug=debug, header_dict=header_dict)
        have_b2plasmf = True

    if not os.path.isfile(eirene_fort44_file_path):
        print("Warning! No EIRENE output file ({}) found.".format(eirene_fort44_file_path),
              "Assuming B2 stand-alone simulation.")
        b2_standalone = True
    else:
        # Load data for neutral species from EIRENE output file
        eirene = load_fort44_file(eirene_fort44_file_path, debug=debug)
        b2_standalone = False

If a specific file is not in simulation_path, the user can specify the path to it.

jirakova commented 3 years ago

In the case that @vsnever's solution is adopted, I'd drop a note in a visible place of the documentation (examples, docstring, file-not-found message) that the user may redefine the individual file paths. It will be a bit of a drag to write the paths out every time I want to load a simulation, especially since I may not know from the get-go where the files are stored, but divining some general rules for file placement which can be implemented directly into Cherab would be even less reliable. At least the Cherab user who runs SOLPS will learn which files are important to read the simulation state.

Mateasek commented 3 years ago

Right, thanks for explanation.

vsnever commented 3 years ago

Here is a variant for the docstring for load_solps_from_raw_output():

    Load a SOLPS simulation from raw SOLPS output files.

    Relevant files are:
    * mesh description file (b2fgmtry)
    * B2 plasma state (b2fstate)
    * B2 plasma solution, formatted (b2fplasmf), optional
    * Eirene output file (fort.44), optional

    :param str simulation_path: String path to simulation directory.
                                The SOLPS output files will be searched for in this directory
                                by their default names, unless the paths to specific files are
                                explicitly provided by the user.
                                Example: '/home/user/solps5/runs/simulation_name/run'.
                                Defaults to the current working directory.
    :param bool debug: A flag for displaying textual debugging information when parsing
                       the SOLPS files. Defaults to False.
    :param str mesh_file_path: String path to mesh description file (b2fgmtry).
                               Defaults to '{simulation_path}/b2fgmtry' if None.
    :param str b2_state_file_path: String path to B2 plasma state file (b2fstate).
                                   Defaults to '{simulation_path}/b2fstate' if None.
    :param str b2_plasma_file_path: String path to formatted B2 plasma solution file (b2fplasmf).
                                    Defaults to '{simulation_path}/b2fplasmf' if None.
    :param str eirene_fort44_file_path: String path to Eirene output file (fort.44).
                                        Defaults to '{simulation_path}/fort.44' if None.

    :rtype: SOLPSSimulation
jirakova commented 3 years ago

@vsnever This looks fantastic.

vsnever commented 3 years ago

I pushed this change to #62, so let's wait for @jacklovell's decision.