BELFEM 0.9.0
Berkeley Lab Finite Element Framework
Loading...
Searching...
No Matches
fn_mesh_config_tag.hpp
Go to the documentation of this file.
1/*
2 * BELFEM -- The Berkeley Lab Finite Element Framework
3 * Copyright (c) 2026, The Regents of the University of California,
4 * through Lawrence Berkeley National Laboratory (subject to receipt of any required
5 * approvals from the U.S. Dept. of Energy). All rights reserved.
6 *
7 * Developers: Christian Messe, Gregory Giard
8 *
9 * See the top-level LICENSE file for the complete license and disclaimer.
10 */
11
12#ifndef BELFEM_FN_MESH_CONFIG_TAG_HPP
13#define BELFEM_FN_MESH_CONFIG_TAG_HPP
14
15#include <cstdio>
16#include <algorithm>
17#include <string>
18
19#include "typedefs.hpp"
20#include "cl_Cell.hpp"
21// cl_Input_Section.hpp declares Vector< id_t > without including it, so this
22// header must come first for this file to be includable on its own
23#include "cl_Vector.hpp"
24#include "stringtools.hpp"
25#include "cl_InputFile.hpp"
27#include "cl_Input_Section.hpp"
28
29namespace belfem
30{
31 namespace fem
32 {
33 namespace maxwell
34 {
35//------------------------------------------------------------------------------
36
67
68//------------------------------------------------------------------------------
69
74 inline uint64_t
75 mesh_config_hash( const string & aText )
76 {
77 uint64_t tHash = 0xcbf29ce484222325ULL ;
78
79 for ( const char tChar : aText )
80 {
81 tHash ^= static_cast< uint64_t >(
82 static_cast< unsigned char >( tChar ) );
83 tHash *= 0x100000001b3ULL ;
84 }
85
86 return tHash ;
87 }
88
89//------------------------------------------------------------------------------
90
91 namespace config_tag
92 {
103 inline bool
104 looks_numeric( const string & aWord )
105 {
106 if ( aWord.size() == 0 ) return false ;
107
108 bool tHasDigit = false ;
109
110 for ( const char tChar : aWord )
111 {
112 if ( tChar >= '0' && tChar <= '9' )
113 {
114 tHasDigit = true ;
115 }
116 else if ( tChar != '+' && tChar != '-' && tChar != '.'
117 && tChar != 'e' && tChar != 'E' )
118 {
119 return false ;
120 }
121 }
122
123 return tHasDigit ;
124 }
125
126//------------------------------------------------------------------------------
127
135 inline string
136 value_to_canonical( const string & aString )
137 {
138 Cell< string > tWords = string_to_words( aString );
139
140 if ( tWords.size() == 0 ) return "" ;
141
142 // a number, optionally followed by a unit
143 if ( tWords.size() <= 2 && looks_numeric( tWords( 0 ) ) )
144 {
145 real tValue = to_real( tWords( 0 ) );
146
147 if ( tWords.size() == 2 )
148 {
149 // scale into SI; unit_to_si returns 1.0 for anything it
150 // does not recognize, which is the identity we want here
151 tValue *= unit_to_si( tWords( 1 ) ).first ;
152 }
153
154 char tBuffer[ 32 ];
155 std::snprintf( tBuffer, sizeof( tBuffer ), "%.12g", tValue );
156 return string( tBuffer );
157 }
158
159 // not a quantity: join the words so spacing cannot matter
160 string tResult = string_to_lower( tWords( 0 ) );
161 for ( index_t k = 1 ; k < tWords.size() ; ++k )
162 {
163 tResult += " " + string_to_lower( tWords( k ) );
164 }
165
166 // fold the boolean spellings onto one form, so "edge coating :
167 // on" and "... : true" are one configuration rather than two
168 // tags. The NUMERIC spellings are deliberately left alone: "1"
169 // is also a perfectly good entity id, and mapping it to "true"
170 // would let an id collide with a flag
171 if ( tResult == "on" || tResult == "true" || tResult == "yes" )
172 {
173 return "true" ;
174 }
175 if ( tResult == "off" || tResult == "false" || tResult == "no" )
176 {
177 return "false" ;
178 }
179
180 return tResult ;
181 }
182
183//------------------------------------------------------------------------------
184
190 inline void
192 const input::Section * aSection,
193 const string & aPath,
194 Cell< string > & aLines,
195 const index_t aIndex = 0 )
196 {
197 // see collect_terminals: the index keeps same-type siblings apart
198 const string tPath = aPath + "/" + string_to_lower( aSection->type() )
199 + ":" + string_to_lower( aSection->label() )
200 + "[" + std::to_string( aIndex ) + "]" ;
201
202 for ( index_t k = 0 ; k < aSection->num_keys() ; ++k )
203 {
204 const string & tKey = aSection->key( k );
205
206 aLines.push( tPath + "." + string_to_lower( tKey ) + " = "
207 + value_to_canonical( aSection->get_string( tKey ) ) );
208 }
209
210 for ( index_t k = 0 ; k < aSection->num_sections() ; ++k )
211 {
212 collect_section( aSection->section( k ), tPath, aLines, k );
213 }
214 }
215
216//------------------------------------------------------------------------------
217
235 inline void
237 const input::Section * aSection,
238 const string & aPath,
239 Cell< string > & aLines,
240 const index_t aIndex = 0 )
241 {
242 // the index disambiguates same-type siblings: two unlabelled
243 // `terminal pair` sections would otherwise share a path, and
244 // swapping ids BETWEEN them would leave the line set - and the
245 // tag - unchanged. Order sensitivity is the safe trade here:
246 // reordering costs a rebuild, a collision would cost a stale mesh
247 const string tPath = aPath + "/" + string_to_lower( aSection->type() )
248 + ":" + string_to_lower( aSection->label() )
249 + "[" + std::to_string( aIndex ) + "]" ;
250
251 for ( index_t k = 0 ; k < aSection->num_keys() ; ++k )
252 {
253 const string tKey = string_to_lower( aSection->key( k ) );
254
255 // both spellings are in use: helix writes "input terminals",
256 // corc and the circuit decks write "input curves"
257 if ( tKey.find( "terminal" ) == string::npos
258 && tKey.find( "curve" ) == string::npos ) continue ;
259
260 aLines.push( tPath + "." + tKey + " = "
262 aSection->get_string( aSection->key( k ) ) ) );
263 }
264
265 for ( index_t k = 0 ; k < aSection->num_sections() ; ++k )
266 {
267 collect_terminals( aSection->section( k ), tPath, aLines, k );
268 }
269 }
270
271//------------------------------------------------------------------------------
272
278 inline void
280 const input::Section * aSection,
281 Cell< string > & aLines )
282 {
283 const string tPath = "layers:" + string_to_lower( aSection->label() );
284
285 const Cell< string > & tBuffer = aSection->buffer();
286
287 index_t tCount = 0 ;
288
289 for ( index_t k = aSection->start() ; k < aSection->end() ; ++k )
290 {
291 Cell< string > tWords = string_to_words( tBuffer( k ) );
292
293 // material <sep> thickness unit
294 if ( tWords.size() < 4 ) continue ;
295
296 real tThickness = to_real( tWords( 2 ) )
297 * unit_to_si( tWords( 3 ) ).first ;
298
299 char tBuf[ 32 ];
300 std::snprintf( tBuf, sizeof( tBuf ), "%.12g", tThickness );
301
302 aLines.push( tPath + ".layer[" + std::to_string( tCount ) + "].material = "
303 + string_to_lower( tWords( 0 ) ) );
304 aLines.push( tPath + ".layer[" + std::to_string( tCount ) + "].thickness = "
305 + string( tBuf ) );
306 ++tCount ;
307 }
308
309 // the layer COUNT is itself mesh-defining: it sets how many layer
310 // blocks are built
311 aLines.push( tPath + ".count = " + std::to_string( tCount ) );
312 }
313 }
314
315//------------------------------------------------------------------------------
316
325 inline string
326 mesh_config_text( const InputFile & aInputFile )
327 {
328 Cell< string > tLines ;
329
330 // -- topology: domain types, thin shells and their sidesets, edge
331 // coating, periodic source/target, curves
332 if ( aInputFile.section_exists( "topology" ) )
333 {
335 aInputFile.section( "topology" ), "", tLines );
336 }
337
338 // -- homology: the cut algorithm changes the cuts baked into the file
339 if ( aInputFile.section_exists( "homology" ) )
340 {
341 const input::Section * tSection = aInputFile.section( "homology" );
342
343 if ( tSection->key_exists( "algorithm" ) )
344 {
345 tLines.push( "homology.algorithm = " + config_tag::value_to_canonical(
346 tSection->get_string( "algorithm" ) ) );
347 }
348 }
349
350 // -- terminal ids that feed the cuts. Both the boundary-condition
351 // tree and the circuit tree, recursively: a deck can put its
352 // cut-defining ids in either ( examples/tapestack_circuit has them ONLY
353 // under `circuit` ). Amplitudes and waveforms stay out
354 if ( aInputFile.section_exists( "boundary conditions" ) )
355 {
357 aInputFile.section( "boundary conditions" ), "", tLines );
358 }
359
360 if ( aInputFile.section_exists( "circuit" ) )
361 {
363 aInputFile.section( "circuit" ), "", tLines );
364 }
365
366 // -- layer stacks, matched to a thin shell by label
367 for ( index_t k = 0 ; k < aInputFile.num_sections() ; ++k )
368 {
369 const input::Section * tSection = aInputFile.section( k );
370
371 if ( string_to_lower( tSection->type() ) == "layers" )
372 {
373 config_tag::collect_layers( tSection, tLines );
374 }
375 }
376
377 // -- the ONE solver-section setting that changes the discretization:
378 // whether layer interfaces get duplicate dofs and ghost facets.
379 // Added 2026-09-01; every cache built before then misses once
380 // and is rebuilt, which is the point ( a stale cache silently
381 // kept the other layout )
382 tLines.push( string( "thinshell.ghost = " )
383 + ( fem::ghost_facets_requested( aInputFile ) ? "on" : "off" ) );
384
385 // sort so deck order cannot change the tag
386 std::sort( tLines.vector_data().begin(), tLines.vector_data().end() );
387
388 string tText ;
389 for ( const string & tLine : tLines )
390 {
391 tText += tLine + "\n" ;
392 }
393
394 return tText ;
395 }
396
397//------------------------------------------------------------------------------
398
402 inline uint64_t
403 mesh_config_tag( const InputFile & aInputFile )
404 {
405 return mesh_config_hash( mesh_config_text( aInputFile ) );
406 }
407
408//------------------------------------------------------------------------------
409 }
410 }
411}
412#endif //BELFEM_FN_MESH_CONFIG_TAG_HPP
size_t size() const
return the size of the Cell
Definition cl_Cell.hpp:181
std::vector< T > & vector_data()
Definition cl_Cell.hpp:135
void push(const T &aValue)
push an entry to the end of the cell (copy version)
Definition cl_Cell.hpp:262
Parser for the input.conf configuration format.
Definition cl_InputFile.hpp:30
bool section_exists(const string &aSection) const
tell if a section exists
Definition cl_InputFile.cpp:170
index_t num_sections() const
number of sections
Definition cl_InputFile.cpp:162
const input::Section * section(const string &aSection) const
return a top-level section by type string ( a labelled section is keyed as "type:label" )
Definition cl_InputFile.cpp:146
One hierarchical section of a configuration file.
Definition cl_Input_Section.hpp:34
const string & type() const
return the type of this Section
Definition cl_Input_Section.cpp:218
bool key_exists(const string &aKey) const
check if a key exists
Definition cl_Input_Section.cpp:250
const string & label() const
return the name of this Section
Definition cl_Input_Section.cpp:226
const string & key() const
Definition cl_Input_Section.cpp:234
index_t num_sections() const
number of sections
Definition cl_Input_Section.hpp:349
const Section * section(const string &aType) const
return a subsection by type string (requires empty label)
Definition cl_Input_Section.cpp:382
index_t num_keys() const
number of keys
Definition cl_Input_Section.hpp:357
const string & get_string(const string &aKey) const
get the value of a key
Definition cl_Input_Section.cpp:266
index_t start() const
start flag in buffer
Definition cl_Input_Section.hpp:381
const Cell< string > & buffer() const
Definition cl_Input_Section.hpp:421
Definition fn_mesh_config_tag.hpp:92
string value_to_canonical(const string &aString)
Canonical form of one input value.
Definition fn_mesh_config_tag.hpp:136
void collect_layers(const input::Section *aSection, Cell< string > &aLines)
The layer stack of one thin shell.
Definition fn_mesh_config_tag.hpp:279
void collect_section(const input::Section *aSection, const string &aPath, Cell< string > &aLines, const index_t aIndex=0)
Emit every key of a section, then recurse into its subsections.
Definition fn_mesh_config_tag.hpp:191
bool looks_numeric(const string &aWord)
True if the WHOLE word is a single decimal number.
Definition fn_mesh_config_tag.hpp:104
void collect_terminals(const input::Section *aSection, const string &aPath, Cell< string > &aLines, const index_t aIndex=0)
Collect the current-injection domains from a section tree.
Definition fn_mesh_config_tag.hpp:236
Definition cl_EF_PENTA6TS.hpp:23
uint64_t mesh_config_tag(const InputFile &aInputFile)
Convenience: the tag of an input file.
Definition fn_mesh_config_tag.hpp:403
uint64_t mesh_config_hash(const string &aText)
The mesh CONFIGURATION tag: a fingerprint of the settings that decide how a .msh is turned into an en...
Definition fn_mesh_config_tag.hpp:75
string mesh_config_text(const InputFile &aInputFile)
The canonical text behind the tag.
Definition fn_mesh_config_tag.hpp:326
Definition cl_IFB_LINE3.hpp:21
bool ghost_facets_requested(const input::Section *aSolver)
true if the deck asks for duplicate interface dofs and ghost facets
Definition fn_FEM_ghost_switch.hpp:104
USER GUIDES:
Definition cl_Capacitor.cpp:16
value unit_to_si(const string &aString)
Definition stringtools.cpp:335
std::string string
Definition typedefs.hpp:26
real to_real(const std::string &aString)
convert string to real, return NAN if it is not real
Definition stringtools.cpp:316
std::string string_to_lower(const std::string &aString)
convert the string to lower case
Definition stringtools.cpp:267
uint32_t index_t
Definition typedefs.hpp:52
Cell< string > string_to_words(const std::string &aString, char aDelimiter)
create a cell of words from a string
Definition stringtools.cpp:201
double real
Definition typedefs.hpp:36