Skip to content

Commit 657794f

Browse files
committed
Merge branch 'staging/dycore-prefix-namelist' into develop (PR #1285)
When MPAS-A is used as a dynamical core in a host model (e.g., CAM/CAM-SIMA), it needs to share the namelist file with other model components. As a result, MPAS-A namelist groups and options may not be easily recognizable at first sight. The solution, which is implemented by this merge, is to add a unique identifier to all MPAS-A namelist group and option names. The following transformations are performed for each MPAS-A namelist group and option name: - Leading 'config_' is removed recursively from the name. Case-insensitive. - Leading 'mpas_' is removed recursively from the name. Case-insensitive. - Prepend 'mpas_' to the name. By doing so, it is now easier to distinguish MPAS-A namelist groups and options from host model ones. The possibility of name collisions with host model ones is also resolved once and for all. Note that only namelist I/O is affected. Internally, MPAS-A still refers to its namelist options by their original names. Compared to the implementation in CAM, this merge applies the "name mangling" logic in an algorithmic and predictable way. It works automatically even if there are additions, modifications, or deletions to MPAS-A namelist groups and options. The "name mangling" logic is entirely guarded behind the MPAS_CAM_DYCORE macro. For stand-alone MPAS-A, its namelist groups and options keep their original names as in the registry. For CAM, it is not affected by this merge because it does not use the code generation functionality of MPAS-A for namelist reading at all. For CAM-SIMA, the Fortran include files generated by the parse utility stay the same. Its regression tests all pass, indicating identical model results to the previous baseline. * staging/dycore-prefix-namelist: Prefix all namelist group and option names for MPAS dycore in CAM/CAM-SIMA
2 parents b669db1 + 71f3d22 commit 657794f

File tree

2 files changed

+82
-10
lines changed

2 files changed

+82
-10
lines changed

src/tools/registry/gen_inc.c

+81-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
#include "fortprintf.h"
1616
#include "utility.h"
1717

18+
#ifdef MPAS_CAM_DYCORE
19+
#include <ctype.h>
20+
#endif
21+
1822
void process_core_macro(const char *macro, const char *val, va_list ap);
1923
void process_domain_macro(const char *macro, const char *val, va_list ap);
2024

@@ -696,8 +700,12 @@ int parse_namelist_records_from_registry(ezxml_t registry)/*{{{*/
696700
ezxml_t nmlrecs_xml, nmlopt_xml;
697701

698702
const char *const_core;
699-
const char *nmlrecname, *nmlrecindef, *nmlrecinsub;
700-
const char *nmloptname, *nmlopttype, *nmloptval, *nmloptunits, *nmloptdesc, *nmloptposvals, *nmloptindef;
703+
const char *original_nmlrecname, *nmlrecindef, *nmlrecinsub;
704+
const char *original_nmloptname, *nmlopttype, *nmloptval, *nmloptunits, *nmloptdesc, *nmloptposvals, *nmloptindef;
705+
706+
// Fortran variable names have a length limit of 63 characters. + 1 for the terminating null character.
707+
char nmlrecname[64];
708+
char nmloptname[64];
701709

702710
char pool_name[1024];
703711
char core_string[1024];
@@ -743,7 +751,9 @@ int parse_namelist_records_from_registry(ezxml_t registry)/*{{{*/
743751

744752
// Parse Namelist Records
745753
for (nmlrecs_xml = ezxml_child(registry, "nml_record"); nmlrecs_xml; nmlrecs_xml = nmlrecs_xml->next){
746-
nmlrecname = ezxml_attr(nmlrecs_xml, "name");
754+
original_nmlrecname = ezxml_attr(nmlrecs_xml, "name");
755+
mangle_name(nmlrecname, sizeof(nmlrecname), original_nmlrecname);
756+
747757
nmlrecindef = ezxml_attr(nmlrecs_xml, "in_defaults");
748758
nmlrecinsub = ezxml_attr(nmlrecs_xml, "in_subpool");
749759

@@ -777,7 +787,9 @@ int parse_namelist_records_from_registry(ezxml_t registry)/*{{{*/
777787

778788
// Define variable definitions prior to reading the namelist in.
779789
for (nmlopt_xml = ezxml_child(nmlrecs_xml, "nml_option"); nmlopt_xml; nmlopt_xml = nmlopt_xml->next){
780-
nmloptname = ezxml_attr(nmlopt_xml, "name");
790+
original_nmloptname = ezxml_attr(nmlopt_xml, "name");
791+
mangle_name(nmloptname, sizeof(nmloptname), original_nmloptname);
792+
781793
nmlopttype = ezxml_attr(nmlopt_xml, "type");
782794
nmloptval = ezxml_attr(nmlopt_xml, "default_value");
783795
nmloptunits = ezxml_attr(nmlopt_xml, "units");
@@ -809,7 +821,9 @@ int parse_namelist_records_from_registry(ezxml_t registry)/*{{{*/
809821
// Define the namelist block, to read the namelist record in.
810822
fortprintf(fd, " namelist /%s/ &\n", nmlrecname);
811823
for (nmlopt_xml = ezxml_child(nmlrecs_xml, "nml_option"); nmlopt_xml; nmlopt_xml = nmlopt_xml->next){
812-
nmloptname = ezxml_attr(nmlopt_xml, "name");
824+
original_nmloptname = ezxml_attr(nmlopt_xml, "name");
825+
mangle_name(nmloptname, sizeof(nmloptname), original_nmloptname);
826+
813827
if(nmlopt_xml->next){
814828
fortprintf(fd, " %s, &\n", nmloptname);
815829
} else {
@@ -840,7 +854,9 @@ int parse_namelist_records_from_registry(ezxml_t registry)/*{{{*/
840854
// Define broadcast calls for namelist values.
841855
fortprintf(fd, " if (ierr <= 0) then\n");
842856
for (nmlopt_xml = ezxml_child(nmlrecs_xml, "nml_option"); nmlopt_xml; nmlopt_xml = nmlopt_xml->next){
843-
nmloptname = ezxml_attr(nmlopt_xml, "name");
857+
original_nmloptname = ezxml_attr(nmlopt_xml, "name");
858+
mangle_name(nmloptname, sizeof(nmloptname), original_nmloptname);
859+
844860
nmlopttype = ezxml_attr(nmlopt_xml, "type");
845861

846862
if(strncmp(nmlopttype, "real", 1024) == 0){
@@ -858,7 +874,9 @@ int parse_namelist_records_from_registry(ezxml_t registry)/*{{{*/
858874
fortprintf(fd, " call mpas_log_write(' The following values will be used for variables in this record:')\n");
859875
fortprintf(fd, " call mpas_log_write(' ')\n");
860876
for (nmlopt_xml = ezxml_child(nmlrecs_xml, "nml_option"); nmlopt_xml; nmlopt_xml = nmlopt_xml->next){
861-
nmloptname = ezxml_attr(nmlopt_xml, "name");
877+
original_nmloptname = ezxml_attr(nmlopt_xml, "name");
878+
mangle_name(nmloptname, sizeof(nmloptname), original_nmloptname);
879+
862880
nmlopttype = ezxml_attr(nmlopt_xml, "type");
863881

864882
if (strncmp(nmlopttype, "character", 1024) == 0) {
@@ -885,10 +903,12 @@ int parse_namelist_records_from_registry(ezxml_t registry)/*{{{*/
885903
fortprintf(fd, "\n");
886904

887905
for (nmlopt_xml = ezxml_child(nmlrecs_xml, "nml_option"); nmlopt_xml; nmlopt_xml = nmlopt_xml->next){
888-
nmloptname = ezxml_attr(nmlopt_xml, "name");
906+
original_nmloptname = ezxml_attr(nmlopt_xml, "name");
907+
mangle_name(nmloptname, sizeof(nmloptname), original_nmloptname);
889908

890-
fortprintf(fd, " call mpas_pool_add_config(%s, '%s', %s)\n", pool_name, nmloptname, nmloptname);
891-
fortprintf(fcg, " call mpas_pool_get_config(configPool, '%s', %s)\n", nmloptname, nmloptname);
909+
// Always keep namelist options to their original names in MPAS pools for compatibility reasons.
910+
fortprintf(fd, " call mpas_pool_add_config(%s, '%s', %s)\n", pool_name, original_nmloptname, nmloptname);
911+
fortprintf(fcg, " call mpas_pool_get_config(configPool, '%s', %s)\n", original_nmloptname, nmloptname);
892912
}
893913
fortprintf(fd, "\n");
894914
fortprintf(fcg, "\n");
@@ -2532,3 +2552,54 @@ int parse_structs_from_registry(ezxml_t registry)/*{{{*/
25322552

25332553
return 0;
25342554
}/*}}}*/
2555+
2556+
2557+
/**
2558+
* mangle_name
2559+
*
2560+
* Perform name mangling for MPAS namelist groups and options, as appropriate, depending on the containing
2561+
* host model.
2562+
*
2563+
* When MPAS is used as a dynamical core in a host model (e.g., CAM/CAM-SIMA), it needs to share
2564+
* the namelist file with other model components. As a result, MPAS namelist groups and options may not
2565+
* be easily recognizable at first sight. With the `MPAS_CAM_DYCORE` macro being defined, this function
2566+
* adds a unique identifier to each MPAS namelist group and option name by performing the following
2567+
* transformations:
2568+
*
2569+
* 1. Leading "config_" is removed recursively from the name. Case-insensitive.
2570+
* 2. Leading "mpas_" is removed recursively from the name. Case-insensitive.
2571+
* 3. Prepend "mpas_" to the name.
2572+
*
2573+
* By doing so, it is now easier to distinguish MPAS namelist groups and options from host model ones.
2574+
* The possibility of name collisions with host model ones is also resolved once and for all.
2575+
*
2576+
* For stand-alone MPAS, where the `MPAS_CAM_DYCORE` macro is not defined, this function just returns
2577+
* the name as is.
2578+
*/
2579+
void mangle_name(char *new_name, const size_t new_name_size, const char *old_name)
2580+
{
2581+
if (!new_name || !old_name || new_name_size == 0) return;
2582+
2583+
#ifdef MPAS_CAM_DYCORE
2584+
const char *const new_prefix = "mpas_";
2585+
const char *const old_prefix = "config_";
2586+
2587+
// Remove all leading whitespaces by moving pointer forward.
2588+
while (*old_name != '\0' && isspace((unsigned char) *old_name)) old_name++;
2589+
2590+
// Remove all leading "config_" by moving pointer forward.
2591+
while (strncasecmp(old_name, old_prefix, strlen(old_prefix)) == 0) old_name += strlen(old_prefix);
2592+
2593+
// Remove all leading "mpas_" by moving pointer forward.
2594+
while (strncasecmp(old_name, new_prefix, strlen(new_prefix)) == 0) old_name += strlen(new_prefix);
2595+
2596+
*new_name = '\0';
2597+
snprintf(new_name, new_name_size, "%s%s", new_prefix, old_name);
2598+
2599+
// Remove all trailing whitespaces by zeroing (nulling) out.
2600+
new_name += strlen(new_name) - 1;
2601+
while (*new_name != '\0' && isspace((unsigned char) *new_name)) *new_name-- = '\0';
2602+
#else
2603+
snprintf(new_name, new_name_size, "%s", old_name);
2604+
#endif
2605+
}

src/tools/registry/gen_inc.h

+1
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ int push_attributes(ezxml_t currentPosition);
3838
int merge_structs_and_var_arrays(ezxml_t currentPosition);
3939
int merge_streams(ezxml_t registry);
4040
int parse_structs_from_registry(ezxml_t registry);
41+
void mangle_name(char *new_name, const size_t new_name_size, const char *old_name);

0 commit comments

Comments
 (0)