Read Namelist Error Fortran 90 No Code
Reading time : 7 minutes
(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
Proceed Reading
Source: https://cerfacs.fr/coop/fortran-namelist-workedex
0 Response to "Read Namelist Error Fortran 90 No Code"
Post a Comment