Read Namelist Error Fortran 90 No Code

Reading time : 7 minutes

heat

(A super nintendo controller, Photograph Devin Berko) Fortran Namelists is a I/O standard since 1991, gimmicky to the SuperNintendo Controller. Less fun to play with, and very limited, Namelists are ofttimes shunned. Moreover, making your own I/O parser is very tempting. Let's endeavour them out completely, for once

Experimenting namelists on a real-life software.

The solver AVTP is a in-house Cerfacs thermal solver, written in Fortran. The input files were in custom ASCII files, and had to be refreshed. For example, the main command file was written this way (blank lines did matter):

                        '../MESH/mesh.mesh.h5'            ! Mesh file            '../MESH/mesh.asciiBound'            ! Ascii Boundary file            '../SOLUTBOUND/mesh.solutBound.h5'            ! Boundary solution file            './SOLUT/init.h5'            ! inital solution            './SOLUT/mesh'            ! output files            './TEMPORAL/'            ! temporal evolution directory            1.0d0     ! Reference length            |            scales coordinates X by X/reflen            200            ! Number of iterations            400            ! Number of elements per grouping            (typically of order            100            )            one            ! Preprocessor: skip            (            0            ), use            (            1            )            &            write            (            ii            )            &            terminate            (            3            )            -1        ! Interactive details of convergence            (            one            )            or not            (            0            )            100            ! Prints convergence every x iterations            1            ! Type of output control            10            ! Store solution every 10 iterations            2            ! Spatial scheme - ivisc :            i            =            four delta operator            ;            two            =            2            delta operator            2            ! istoreadd            i            ! Steady state            (            0            )            or            (            1            )            unsteady calculation or            (            2            )            stock-still dt or            (            iii            )            freq calculation  -1         ! Temporal scheme identification            (explicit or implicit)            1000            1e-6  ! Scheme clarification            400.d0     ! Fourier parameter            for            viscous time-stride          

Different formats were considered:

  • The AVBP 7.X Parser, keyword based, human oriented, with perchance not-hashable keys (Read more on AVBP non-hashable keys here).
  • Fortran Namelists. Keyword based, present sice Fortran 77, standard congenital-in since Fortran 90. Look a lot alike AVBP 7.10 Parser.
  • JSON fortran, a JSON parser. Perfect for computer information exchange merely not really human oriented, needing a Fortran 2008 compiler.
  • TOML-f, a TOML parser, good for both homo and machine, but needing a Fortran 2008 compiler.
  • Practise non change format (always an option!)

The AVBP seven.X Parser is 28800 lines of Fortran, half the size of the full AVTP thermal solver sources. It would have be a huge duplication (unless we likewise move the AVBP parser to a mutual library, and therefore bear on AVBP for AVTP). JSON and TOML were tempting, but quite far from the habits of the large end-users ground. We decided to try out Fortran Namelists, and come across how far nosotros could go.

Fortran Namelists beingness a standard, their conversion back and forth with others languages are already available. For python, we used f90nml.

Limitation of namelists.

Offset some resource :

  • An introduction to Namelists - part of the JULES documentation
  • F77 Namelists - the original reference
  • IBM Namelists from F95 to 2003 - beware of IBMs extensions
  • Reading merely some values from anamelist, A Fortran-Lang discussion that sums up the frustrations of namelists.

The prominent limitation with namelists is the static aspect. Once compiled, the lawmaking volition allocate a stock-still number of degrees of liberty once-for-all. Technically speaking, its structure is immutable. Since Fortran 2003, the size of a variable tin be dynamic in namelists, but the construction is not.

This static aspect is bad news, sure, merely not a total pushback. We just take to cope with ii constraints:

Optional arguments

In most serialized format (e.g. YAML) an optional value is inexistent if missing:

                        order            :            type_            :            pizza            tomato            :            truthful            mozzarella            :            truthful          

This data volition know nix about dressing.

                        order            :            type_            :            gnocchi            dressing            :            sugo di noce          

This information will know nothing virtually love apple and mozzarella parameters.

Oppositely, A namelist I/O implies all the options are present in memory, whatsoever the case. Indeed the namelist declaration will be :

                        namelist            \            order            \            type_            ,            tomato            ,            mozzarella            ,            dressing          

We can ask the user, while muting the parsing errors:

                        &order            type_            =            'pizza'            tomato            =            'yes'            mozzarella            =            'no'            /          

This lloks similar dynamic. Yet, the Fortran code volition still know about dressing with its default value if any, or garbage if not. Therefore the developer must keep all the d.o.f. in mind, since all will co-exists at all times.

Dynamic blocks

The 2d problem, Harder to deal with, is when the number of blocks changes. For example, in YAML or JSON, you can declare a listing of objects such as this one:

                        -            type = 'pizza'            love apple = 'yep'            mozzarrella = 'no'            -            type = 'pizza'            tomato = 'aye'            mozzarrella = 'yes'            -            type = 'pizza'            tomato = 'no'            mozzarrella = 'no'          

The number of elements is non divisional, we could have 0,ane, iv or 66 elements in the list. With static namelists nosotros nevertheless can give the impression of a dynamic pick to the cease user :

                        &            &customer001            type            =            'pizza'            tomato            =            'yes'            mozzarrella            =            'no'            /            &customer002            type            =            'pizza'            tomato            =            'yep'            mozzarrella            =            'aye'            /            &customer003            type            =            'pizza'            tomato            =            'no'            mozzarrella            =            'no'            /          

However the developer must pre-declare, for case, 30 blocks from customer001 to customer030, and read the number of activated customers somewhere else. The two downsides are:

  • There can be a mismatch between the number of elements activated and the ones that are actually declared.
  • The code must be re-compiled if a situation requires a bigger list (here 34 customers)

A full case

Here is an example on how to actually implement a namelist. It was inspired from an case from Programming In Modern Fortran, calculation a trick to find problematic input lines.

The namelist declaration is limited to the inside of read_some_parameters(), and is not visible from the signature. The opening and closing are outsourced to open_inputfile(file_path, file_unit, iostat) and close_inputfile(file_path, file_unit, iostat) to have a common ground for user feedback. These subroutines are not speficic to namelists by the manner.

                        !! This module will prove a simple namelist reader            !! Its takes from Modernistic Fortran Porgramming                        !! https://cyber.dabamos.de/programming/modernfortran/namelists.html            !!            !! We added a little trick in the namelist loader,            !! plant on http://degenerateconic.com/namelist-error-checking/            !! to make information technology signal out , if the readin fails,            !! the line in the input file that let to a failure.            program                        main            !! Assuming nosotros want to read some parameters.            !! No namelist aspect should exist seen here            use            ,            intrinsic            ::            iso_fortran_env            ,            merely            :            stderr            =>            error_unit            implicit none            !integer :: int_            character            (            len            =            32            )            ::            type_            character            (            len            =            32            )            ::            love apple            character            (            len            =            32            )            ::            mozzarella            character            (            len            =            32            )            ::            dressing            ! Read from file.            phone call                        read_some_parameters            (            'brusque.nml'            ,            type_            ,            tomato plant            ,            mozzarella            ,            dressing            )            ! Output some values.            impress            '(2a)'            ,            'type_: '            ,            type_            print            '(2a)'            ,            'tomato plant: '            ,            tomato            print            '(2a)'            ,            'mozzarella: '            ,            mozzarella            impress            '(2a)'            ,            'dressing: '            ,            dressing            contains                          subroutine                        read_some_parameters            (            file_path            ,            type_            ,            tomato            ,            mozzarella            ,            dressing            )            !! Read some parmeters,  Here nosotros use a namelist                        !! but if you were to change the storage format (TOML,or home-fabricated),                        !! this signature would non change            graphic symbol            (            len            =*            ),            intent            (            in            )            ::            file_path            graphic symbol            (            len            =            32            ),            intent            (            out            )            ::            type_            grapheme            (            len            =            32            ),            intent            (            out            )            ::            tomato            character            (            len            =            32            ),            intent            (            out            )            ::            mozzarella            character            (            len            =            32            ),            intent            (            out            )            ::            dressing            !integer, intent(out) :: type_            integer            ::            file_unit            ,            iostat            ! Namelist definition===============================            namelist            /            ORDER            /            &            type_            ,            &            tomato            ,            &            mozzarella            ,            &            dressing            type_            =            "undefined"            tomato            =            "undefined"            mozzarella            =            "undefined"            dressing            =            "undefined"            ! Namelist definition===============================            call                        open_inputfile            (            file_path            ,            file_unit            ,            iostat            )            if            (            iostat            /=            0            )            so            !! write here what to do if opening failed"            render                          terminate if                          read            (            nml            =            ORDER            ,            iostat            =            iostat            ,            unit            =            file_unit            )            call                        close_inputfile            (            file_path            ,            file_unit            ,            iostat            )            if            (            iostat            /=            0            )            then            !! write here what to practice if reading failed"            render                          finish if                          end subroutine                        read_some_parameters            !! Namelist helpers            subroutine                        open_inputfile            (            file_path            ,            file_unit            ,            iostat            )            !! Bank check whether file exists, with consitent error message            !! return the file unit            character            (            len            =*            ),            intent            (            in            )            ::            file_path            integer            ,            intent            (            out            )            ::            file_unit            ,            iostat            inquire            (            file            =            file_path            ,            iostat            =            iostat            )            if            (            iostat            /=            0            )            then                          write            (            stderr            ,            '(3a)'            )            'Mistake: file "'            ,            trim            (            file_path            ),            '" non found!'            end if                          open            (            action            =            'read'            ,            file            =            file_path            ,            iostat            =            iostat            ,            newunit            =            file_unit            )            end subroutine                        open_inputfile            subroutine                        close_inputfile            (            file_path            ,            file_unit            ,            iostat            )            !! Check the reading was OK            !! return mistake line IF not            !! close the unit            character            (            len            =*            ),            intent            (            in            )            ::            file_path            character            (            len            =            m            )            ::            line            integer            ,            intent            (            in            )            ::            file_unit            ,            iostat            if            (            iostat            /=            0            )            and so                          write            (            stderr            ,            '(2a)'            )            'Error reading file :"'            ,            trim            (            file_path            )            write            (            stderr            ,            '(a, i0)'            )            'iostat was:"'            ,            iostat            backspace            (            file_unit            )            read            (            file_unit            ,            fmt            =            '(A)'            )            line            write            (            stderr            ,            '(A)'            )            &            'Invalid line : '            //            trim            (            line            )            terminate if                          close            (            file_unit            )            end subroutine                        close_inputfile            end plan                        main          

You can try this code with the brusk.nml case (do non forget the carriage return after \) :

                        &            gild            tomato            =            'yes'            type_            =            'pizza'            mozzarella            =            'no'            !            dressing            =            'bah'            /          

In the original example, the namelist included arrays and derived types. hic sunt dracones : some gfortran versions had issues when parsing derived types (see dedicated thread). Simply should you really inquire for inputs mapping exactly the fashion memory is structured? Are you lot asking to much from the poor cease-user?

Application to our solver

The Main input file

The AVTP code is non request for a huge variety of d.o.f, because the Heat conduction modeling is rather elementary. The chief input file looks like this:

                        &hpc_debug            ncell_group            =            400            preprocessor            =            1            /            &input_control            asciibound_file            =            './MESH/bloup.asciibound'            initial_solution_file            =            './INIT/bloup.init.h5'            material_database            =            'material_database.dat'            mesh_file            =            './MESH/bloup.mesh.h5'            probe_file            =            './bloup_record.dat'            solutbound_file            =            './SOLUTBOUND/bloup.solutbound.h5'            tab_species            =            'FER_varie'            /            &output_average            save_average            =            'no'            save_average_freq            =            ten            save_average_name            =            './SOLUT/av'            save_average_out            =            3000            /            &output_control            save_solution            =            'yes'            save_solution_additional            =            'minimum'            save_solution_name            =            './SOLUT/bloup'            save_solution_time            =            0.0001            save_temporal            =            'yep'            save_temporal_balance            =            'yes'            save_temporal_dirname            =            './TEMPORAL/'            save_temporal_iteration            =            1            /            &preproc            ichoice_nml            =            i            iolcomm_nml            =            0            iorder_nml            =            -1            ippnode_nml            =            1            ireorder_nml            =            1            itghost_nml            =            0            ndum_nml            =            0            /            &run_control            diffusion_scheme            =            'FE_2delta'            fourier            =            v            implicit_conv_crit            =            1e-06            implicit_nb_it            =            1000            number_of_species            =            one            simulation_end_time            =            0.001            solver_type            =            'implicit_CG'            /          
The boundary conditions

Again AVTP setups can comply well replicated number of blocks. As the number of active blocks is read elsewhere while loading the CAD (the mesh), there is no boosted variable needed.

                        &patch001            patch_name_nml            =            'outlet'            boundary_condition            =            'WALL_ISOT'            target_origin            =            'solutbound'            /            &patch002            patch_name_nml            =            'inlet'            boundary_condition            =            'WALL_ISOT'            target_origin            =            'solutbound'            /            &patch003            patch_name_nml            =            'wallup'            boundary_condition            =            'WALL_ISOT'            target_origin            =            'solutbound'            /            &patch004            patch_name_nml            =            'walldown'            boundary_condition            =            'WALL_ISOT'            target_origin            =            'solutbound'            /            &patch005            patch_name_nml            =            'walldroite'            boundary_condition            =            'WALL_ISOT'            target_origin            =            'solutbound'            /            &patch006            patch_name_nml            =            'wallgauche'            boundary_condition            =            'WALL_ISOT'            target_origin            =            'solutbound'            /            &patch007            patch_name_nml            =            'perfo'            boundary_condition            =            'WALL_FLUX'            target_origin            =            'solutbound'            /          

(To see how many patches are currently in you AVTP source see SOURCES/COMMON/commonbl_master.h)

A primal/value parser, by blocks, Like the AVBP parser

To the end user accustomed to AVBP software, the main visual difference is the markup of the blocks, and quotes around strings:

                        #            Namelist            style            (            AVTP            3            .            0            ,            Thermal            setup            )            &            input_control            asciibound_file            =            './MESH/bloup.asciibound'            initial_solution_file            =            './INIT/bloup.init.h5'            material_database            =            'material_database.dat'            mesh_file            =            './MESH/bloup.mesh.h5'            probe_file            =            './bloup_record.dat'            solutbound_file            =            './SOLUTBOUND/bloup.solutbound.h5'            tab_species            =            'FER_varie'            /            #            AVBP            Parser            style            (            AVBP            vii            .            8            ,            CFD            Setup            )            $            INPUT            -            Command            mesh_file            =            ..            /            ..            /            ..            /            ..            /            Documents            /            MESH_C3SM            /            TRAPVTX            /            trappedvtx            .            mesh            .            h5            asciibound_file            =            .            /            SOLUTBOUND            /            combu            .            asciibound            asciibound_tpf_file            =            None            initial_solution_file            =            .            /            INIT            /            combu            .            init            .            h5            mixture_database            =            .            /            mixture_database            .            dat            probe_file            =            .            /            combu_probe            .            dat            solutbound_file            =            .            /            SOLUTBOUND            /            combu            .            solutbound            .            h5            species_database            =            .            /            species_database            .            dat            $            end_INPUT            -            Command          

Adding validation and much more than

I could reasonably fence:

Ok but my custom parser does sooo much more, it tin can check the validity of the inputs and give precise information to gear up the trouble. And Fortran namelist are not that smart don't they?

Practiced point. Namelists will complain if the type does non match, that is all. Moreover, the error raised by a misspelled keyword is sometimes vague. But that is OK because namelists deals with I/O, not validation. Permit's use a real validator

Movement to python

Starting time how we can load the namelist in python? Note this code is universal to whatever namelist:

                        import            f90nml            from            opentea.noob.asciigraph            import            nob_asciigraph            nml            =            f90nml            .            read            (            "./run.params"            )            # the namelist object            nml_dict            =            nml            .            todict            ()            # interpret to dict            print            (            nob_asciigraph            (            nml_dict            ))            # show output          

As you tin encounter, all types are well casted:

            ┣ hpc_debug            (OrderedDict)            ┃  ┣ ncell_group            (int)            :            400            ┃  ┗ preprocessor            (int)            :            i            ┣ input_control            (OrderedDict)            ┃  ┣ asciibound_file            (str)            : ./MESH/bloup.asciibound ┃  ┣ initial_solution_file            (str)            : ./INIT/bloup.init.h5 ┃  ┣ material_database            (str)            : material_database.dat ┃  ┣ mesh_file            (str)            : ./MESH/bloup.mesh.h5 ┃  ┣ probe_file            (str)            : ./bloup_record.dat ┃  ┣ solutbound_file            (str)            : ./SOLUTBOUND/bloup.solutbound.h5 ┃  ┗ tab_species            (str)            : FER_varie ┣ output_average            (OrderedDict)            ┃  ┣ save_average            (str)            : no ┃  ┣ save_average_freq            (int)            :            10            ┃  ┣ save_average_name            (str)            : ./SOLUT/av ┃  ┗ save_average_out            (int)            :            3000            ┣ output_control            (OrderedDict)            ┃  ┣ save_solution            (str)            : yes ┃  ┣ save_solution_additional            (str)            : minimum ┃  ┣ save_solution_name            (str)            : ./SOLUT/bloup ┃  ┣ save_solution_time            (float)            :            0.0001 ┃  ┣ save_temporal            (str)            : yeah ┃  ┣ save_temporal_balance            (str)            : yep ┃  ┣ save_temporal_dirname            (str)            : ./TEMPORAL/ ┃  ┗ save_temporal_iteration            (int)            :            1            ┣ preproc            (OrderedDict)            ┃  ┣ ichoice_nml            (int)            :            one            ┃  ┣ iolcomm_nml            (int)            :            0            ┃  ┣ iorder_nml            (int)            : -1 ┃  ┣ ippnode_nml            (int)            :            one            ┃  ┣ ireorder_nml            (int)            :            i            ┃  ┣ itghost_nml            (int)            :            0            ┃  ┗ ndum_nml            (int)            :            0            ┗ run_control            (OrderedDict)            ┣ diffusion_scheme            (str)            : FE_2delta    ┣ fourier            (int)            :            5            ┣ implicit_conv_crit            (bladder)            : 1e-06    ┣ implicit_nb_it            (int)            :            m            ┣ number_of_species            (int)            :            1            ┣ simulation_end_time            (float)            :            0.001    ┗ solver_type            (str)            : implicit_CG          

At present we can interpret this dictionary to a JSON file, infer online a SCHEMA, and tinker information technology to our taste. You tin read a full explanation on Using the JSON SCHEMA standard for scientific applications. SCHEMA can be lenghty so permit us see a pocket-sized part:

                        type            :            object            properties            :            run_control            :            type            :            object            properties            :            control_kind            :            type            :            string            default            :            fourth dimension            stepper            :            blazon            :            string            default            :            fourier            solver_type            :            type            :            cord            default            :            implicit_CG            diffusion_scheme            :            blazon            :            cord            default            :            FE_2delta            enum            :            [            FE_2delta            ,            FE_4delta            ]            implicit_nb_it            :            type            :            integer            default            :            grand            minimum            :            100            (...)          

Now how do we make a validator out of this? We use jsonschema, a general purpose validator, to write these v statements, applicable for any namelist:

                        import            f90nml            import            yaml            import            jsonschema            from            opentea.noob.asciigraph            import            nob_asciigraph            nml            =            f90nml            .            read            (            "./run.params"            )            nml_dict            =            nml            .            todict            ()            with            open up            (            "./schema.yml"            ,            "r"            )            every bit            fin            :            schema            =            yaml            .            load            (            fin            ,            Loader            =            yaml            .            SafeLoader            )            jsonschema            .            validate            (            nml            ,            schema            )          

Now if yous run this small-scale script on a bad input, east.g. diffusion_scheme=FE_4_delta (Spurious _ here), you get the following mistake.

            Failed validating            'enum'            in schema[            'properties'            ][            'run_control'            ][            'properties'            ][            'diffusion_scheme'            ]:            {            'default':            'FE_2delta',            'enum':            [            'FE_2delta',            'FE_4delta'            ],            'blazon':            'cord'            }            On instance[            'run_control'            ][            'diffusion_scheme'            ]:            'FE_4_delta'          

Takeaway

The static aspect of a Fortran Namelist is a regular pushback, at first. But if you accept the constraints, it can be also a very simple framework to deal with afterwards. In improver, because the structure is a reliable standard, smart people already fabricated some I/O helpers for Python, a language more suited to stunts on dynamic structures and cord operations. This enable the most mainstream and advanced techniques of information validation and automatic documentation on a Fortran input.

So re-call up about it twice : do you lot actually -really- demand a dynamic structure for your inputs values?

Namelists are for you lot if:

  • You want a solid parser for gratis, even without the power Fortran 2008
  • the ready of combinations for your d.o.f. is humanly manageable
  • you can live with unused variables

Namelists are not for you if:

  • The set of combinations for your d.o.f. is exponentially large
  • Unused variables is a terrible sin
  • Your designed the ultimate parser yourself
  • You already moved to TOML-f

is a enquiry scientist focused on computer science and applied science topics for HPC.


Proceed Reading


alvordints1980.blogspot.com

Source: https://cerfacs.fr/coop/fortran-namelist-workedex

0 Response to "Read Namelist Error Fortran 90 No Code"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel