1 #include <petsc/private/petscimpl.h> /*I "petscsys.h" I*/ 2 #if defined(PETSC_HAVE_YAML) 3 #include <yaml.h> 4 #endif 5 6 static MPI_Comm petsc_yaml_comm = MPI_COMM_NULL; /* only used for parallel error handling */ 7 8 PETSC_STATIC_INLINE MPI_Comm PetscYAMLGetComm(void) 9 { 10 return PetscLikely(petsc_yaml_comm != MPI_COMM_NULL) ? petsc_yaml_comm : (petsc_yaml_comm = PETSC_COMM_SELF); 11 } 12 13 PETSC_STATIC_INLINE MPI_Comm PetscYAMLSetComm(MPI_Comm comm) 14 { 15 MPI_Comm prev = PetscYAMLGetComm(); petsc_yaml_comm = comm; return prev; 16 } 17 18 #if defined(PETSC_HAVE_YAML) 19 20 #define TAG(node) ((const char *)((node)->tag)) 21 #define STR(node) ((const char *)((node)->data.scalar.value)) 22 #define SEQ(node) ((node)->data.sequence.items) 23 #define MAP(node) ((node)->data.mapping.pairs) 24 25 static PetscErrorCode PetscParseLayerYAML(PetscOptions options, yaml_document_t *doc, yaml_node_t *node) 26 { 27 MPI_Comm comm = PetscYAMLGetComm(); 28 char name[PETSC_MAX_OPTION_NAME] = "", prefix[PETSC_MAX_OPTION_NAME] = ""; 29 PetscErrorCode ierr; 30 31 PetscFunctionBegin; 32 if (node->type != YAML_MAPPING_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping"); 33 for (yaml_node_pair_t *pair = MAP(node).start; pair < MAP(node).top; pair++) { 34 yaml_node_t *keynode = yaml_document_get_node(doc, pair->key); 35 yaml_node_t *valnode = yaml_document_get_node(doc, pair->value); 36 PetscBool isMergeKey,isDummyKey,isIncludeTag; 37 38 if (!keynode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document"); 39 if (!valnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document"); 40 if (keynode->type != YAML_SCALAR_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar"); 41 42 /* "<<" is the merge key: don't increment the prefix */ 43 ierr = PetscStrcmp(STR(keynode), "<<", &isMergeKey);CHKERRQ(ierr); 44 if (isMergeKey) { 45 if (valnode->type == YAML_SEQUENCE_NODE) { 46 for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) { 47 yaml_node_t *itemnode = yaml_document_get_node(doc, *item); 48 if (!itemnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document"); 49 if (itemnode->type != YAML_MAPPING_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping"); 50 ierr = PetscParseLayerYAML(options, doc, itemnode);CHKERRQ(ierr); 51 } 52 } else if (valnode->type == YAML_MAPPING_NODE) { 53 ierr = PetscParseLayerYAML(options, doc, valnode);CHKERRQ(ierr); 54 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected sequence or mapping"); 55 continue; /* to next pair */ 56 } 57 58 /* "$$*" are treated as dummy keys, we use them for !include tags and to define anchors */ 59 ierr = PetscStrbeginswith(STR(keynode), "$$", &isDummyKey);CHKERRQ(ierr); 60 if (isDummyKey) { 61 ierr = PetscStrendswith(TAG(valnode), "!include", &isIncludeTag);CHKERRQ(ierr);CHKERRQ(ierr); 62 if (isIncludeTag) { /* TODO: add proper support relative paths */ 63 ierr = PetscOptionsInsertFileYAML(comm, options, STR(valnode), PETSC_TRUE);CHKERRQ(ierr); 64 } 65 continue; /* to next pair */ 66 } 67 68 if (valnode->type == YAML_SCALAR_NODE) { 69 ierr = PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode));CHKERRQ(ierr); 70 ierr = PetscOptionsSetValue(options, name, STR(valnode));CHKERRQ(ierr); 71 72 } else if (valnode->type == YAML_SEQUENCE_NODE) { 73 PetscSegBuffer seg; 74 char *buf, *strlist; 75 PetscBool addSep = PETSC_FALSE; 76 77 ierr = PetscSegBufferCreate(sizeof(char), PETSC_MAX_PATH_LEN, &seg);CHKERRQ(ierr); 78 for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) { 79 yaml_node_t *itemnode = yaml_document_get_node(doc, *item); 80 const char *itemstr = NULL; 81 size_t itemlen; 82 83 if (!itemnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document"); 84 85 if (itemnode->type == YAML_SCALAR_NODE) { 86 itemstr = STR(itemnode); 87 88 } else if (itemnode->type == YAML_MAPPING_NODE) { 89 yaml_node_pair_t *kvn = itemnode->data.mapping.pairs.start; 90 yaml_node_pair_t *top = itemnode->data.mapping.pairs.top; 91 92 if (top - kvn > 1) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node value: expected a single key:value pair"); 93 if (top - kvn > 0) { 94 yaml_node_t *kn = yaml_document_get_node(doc, kvn->key); 95 yaml_node_t *vn = yaml_document_get_node(doc, kvn->value); 96 97 if (!kn) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document"); 98 if (!vn) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document"); 99 if (kn->type != YAML_SCALAR_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar"); 100 101 ierr = PetscStrcmp(STR(kn), "<<", &isMergeKey);CHKERRQ(ierr); 102 if (isMergeKey) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node value: merge key '<<' not supported here"); 103 104 ierr = PetscStrbeginswith(STR(kn), "$$", &isDummyKey);CHKERRQ(ierr); 105 if (isDummyKey) continue; 106 itemstr = STR(kn); 107 } 108 109 ierr = PetscSNPrintf(prefix,sizeof(prefix), "%s_", STR(keynode));CHKERRQ(ierr); 110 ierr = PetscOptionsPrefixPush(options, prefix);CHKERRQ(ierr); 111 ierr = PetscParseLayerYAML(options, doc, itemnode);CHKERRQ(ierr); 112 ierr = PetscOptionsPrefixPop(options);CHKERRQ(ierr); 113 114 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar or mapping"); 115 116 ierr = PetscStrlen(itemstr, &itemlen);CHKERRQ(ierr); 117 if (itemlen) { 118 if (addSep) { 119 ierr = PetscSegBufferGet(seg, 1, &buf);CHKERRQ(ierr); 120 ierr = PetscArraycpy(buf, ",", 1);CHKERRQ(ierr); 121 } 122 ierr = PetscSegBufferGet(seg, itemlen, &buf);CHKERRQ(ierr); 123 ierr = PetscArraycpy(buf, itemstr, itemlen);CHKERRQ(ierr); 124 addSep = PETSC_TRUE; 125 } 126 } 127 ierr = PetscSegBufferGet(seg, 1, &buf);CHKERRQ(ierr); 128 ierr = PetscArrayzero(buf, 1);CHKERRQ(ierr); 129 ierr = PetscSegBufferExtractAlloc(seg, &strlist);CHKERRQ(ierr); 130 ierr = PetscSegBufferDestroy(&seg);CHKERRQ(ierr); 131 132 ierr = PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode));CHKERRQ(ierr); 133 ierr = PetscOptionsSetValue(options, name, strlist);CHKERRQ(ierr); 134 ierr = PetscFree(strlist);CHKERRQ(ierr); 135 136 } else if (valnode->type == YAML_MAPPING_NODE) { 137 ierr = PetscSNPrintf(prefix,sizeof(prefix), "%s_", STR(keynode));CHKERRQ(ierr); 138 ierr = PetscOptionsPrefixPush(options, prefix);CHKERRQ(ierr); 139 ierr = PetscParseLayerYAML(options, doc, valnode);CHKERRQ(ierr); 140 ierr = PetscOptionsPrefixPop(options);CHKERRQ(ierr); 141 142 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar, sequence or mapping"); 143 } 144 PetscFunctionReturn(0); 145 } 146 147 #endif 148 149 /*@C 150 PetscOptionsInsertStringYAML - Inserts YAML-formatted options into the database from a string 151 152 Logically Collective 153 154 Input Parameter: 155 + options - options database, use NULL for default global database 156 - in_str - YAML-formatted string options 157 158 Level: intermediate 159 160 .seealso: PetscOptionsSetValue(), PetscOptionsView(), PetscOptionsHasName(), PetscOptionsGetInt(), 161 PetscOptionsGetReal(), PetscOptionsGetString(), PetscOptionsGetIntArray(), PetscOptionsBool(), 162 PetscOptionsName(), PetscOptionsBegin(), PetscOptionsEnd(), PetscOptionsHead(), 163 PetscOptionsStringArray(),PetscOptionsRealArray(), PetscOptionsScalar(), 164 PetscOptionsBoolGroupBegin(), PetscOptionsBoolGroup(), PetscOptionsBoolGroupEnd(), 165 PetscOptionsFList(), PetscOptionsEList(), PetscOptionsInsertFile(), PetscOptionsInsertFileYAML() 166 @*/ 167 PetscErrorCode PetscOptionsInsertStringYAML(PetscOptions options,const char in_str[]) 168 { 169 #if defined(PETSC_HAVE_YAML) 170 MPI_Comm comm = PetscYAMLGetComm(); 171 yaml_parser_t parser; 172 yaml_document_t doc; 173 yaml_node_t *root; 174 PetscErrorCode ierr; 175 176 PetscFunctionBegin; 177 if (!in_str) in_str = ""; 178 ierr = !yaml_parser_initialize(&parser); if (ierr) SETERRQ(comm, PETSC_ERR_LIB, "YAML parser initialization error"); 179 yaml_parser_set_input_string(&parser, (const unsigned char *)in_str, strlen(in_str)); 180 do { 181 ierr = !yaml_parser_load(&parser, &doc); if (ierr) SETERRQ(comm, PETSC_ERR_LIB, "YAML parser loading error"); 182 root = yaml_document_get_root_node(&doc); 183 if (root) { 184 ierr = PetscParseLayerYAML(options, &doc, root);CHKERRQ(ierr); 185 } 186 yaml_document_delete(&doc); 187 } while (root); 188 yaml_parser_delete(&parser); 189 PetscFunctionReturn(0); 190 #else 191 MPI_Comm comm = PetscYAMLGetComm(); 192 (void)options; (void)in_str; /* unused */ 193 SETERRQ(comm, PETSC_ERR_SUP, "YAML not supported in this build.\nPlease reconfigure using --download-yaml"); 194 #endif 195 } 196 197 /*@C 198 PetscOptionsInsertFileYAML - Insert a YAML-formatted file in the option database 199 200 Collective 201 202 Input Parameter: 203 + comm - the processes that will share the options (usually PETSC_COMM_WORLD) 204 . options - options database, use NULL for default global database 205 . file - name of file 206 - require - if PETSC_TRUE will generate an error if the file does not exist 207 208 Only a small subset of the YAML standard is implemented. Non-scalar keys are NOT supported; 209 aliases and the merge key "<<" are. 210 The algorithm recursively parses the yaml file, pushing and popping prefixes 211 and inserting key + values pairs using PetscOptionsSetValue(). 212 213 PETSc will generate an error condition that stops the program if a YAML error 214 is detected, hence the user should check that the YAML file is valid before 215 supplying it, for instance at http://www.yamllint.com/ . 216 217 Inspired by https://stackoverflow.com/a/621451 218 219 Level: intermediate 220 221 .seealso: PetscOptionsSetValue(), PetscOptionsView(), PetscOptionsHasName(), PetscOptionsGetInt(), 222 PetscOptionsGetReal(), PetscOptionsGetString(), PetscOptionsGetIntArray(), PetscOptionsBool(), 223 PetscOptionsName(), PetscOptionsBegin(), PetscOptionsEnd(), PetscOptionsHead(), 224 PetscOptionsStringArray(),PetscOptionsRealArray(), PetscOptionsScalar(), 225 PetscOptionsBoolGroupBegin(), PetscOptionsBoolGroup(), PetscOptionsBoolGroupEnd(), 226 PetscOptionsFList(), PetscOptionsEList(), PetscOptionsInsertFile(), PetscOptionsInsertStringYAML() 227 @*/ 228 PetscErrorCode PetscOptionsInsertFileYAML(MPI_Comm comm,PetscOptions options,const char file[],PetscBool require) 229 { 230 int yamlLength = -1; 231 char *yamlString = NULL; 232 MPI_Comm prev; 233 PetscMPIInt rank; 234 PetscErrorCode ierr; 235 236 PetscFunctionBegin; 237 ierr = MPI_Comm_rank(comm, &rank);CHKERRMPI(ierr); 238 if (!rank) { 239 char fpath[PETSC_MAX_PATH_LEN]; 240 char fname[PETSC_MAX_PATH_LEN]; 241 FILE *fd; 242 size_t rd; 243 244 ierr = PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath));CHKERRQ(ierr); 245 ierr = PetscFixFilename(fpath, fname);CHKERRQ(ierr); 246 247 fd = fopen(fname, "r"); 248 if (fd) { 249 fseek(fd, 0, SEEK_END); 250 yamlLength = (int)ftell(fd); 251 fseek(fd, 0, SEEK_SET); 252 if (yamlLength < 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to query size of YAML file: %s", fname); 253 ierr = PetscMalloc1(yamlLength+1, &yamlString);CHKERRQ(ierr); 254 rd = fread(yamlString, 1, (size_t)yamlLength, fd); 255 if (rd != (size_t)yamlLength) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read entire YAML file: %s", fname); 256 yamlString[yamlLength] = 0; 257 fclose(fd); 258 } 259 } 260 261 ierr = MPI_Bcast(&yamlLength, 1, MPI_INT, 0, comm);CHKERRMPI(ierr); 262 if (require && yamlLength < 0) SETERRQ1(comm, PETSC_ERR_FILE_OPEN, "Unable to open YAML option file: %s\n", file); 263 if (yamlLength < 0) PetscFunctionReturn(0); 264 265 if (rank) {ierr = PetscMalloc1(yamlLength+1, &yamlString);CHKERRQ(ierr);} 266 ierr = MPI_Bcast(yamlString, yamlLength+1, MPI_CHAR, 0, comm);CHKERRMPI(ierr); 267 268 prev = PetscYAMLSetComm(comm); 269 ierr = PetscOptionsInsertStringYAML(options, yamlString);CHKERRQ(ierr); 270 (void) PetscYAMLSetComm(prev); 271 272 ierr = PetscFree(yamlString);CHKERRQ(ierr); 273 PetscFunctionReturn(0); 274 } 275