-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcontributing.html
More file actions
869 lines (867 loc) · 72.9 KB
/
Copy pathcontributing.html
File metadata and controls
869 lines (867 loc) · 72.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
<!-- HTML header for doxygen 1.9.1-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SY496B9L99"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-SY496B9L99');
</script>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.16.1"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>MFC: Contributing</title>
<meta name="description" content="Contributing — MFC documentation. Open-source exascale multiphase flow solver." />
<meta name="keywords" content="exascale, fluid dynamics, cfd, computational fluid dynamics, compressible, hpc, bryngelson, colonius, subgrid, multiphase, frontier, summit, el capitan, aurora, amd gpu, gpu, nvidia"/>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript" src="cookie.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
extensions: ["tex2jax.js", "TeX/AMSmath.js", "TeX/AMSsymbols.js"],
jax: ["input/TeX","output/HTML-CSS"],
});
// This file is set as MATHJAX_CODEFILE in the Doxyfile. It configures how
// MathJax renders expressions in Markdown so that it is consistent with GitHub.
MathJax.Hub.Config({
extensions: ["tex2jax.js"],
jax: ["input/TeX", "output/HTML-CSS"],
tex2jax: {
inlineMath: [ ['$', '$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true,
ignoreClass: "line" // Ignore code blocks: https://web.archive.org/web/20120430100225/http://www.mathjax.org/docs/1.1/options/tex2jax.html
},
"HTML-CSS": {
fonts: ["TeX"]
}
});
</script>
<script type="text/javascript" async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
<link rel="shortcut icon" href="icon.ico" type="image/x-icon" />
<link href="doxygen-awesome.css" rel="stylesheet" type="text/css"/>
<link href="doxygen-awesome-sidebar-only.css" rel="stylesheet" type="text/css"/>
<link href="custom.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectlogo"><img alt="Logo" src="icon.ico"/></td>
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">MFC
</div>
<div id="projectbrief">Exascale flow solver</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Cross-navigation injected into sidebar via script below -->
<script>
document.addEventListener('DOMContentLoaded', function() {
var nav = document.createElement('div');
nav.id = 'mfc-nav';
var items = [
['../documentation/index.html', 'documentation', 'User Guide'],
['../api/index.html', 'api', 'API Documentation']
];
var path = window.location.pathname;
var apiPaths = ['/api/', '/pre_process/', '/simulation/', '/post_process/'];
for (var i = 0; i < items.length; i++) {
var a = document.createElement('a');
a.href = items[i][0];
a.textContent = items[i][2];
if (items[i][1] === 'api') {
for (var j = 0; j < apiPaths.length; j++) {
if (path.indexOf(apiPaths[j]) !== -1) { a.className = 'active'; break; }
}
} else {
if (path.indexOf('/' + items[i][1] + '/') !== -1) a.className = 'active';
}
nav.appendChild(a);
}
var sideNav = document.getElementById('side-nav');
if (sideNav) sideNav.insertBefore(nav, sideNav.firstChild);
});
</script>
<!-- end header part -->
<!-- Generated by Doxygen 1.16.1 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search/",'.html');
</script>
<script type="text/javascript">
$(function() { codefold.init(); });
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
initMenu('',true,false,'search.php','Search',true);
$(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
<div id="nav-tree">
<div id="nav-tree-contents">
<div id="nav-sync" class="sync"></div>
</div>
</div>
<div id="splitbar" style="-moz-user-select:none;"
class="ui-resizable-handle">
</div>
</div>
<script type="text/javascript">
$(function(){initNavTree('contributing.html','',''); });
</script>
<div id="container">
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<div id="MSearchResults">
<div class="SRPage">
<div id="SRIndex">
<div id="SRResults"></div>
<div class="SRStatus" id="Loading">Loading...</div>
<div class="SRStatus" id="Searching">Searching...</div>
<div class="SRStatus" id="NoMatches">No Matches</div>
</div>
</div>
</div>
</div>
<div><div class="header">
<div class="headertitle"><div class="title">Contributing </div></div>
</div><!--header-->
<div class="contents">
<div class="textblock"><h1 class="doxsection"><a class="anchor" id="autotoc_md82"></a>
Contributing to MFC</h1>
<p>We welcome contributions of all kinds: bug fixes, new features, documentation, tests, and issue triage. This guide covers everything you need to get started and get your changes merged.</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md83"></a>
Getting Set Up</h2>
<ol type="1">
<li><b>Fork and clone</b> <div class="fragment"><div class="line">git clone https://github.com/<your-user>/MFC.git</div>
<div class="line">cd MFC</div>
<div class="line">git remote add upstream https://github.com/MFlowCode/MFC.git</div>
</div><!-- fragment --></li>
<li><b>Build MFC</b> (see <a class="el" href="getting-started.html" title="Getting Started">Getting Started</a> for full details): <div class="fragment"><div class="line">./mfc.sh build -j $(nproc)</div>
</div><!-- fragment --></li>
<li><b>Run the test suite</b> to verify your environment: <div class="fragment"><div class="line">./mfc.sh test -j $(nproc)</div>
</div><!-- fragment --></li>
</ol>
<h2 class="doxsection"><a class="anchor" id="autotoc_md84"></a>
Architecture Overview</h2>
<p>Understanding MFC's structure helps you know where to make changes and what they affect.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md85"></a>
Three-Phase Pipeline</h3>
<p>MFC runs simulations in three phases, each a separate Fortran executable:</p>
<ol type="1">
<li><b>pre_process</b> — Reads case parameters, generates initial conditions (patch geometries, flow states), writes binary grid and flow data to disk.</li>
<li><b>simulation</b> — Reads the initial state, advances the solution in time via TVD Runge-Kutta integration, writes solution snapshots at specified intervals.</li>
<li><b>post_process</b> — Reads simulation snapshots, computes derived quantities (vorticity, Schlieren, sound speed, etc.), writes Silo/HDF5 output for visualization.</li>
</ol>
<p>All three share code in <span class="tt">src/common/</span>. Only <span class="tt">simulation</span> is GPU-accelerated.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md86"></a>
Directory Layout</h3>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">Directory </th><th class="markdownTableHeadNone">Contents </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">src/simulation/</span> </td><td class="markdownTableBodyNone">Time-stepping, RHS, Riemann solvers, WENO, physics models (GPU-accelerated) </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">src/pre_process/</span> </td><td class="markdownTableBodyNone">Initial condition generation </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">src/post_process/</span> </td><td class="markdownTableBodyNone">Derived variable computation and formatted output </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">src/common/</span> </td><td class="markdownTableBodyNone">Derived types, global parameters, MPI, precision, I/O — shared by all three executables </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">toolchain/</span> </td><td class="markdownTableBodyNone">Python CLI, parameter system, case validation, build orchestration </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">tests/</span> </td><td class="markdownTableBodyNone">Golden files for 500+ regression tests </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">examples/</span> </td><td class="markdownTableBodyNone">Sample case files </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">docs/</span> </td><td class="markdownTableBodyNone">Doxygen documentation source </td></tr>
</table>
<h3 class="doxsection"><a class="anchor" id="autotoc_md87"></a>
Simulation Data Flow</h3>
<p>Each time step, <span class="tt">simulation</span> computes the right-hand side through this pipeline:</p>
<div class="fragment"><div class="line">q_cons_vf (conservative variables: density, momentum, energy, volume fractions)</div>
<div class="line"> → convert to primitive (density, velocity, pressure)</div>
<div class="line"> → WENO reconstruction (left/right states at cell faces)</div>
<div class="line"> → Riemann solve (numerical fluxes)</div>
<div class="line"> → flux divergence + source terms (viscous, surface tension, body forces)</div>
<div class="line"> → RHS assembly</div>
<div class="line"> → Runge-Kutta update → q_cons_vf (next stage/step)</div>
</div><!-- fragment --><p>Key data structures (defined in <span class="tt">src/common/m_derived_types.fpp</span>):</p><ul>
<li><span class="tt">scalar_field</span> — wraps a 3D <span class="tt">real(stp)</span> array (<span class="tt">%sf(i,j,k)</span>)</li>
<li><span class="tt">vector_field</span> — array of <span class="tt">scalar_field</span> (<span class="tt">%vf(1:sys_size)</span>)</li>
<li><span class="tt">q_cons_vf</span> / <span class="tt">q_prim_vf</span> — conservative and primitive state vectors</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md88"></a>
Build Toolchain</h3>
<p><span class="tt">./mfc.sh</span> is a shell wrapper that invokes the Python toolchain (<span class="tt">toolchain/main.py</span>), which orchestrates:</p>
<ol type="1">
<li><b>CMake</b> configures the build (compiler detection, dependencies, GPU backend)</li>
<li><b>Fypp</b> preprocesses <span class="tt">.fpp</span> files into <span class="tt">.f90</span> (expands GPU macros, code generation)</li>
<li><b>Fortran compiler</b> builds three executables from the generated <span class="tt">.f90</span> files</li>
</ol>
<p>See <a class="el" href="parameters.html" title="Case Parameters">Case Parameters</a> for the full list of ~3,400 simulation parameters. See <a class="el" href="case_constraints.html" title="Case Creator Guide">Case Creator Guide</a> for feature compatibility and example configurations.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md89"></a>
How the Build Fits Together</h3>
<p>The build pipeline has four layers. Each layer has a single responsibility; the handoff between them is narrow.</p>
<div class="fragment"><div class="line">mfc.sh (env bootstrap, venv, module loading, lock)</div>
<div class="line"> └─ toolchain/mfc/build.py (config slugs, cmake invocation)</div>
<div class="line"> └─ CMakeLists.txt + cmake/{GPU,Fypp,ParamsCodegen,MFCTargets}.cmake</div>
<div class="line"> └─ toolchain/mfc/params/generators/cmake_gen.py (writes 15 generated .fpp includes)</div>
</div><!-- fragment --><p><b><span class="tt">mfc.sh</span> → <span class="tt">build.py</span>.</b> <span class="tt">mfc.sh</span> is a thin shell wrapper that activates the Python virtual environment, loads HPC modules, and delegates to <span class="tt">build.py</span>. <span class="tt">build.py</span> calls <span class="tt">get_slug</span> to compute a human-readable variant identifier (<span class="tt"><prefix>-<10-hex></span>, e.g. <span class="tt">gpu-acc-chem-f93aa400b9</span>) that encodes every build-affecting flag: GPU backend, precision mode, debug, chemistry, MPI. Staging and install trees are namespaced by slug under <span class="tt">build/staging/</span> and <span class="tt">build/install/</span>, so multiple variants coexist without interfering.</p>
<p><b>CMake layer.</b> <span class="tt">cmake/Fypp.cmake</span> defines <span class="tt">HANDLE_SOURCES</span>, which sets up one <span class="tt">add_custom_command</span> per <span class="tt">.fpp</span> file to run Fypp at build time. <span class="tt">cmake/ParamsCodegen.cmake</span> registers a single ninja-tracked <span class="tt">add_custom_command</span> (DEPENDS all <span class="tt">params/*.py</span>) that invokes <span class="tt">cmake_gen.py</span> and writes the 15 generated includes under <span class="tt">build/include/<target>/</span>. There is no configure-time generation: all 15 files are build outputs, so changing any <span class="tt">params/*.py</span> triggers only a targeted rebuild, not a full reconfigure.</p>
<p><b>Fypp and per-target stubs.</b> Fypp resolves <span class="tt">#:include</span> at parse time, so every <span class="tt">.fpp</span> file sees exactly the include path for the target being compiled. <span class="tt">src/common/</span> is compiled once per executable with the <span class="tt">MFC_<TARGET></span> preprocessor define (<span class="tt">MFC_PRE_PROCESS</span>, <span class="tt">MFC_SIMULATION</span>, or <span class="tt">MFC_POST_PROCESS</span>) — this is intentional. It is what lets common modules include per-target generated files and gate simulation-only code with <span class="tt">#ifdef MFC_SIMULATION</span> without duplication.</p>
<p>For which of the 15 files is manual vs. generated and what each contains, see the "How to Add a New Simulation Parameter" section below.</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md90"></a>
Development Workflow</h2>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">Step </th><th class="markdownTableHeadNone">Command / Action </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Sync your fork </td><td class="markdownTableBodyNone"><span class="tt">git checkout master && git pull upstream master</span> </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Create a branch on your fork </td><td class="markdownTableBodyNone"><span class="tt">git checkout -b feature/<short-name></span> </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Code, test, document </td><td class="markdownTableBodyNone">Follow the standards below </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Run tests </td><td class="markdownTableBodyNone"><span class="tt">./mfc.sh test</span> </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Commit </td><td class="markdownTableBodyNone">Clear, atomic commits (see below) </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Push to your fork </td><td class="markdownTableBodyNone"><span class="tt">git push origin feature/<short-name></span> </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Open a PR </td><td class="markdownTableBodyNone">From your fork to <span class="tt">MFlowCode/MFC:master</span>. Every push triggers CI – bundle changes to avoid flooding the queue </td></tr>
</table>
<h3 class="doxsection"><a class="anchor" id="autotoc_md91"></a>
Commit Messages</h3>
<ul>
<li>Start with a concise (50 chars or fewer) summary in imperative mood: <span class="tt">Fix out-of-bounds in EOS module</span></li>
<li>Add a blank line, then a detailed explanation if needed</li>
<li>Reference related issues: <span class="tt">Fixes #123</span> or <span class="tt">Part of #456</span></li>
</ul>
<h2 class="doxsection"><a class="anchor" id="autotoc_md92"></a>
Coding Standards</h2>
<p>MFC is written in modern Fortran 2008+ with <a href="https://github.com/aradi/fypp">Fypp</a> metaprogramming. The standards below are split into <b>hard rules</b> (enforced in CI and review) and <b>soft guidelines</b> (goals for new code).</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md93"></a>
Hard Rules</h3>
<p>These are enforced. CI and reviewers will flag violations.</p>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">Element </th><th class="markdownTableHeadNone">Rule </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Formatting </td><td class="markdownTableBodyNone">Enforced automatically by pre-commit hook (<span class="tt">./mfc.sh format</span> and <span class="tt">./mfc.sh lint</span>) </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Indentation </td><td class="markdownTableBodyNone">2 spaces; continuation lines align beneath <span class="tt">&</span> </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Case </td><td class="markdownTableBodyNone">Lowercase keywords and intrinsics (<span class="tt">do</span>, <span class="tt">end subroutine</span>, ...) </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Modules </td><td class="markdownTableBodyNone"><span class="tt">m_<feature></span> (e.g. <span class="tt">m_riemann_solvers</span>) </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Public subroutines </td><td class="markdownTableBodyNone"><span class="tt">s_<verb>_<noun></span> (e.g. <span class="tt">s_compute_flux</span>) </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Public functions </td><td class="markdownTableBodyNone"><span class="tt">f_<verb>_<noun></span> (e.g. <span class="tt">f_create_bbox</span>) </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Variables </td><td class="markdownTableBodyNone">Every argument has explicit <span class="tt">intent</span>; use <span class="tt">implicit none</span>, <span class="tt">dimension</span>/<span class="tt">allocatable</span>/<span class="tt">pointer</span> as appropriate </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Forbidden </td><td class="markdownTableBodyNone"><span class="tt">goto</span>, <span class="tt">COMMON</span> blocks, global <span class="tt">save</span> variables </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Error handling </td><td class="markdownTableBodyNone">Call <span class="tt">s_mpi_abort(<msg>)</span> – never <span class="tt">stop</span> or <span class="tt">error stop</span> </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">GPU macros </td><td class="markdownTableBodyNone">Do not use raw OpenACC/OpenMP pragmas. Use the project's Fypp GPU macros (see below) </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Compiler support </td><td class="markdownTableBodyNone">Code must compile with GNU gfortran, NVIDIA nvfortran, Cray ftn, and Intel ifx </td></tr>
</table>
<h3 class="doxsection"><a class="anchor" id="autotoc_md94"></a>
Soft Guidelines</h3>
<p>Aim for these in new and modified code. Existing code may not meet all of them.</p>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">Element </th><th class="markdownTableHeadNone">Guideline </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Routine size </td><td class="markdownTableBodyNone">Prefer subroutine ≤ 500 lines, helper ≤ 150, function ≤ 100, file ≤ 1000 </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Arguments </td><td class="markdownTableBodyNone">Prefer ≤ 6; consider a derived-type params struct for more </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">DRY </td><td class="markdownTableBodyNone">Avoid duplicating logic; factor shared code into helpers </td></tr>
</table>
<h2 class="doxsection"><a class="anchor" id="autotoc_md95"></a>
Common Pitfalls</h2>
<p>This section documents domain-specific issues that frequently appear in MFC contributions. Both human reviewers and AI code reviewers reference this section.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md96"></a>
Array Bounds and Indexing</h3>
<ul>
<li>MFC uses <b>non-unity lower bounds</b> (e.g., <span class="tt">idwbuff(1)%beg:idwbuff(1)%end</span> with negative ghost-cell indices). Always verify loop bounds match array declarations.</li>
<li><b>Riemann solver indexing:</b> Left states at <span class="tt">j</span>, right states at <span class="tt">j+1</span>. Off-by-one here corrupts fluxes.</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md97"></a>
Precision and Type Safety</h3>
<ul>
<li><b><span class="tt">stp</span> vs <span class="tt">wp</span> mixing:</b> In mixed-precision mode, <span class="tt">stp</span> (storage) may be half-precision while <span class="tt">wp</span> (working) is double. Conversions between them must be intentional, especially in MPI pack/unpack and RHS accumulation.</li>
<li><b>No double-precision intrinsics:</b> <span class="tt">dsqrt</span>, <span class="tt">dexp</span>, <span class="tt">dlog</span>, <span class="tt">dble</span>, <span class="tt">dabs</span>, <span class="tt">real(8)</span>, <span class="tt">real(4)</span> are forbidden. Use generic intrinsics with <span class="tt">wp</span> kind.</li>
<li><b>MPI type matching:</b> <span class="tt">mpi_p</span> must match <span class="tt">wp</span>; <span class="tt">mpi_io_p</span> must match <span class="tt">stp</span>. Mismatches corrupt communicated data.</li>
<li><b>Scalars into device routines that loop:</b> a <span class="tt">GPU_ROUTINE</span> containing any <span class="tt">GPU_LOOP</span> (itself or through what it calls) must be called with scalars, never an array element (<span class="tt">q%sf(j,k,l)</span>, <span class="tt">alpha(i)</span>). Copy the element to a local first and receive results into a local. Cray OpenACC 19 to 21 miscompiles the pair silently at every routine level, OpenMP offload does not (<a href="https://github.com/MFlowCode/MFC/issues/1815">#1815</a>); the linter enforces it inside kernels and device routines.</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md98"></a>
Memory and Allocation</h3>
<ul>
<li><b>ALLOCATE/DEALLOCATE pairing:</b> Every <span class="tt">@:ALLOCATE()</span> must have a matching <span class="tt">@:DEALLOCATE()</span>. Missing deallocations leak GPU memory.</li>
<li><b><span class="tt">@:ACC_SETUP_VFs</span> / <span class="tt">@:ACC_SETUP_SFs</span>:</b> Vector/scalar fields must have GPU pointer setup before use in kernels.</li>
<li><b>Conditional allocation:</b> If an array is allocated inside an <span class="tt">if</span> block, its deallocation must follow the same condition.</li>
<li><b>Out-of-bounds access:</b> Fortran is permissive with assumed-shape arrays. Check that index arithmetic stays within declared bounds.</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md99"></a>
MPI Correctness</h3>
<ul>
<li><b>Halo exchange:</b> Pack/unpack offset calculations (<span class="tt">pack_offset</span>, <span class="tt">unpack_offset</span>) must be correct for both interior and periodic boundaries. Off-by-one causes data corruption.</li>
<li><b>GPU data coherence:</b> Non-RDMA MPI requires <span class="tt">GPU_UPDATE(host=...)</span> before send and <span class="tt">GPU_UPDATE(device=...)</span> after receive. Missing these causes stale data.</li>
<li><b>Buffer sizing:</b> <span class="tt">halo_size</span> depends on dimensionality and QBMM state. <span class="tt">v_size</span> must account for extra bubble variables when QBMM is active.</li>
<li><b>Deadlocks:</b> Mismatched send/recv counts or tags across MPI ranks.</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md100"></a>
Physics and Model Consistency</h3>
<ul>
<li><b>Pressure formula</b> must match <span class="tt">model_eqns</span> value. Model 2/3 (multi-fluid), MHD, and hypoelastic each use different EOS formulations. Wrong formula = wrong physics.</li>
<li><b>Conservative-primitive conversion:</b> Density recovery, kinetic energy, and pressure each have model-specific paths. Verify the correct branch is taken.</li>
<li><b>Volume fractions</b> must sum to 1. <span class="tt">alpha_rho_K</span> must be non-negative. Species mass fractions should be clipped to [0,1].</li>
<li><b>Boundary conditions:</b> Periodic BCs must match at both ends (<span class="tt">bc_x%beg</span> and <span class="tt">bc_x%end</span>). Cylindrical coordinates have special requirements (<span class="tt">bc_y%beg = -14</span> for axis in 3D).</li>
<li><b>Parameter constraints:</b> New parameters or physics features must be validated in <span class="tt">toolchain/mfc/case_validator.py</span>. New features should add corresponding validation.</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md101"></a>
Python Toolchain</h3>
<ul>
<li>New parameters in <span class="tt">toolchain/mfc/params/definitions.py</span> must have correct types, constraints, and tags.</li>
<li>Validation in <span class="tt">case_validator.py</span> must cover new interdependencies.</li>
<li>CLI schema in <span class="tt">toolchain/mfc/cli/commands.py</span> must match argument parsing.</li>
<li>Check subprocess calls for shell injection risks and missing error handling.</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md102"></a>
Compiler Portability</h3>
<ul>
<li>Any compiler-specific code (<span class="tt">#ifdef __INTEL_COMPILER</span> etc.) must have fallbacks for all four supported compilers.</li>
<li>Fypp macros must expand correctly for both GPU and CPU builds (macros are <span class="tt">#ifdef</span>'d out for non-GPU).</li>
<li>No hardcoded GPU architectures without CMake detection.</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md103"></a>
Architecture Notes</h3>
<ul>
<li><b><span class="tt">src/common/</span> affects all three executables</b> (pre_process, simulation, post_process). Changes here have wide blast radius.</li>
<li>No new global state; private helpers stay inside their defining module.</li>
<li>Flag modifications to public subroutine signatures, parameter defaults, or output formats.</li>
<li>Avoid unnecessary host/device transfers in hot loops, redundant allocations, and algorithmic inefficiency.</li>
</ul>
<h2 class="doxsection"><a class="anchor" id="autotoc_md104"></a>
Fypp and GPU</h2>
<p>MFC uses <a href="https://github.com/aradi/fypp">Fypp</a> macros (in <span class="tt">src/*/include/</span>) to generate accelerator-specific Fortran for OpenACC and OpenMP backends. Only <span class="tt">simulation</span> (plus its <span class="tt">common</span> dependencies) is GPU-accelerated.</p>
<ul>
<li><b>Raw OpenACC/OpenMP pragmas are not allowed.</b> Use the project's Fypp GPU macros instead.</li>
<li>Add <span class="tt">collapse(n)</span> when safe, declare loop-local variables with <span class="tt">private(...)</span>.</li>
<li>Avoid <span class="tt">stop</span>/<span class="tt">error stop</span> inside device code.</li>
<li>Keep macros simple and readable.</li>
</ul>
<p>See <a class="el" href="gpuParallelization.html" title="GPU Parallelization">GPU Parallelization</a> for the full GPU macro API reference, including all parameters, restrictions, examples, and debugging tools.</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md105"></a>
How-To Guides</h2>
<p>Step-by-step recipes for common development tasks.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md106"></a>
How to Add a New Simulation Parameter</h3>
<p>Adding a parameter touches both the Python toolchain and Fortran source. Follow these steps in order. See <a class="el" href="parameters.html" title="Case Parameters">Case Parameters</a> for the full list of existing parameters and <a class="el" href="case_constraints.html" title="Case Creator Guide">Case Creator Guide</a> for feature compatibility.</p>
<p><b>Step 1: Register in Python</b> (<span class="tt">toolchain/mfc/params/definitions.py</span>)</p>
<p>Add a call to <span class="tt">_r()</span> inside the <span class="tt">_load()</span> function:</p>
<div class="fragment"><div class="line">_r(<span class="stringliteral">"my_param"</span>, REAL, {<span class="stringliteral">"my_feature_tag"</span>},</div>
<div class="line"> desc=<span class="stringliteral">"Description of the parameter"</span>,</div>
<div class="line"> math=<span class="stringliteral">r"\f$\xi\f$"</span>)</div>
</div><!-- fragment --><p>The arguments are:</p><ul>
<li><b>name</b>: parameter name (must match the Fortran namelist variable)</li>
<li><b>type</b>: <span class="tt">INT</span>, <span class="tt">REAL</span>, <span class="tt">LOG</span>, <span class="tt">STR</span>, or <span class="tt">A_REAL</span> (analytic expression)</li>
<li><b>tags</b>: set of feature tags for grouping (e.g. <span class="tt">{"bubbles"}</span>, <span class="tt">{"mhd"}</span>)</li>
<li><b>desc</b>: human-readable description (optional; auto-generated from <span class="tt">_SIMPLE_DESCS</span> or <span class="tt">_ATTR_DESCS</span> if omitted)</li>
<li><b>math</b>: LaTeX math symbol in Doxygen format (optional; shown in the Symbol column of <a class="el" href="parameters.html" title="Case Parameters">Case Parameters</a>)</li>
</ul>
<p>For indexed families like <span class="tt">fluid_pp</span>, put the symbol next to its attribute name using tuples:</p>
<div class="fragment"><div class="line"><span class="keywordflow">for</span> f <span class="keywordflow">in</span> range(1, NF + 1):</div>
<div class="line"> px = f<span class="stringliteral">"fluid_pp({f})%"</span></div>
<div class="line"> <span class="keywordflow">for</span> a, sym <span class="keywordflow">in</span> [(<span class="stringliteral">"gamma"</span>, <span class="stringliteral">r"\f$\gamma_k\f$"</span>),</div>
<div class="line"> (<span class="stringliteral">"my_attr"</span>, <span class="stringliteral">r"\f$\xi_k\f$"</span>)]: <span class="comment"># <-- add here</span></div>
<div class="line"> _r(f<span class="stringliteral">"{px}{a}"</span>, REAL, math=sym)</div>
</div><!-- fragment --><p><b>Step 2: Add constraints</b> (same file, <span class="tt">CONSTRAINTS</span> dict)</p>
<p>If the parameter has valid ranges or choices:</p>
<div class="fragment"><div class="line">CONSTRAINTS = {</div>
<div class="line"> <span class="comment"># ...</span></div>
<div class="line"> <span class="stringliteral">"my_param"</span>: {<span class="stringliteral">"min"</span>: 0, <span class="stringliteral">"max"</span>: 100},</div>
<div class="line"> <span class="comment"># or: "my_param": {"choices": [1, 2, 3]},</span></div>
<div class="line">}</div>
</div><!-- fragment --><p><b>Step 3: Add dependencies</b> (same file, <span class="tt">DEPENDENCIES</span> dict)</p>
<p>If enabling one parameter requires or recommends others:</p>
<div class="fragment"><div class="line">DEPENDENCIES = {</div>
<div class="line"> <span class="comment"># ...</span></div>
<div class="line"> <span class="stringliteral">"my_param"</span>: {</div>
<div class="line"> <span class="stringliteral">"when_true"</span>: {</div>
<div class="line"> <span class="stringliteral">"requires"</span>: [<span class="stringliteral">"other_param"</span>],</div>
<div class="line"> <span class="stringliteral">"recommends"</span>: [<span class="stringliteral">"optional_param"</span>],</div>
<div class="line"> }</div>
<div class="line"> },</div>
<div class="line">}</div>
</div><!-- fragment --><p>Triggers include <span class="tt">when_true</span> (logical is <span class="tt">T</span>), <span class="tt">when_set</span> (parameter is not <span class="tt">None</span>), and <span class="tt">when_value</span> (parameter equals a specific value).</p>
<p><b>Step 4: Add physics validation</b> (<span class="tt">toolchain/mfc/case_validator.py</span>)</p>
<p>If the parameter has cross-parameter constraints that go beyond simple min/max:</p>
<div class="fragment"><div class="line"><span class="keyword">def </span>check_my_feature(self):</div>
<div class="line"> <span class="keywordflow">if</span> self.params[<span class="stringliteral">"my_param"</span>] > 0 <span class="keywordflow">and</span> <span class="keywordflow">not</span> self.params[<span class="stringliteral">"other_param"</span>]:</div>
<div class="line"> self.errors.append(<span class="stringliteral">"my_param requires other_param to be set"</span>)</div>
</div><!-- fragment --><p>If your check enforces a physics constraint, also add a <span class="tt">PHYSICS_DOCS</span> entry (see <a class="el" href="#how-to-document-physics-constraints" title="How to Document Physics Constraints">How to Document Physics Constraints</a> below).</p>
<p><b>Step 5: Fortran declaration and namelist binding (auto-generated)</b></p>
<p>Scalar declarations, GPU declare lines, Doxygen descriptions, and namelist bindings are auto-generated at build time (ninja-tracked custom command) from the <span class="tt">TYPED_DECLS</span> and <span class="tt">FORTRAN_ARRAY_DIMS</span> tables in <span class="tt">toolchain/mfc/params/definitions.py</span>. For a plain scalar registered with <span class="tt">_r()</span> / <span class="tt">_nv()</span> above, no manual Fortran edit is needed — the next build regenerates the include in <span class="tt">m_global_parameters_common.fpp</span> (compiled per target) automatically: the generation command is ninja-tracked against every file under <span class="tt">toolchain/mfc/params/</span>.</p>
<p>Still manual (not auto-generated):</p>
<ul>
<li><span class="tt">TYPE</span> member definitions inside derived types in <span class="tt">src/common/m_derived_types.fpp</span></li>
<li>Default-value assignments in <span class="tt">s_assign_default_values_to_user_inputs</span></li>
<li>Multi-variable declaration lines (<span class="tt">bc_x/y/z</span>, <span class="tt">x/y/z_domain</span>, <span class="tt">x/y/z_output</span>)</li>
<li>MPI broadcast residue in <span class="tt">src/*/m_mpi_proxy.fpp</span> (computed/non-namelist variables such as <span class="tt">m_glb</span>/<span class="tt">n_glb</span>/<span class="tt">p_glb</span>, <span class="tt">cfl_dt</span>, <span class="tt">bc_io</span>, and complex struct-member array loops; namelist-registry scalars are broadcast via the auto-generated <span class="tt">generated_bcast.fpp</span> include)</li>
<li><span class="tt">CASE_OPT_EXTRA_LINES</span> in <span class="tt">toolchain/mfc/params/generators/fortran_gen.py</span> for case-optimization constants</li>
</ul>
<p>Editing any existing file under <span class="tt">toolchain/mfc/params/</span> (tables or generators) triggers regeneration on the next build automatically. Only <em>adding a new file</em> there requires one reconfigure — the dependency list is globbed at configure time.</p>
<p><b>Step 6: Use in Fortran code</b></p>
<p>Reference <span class="tt">my_param</span> anywhere in the target's modules. It is available as a global after the namelist is read at startup.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md107"></a>
How to Write a GPU Parallel Loop</h3>
<p>All GPU loops use Fypp macros. See <a class="el" href="gpuParallelization.html" title="GPU Parallelization">GPU Parallelization</a> for the full API.</p>
<p><b>Simple parallel loop</b> (3D with collapse):</p>
<div class="fragment"><div class="line">$:gpu_parallel_loop(collapse=3)</div>
<div class="line"><span class="keywordflow">do</span> l = 0, p</div>
<div class="line"> <span class="keywordflow">do</span> k = 0, n</div>
<div class="line"> <span class="keywordflow">do</span> j = 0, m</div>
<div class="line"> q_sf(j, k, l) = 0._wp</div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow">end do</span></div>
<div class="line">$:end_gpu_parallel_loop()</div>
</div><!-- fragment --><p><b>With private variables</b> (temporaries local to each thread):</p>
<div class="fragment"><div class="line">$:gpu_parallel_loop(collapse=3, <span class="keywordtype">private</span>=<span class="stringliteral">'[rho, pres, vel]'</span>)</div>
<div class="line"><span class="keywordflow">do</span> l = 0, p</div>
<div class="line"> <span class="keywordflow">do</span> k = 0, n</div>
<div class="line"> <span class="keywordflow">do</span> j = 0, m</div>
<div class="line"> rho = q_prim_vf(1)%sf(j, k, l)</div>
<div class="line"> pres = q_prim_vf(eqn_idx%E)%sf(j, k, l)</div>
<div class="line"> <span class="comment">! ... use rho, pres as thread-local ...</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow">end do</span></div>
<div class="line">$:end_gpu_parallel_loop()</div>
</div><!-- fragment --><p><b>With reduction:</b></p>
<div class="fragment"><div class="line">$:gpu_parallel_loop(collapse=3, &</div>
<div class="line"> & reduction=<span class="stringliteral">'[[my_sum], [my_max]]'</span>, &</div>
<div class="line"> & reductionop=<span class="stringliteral">'[+, MAX]'</span>, &</div>
<div class="line"> & copy=<span class="stringliteral">'[my_sum, my_max]'</span>)</div>
<div class="line"><span class="keywordflow">do</span> l = 0, p</div>
<div class="line"> <span class="keywordflow">do</span> k = 0, n</div>
<div class="line"> <span class="keywordflow">do</span> j = 0, m</div>
<div class="line"> my_sum = my_sum + q_sf(j, k, l)</div>
<div class="line"> my_max = max(my_max, q_sf(j, k, l))</div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow">end do</span></div>
<div class="line">$:end_gpu_parallel_loop()</div>
</div><!-- fragment --><p><b>Sequential inner loop</b> within a parallel region:</p>
<div class="fragment"><div class="line">$:gpu_parallel_loop(collapse=3)</div>
<div class="line"><span class="keywordflow">do</span> l = 0, p</div>
<div class="line"> <span class="keywordflow">do</span> k = 0, n</div>
<div class="line"> <span class="keywordflow">do</span> j = 0, m</div>
<div class="line"> $:gpu_loop(parallelism=<span class="stringliteral">'[seq]'</span>)</div>
<div class="line"> <span class="keywordflow">do</span> i = 1, num_fluids</div>
<div class="line"> alpha(i) = q_prim_vf(eqn_idx%adv%beg + i - 1)%sf(j, k, l)</div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow">end do</span></div>
<div class="line">$:end_gpu_parallel_loop()</div>
</div><!-- fragment --><p>Key rules:</p><ul>
<li>Always pair <span class="tt">$:GPU_PARALLEL_LOOP(...)</span> with <span class="tt">$:END_GPU_PARALLEL_LOOP()</span></li>
<li>Use <span class="tt">collapse(n)</span> to fuse nested loops when the loop bounds are independent</li>
<li>Declare all loop-local temporaries in <span class="tt">private='[...]'</span></li>
<li>Never use <span class="tt">stop</span> or <span class="tt">error stop</span> inside a GPU loop</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md108"></a>
How to Allocate and Manage GPU Arrays</h3>
<p>The full lifecycle of a GPU-resident array:</p>
<p><b>Step 1: Declare</b> with GPU directive for module-level variables:</p>
<div class="fragment"><div class="line"><span class="keywordtype">real</span>(wp), <span class="keywordtype">allocatable</span>, <span class="keywordtype">dimension(:,:,:)</span> :: my_array</div>
<div class="line">$:gpu_declare(create=<span class="stringliteral">'[my_array]'</span>)</div>
</div><!-- fragment --><p><b>Step 2: Allocate</b> in your initialization subroutine:</p>
<div class="fragment"><div class="line">@:<span class="keyword">ALLOCATE</span>(my_array(0:m, 0:n, 0:p))</div>
</div><!-- fragment --><p><span class="tt">@:ALLOCATE</span> handles both the Fortran <span class="tt">allocate</span> and the GPU <span class="tt">enter data create</span>.</p>
<p><b>Step 3: Setup pointer fields</b> (only needed for derived types with pointer components like <span class="tt">scalar_field</span>):</p>
<div class="fragment"><div class="line">@:<span class="keyword">ALLOCATE</span>(my_field%sf(0:m, 0:n, 0:p))</div>
<div class="line">@:acc_setup_sfs(my_field)</div>
</div><!-- fragment --><p><span class="tt">@:ACC_SETUP_SFs</span> registers the pointer with the GPU runtime (required on Cray).</p>
<p><b>Step 4: Deallocate</b> in your finalization subroutine, mirroring every allocation:</p>
<div class="fragment"><div class="line">@:<span class="keyword">DEALLOCATE</span>(my_array)</div>
</div><!-- fragment --><p>If an array is allocated inside an <span class="tt">if</span> block, its deallocation must follow the same condition.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md109"></a>
How to Add an Equation of State</h3>
<p>Every stiffened-gas expression lives in <span class="tt">src/common/m_variables_conversion.fpp</span>. Adding a second EOS means supplying these, not grepping for <span class="tt">gammas</span>:</p>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">Operator </th><th class="markdownTableHeadNone">Gives </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">s_compute_mixture_coefficients</span> / <span class="tt">_dt</span> </td><td class="markdownTableBodyNone">mixture \(\Gamma, \Pi_\infty, q_v\) from the phase fractions, and their time derivative </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">f_pressure</span> / <span class="tt">s_compute_energy</span> </td><td class="markdownTableBodyNone">\(p(e)\) and \(E(p)\) </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">f_bulk_modulus</span> </td><td class="markdownTableBodyNone">\(K(p)\) - every sound speed in MFC is \(K/\rho\), differing only in how phases are mixed </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">s_compute_speed_of_sound</span> / <span class="tt">_avg</span> </td><td class="markdownTableBodyNone">that mixing: Wood's law, 6-equation, bubble-diluted </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">s_phase_internal_energy</span> </td><td class="markdownTableBodyNone">per-phase internal energy (6-equation model) </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">f_isentrope_exponent</span> / <span class="tt">f_isentrope_pressure</span> </td><td class="markdownTableBodyNone">the isentrope \(p + B = \textrm{const}\,\rho^n\) </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">f_sg_thermal</span> </td><td class="markdownTableBodyNone">the thermal law \(p + B = (n-1)c_v\rho T\) </td></tr>
</table>
<p>The first six are <em>mechanical</em> - they need only \(p, \rho, e, c\). The last two are <em>caloric</em> and additionally need \(c_v\) and \(q'_v\). An EOS that supplies only the mechanical set cannot support phase change (<span class="tt">m_phase_change</span> also needs entropy and enthalpy) or reactive burn, so those features must be prohibited for it in <span class="tt">case_validator.py</span>.</p>
<p>The coefficients arrive in two parameterizations of the same EOS: <span class="tt">gammas</span>/<span class="tt">pi_infs</span> are the stored forms the user supplies (see <a class="el" href="equations.html#sec-stored-forms" title="Stored Parameter Conventions">Stored Parameter Conventions</a>), and <span class="tt">isentrope_n</span>/<span class="tt">isentrope_B</span> are the same EOS as \(p + B = \textrm{const}\,\rho^n\), derived once at start-up. Convert with the <span class="tt">f_isentrope_*</span> operators rather than open-coding either relation.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md110"></a>
How to Add a Test Case</h3>
<p><b>Step 1: Create a case file</b></p>
<p>Test cases are Python scripts that print a JSON dict of parameters. See <span class="tt">examples/</span> for templates:</p>
<div class="fragment"><div class="line"><span class="comment">#!/usr/bin/env python3</span></div>
<div class="line"><span class="keyword">import</span> json</div>
<div class="line"> </div>
<div class="line">print(json.dumps({</div>
<div class="line"> <span class="stringliteral">"run_time_info"</span>: <span class="stringliteral">"F"</span>,</div>
<div class="line"> <span class="stringliteral">"x_domain%beg"</span>: 0.0,</div>
<div class="line"> <span class="stringliteral">"x_domain%end"</span>: 1.0,</div>
<div class="line"> <span class="stringliteral">"m"</span>: 49,</div>
<div class="line"> <span class="stringliteral">"n"</span>: 0,</div>
<div class="line"> <span class="stringliteral">"p"</span>: 0,</div>
<div class="line"> <span class="stringliteral">"dt"</span>: 1e-6,</div>
<div class="line"> <span class="stringliteral">"t_step_start"</span>: 0,</div>
<div class="line"> <span class="stringliteral">"t_step_stop"</span>: 100,</div>
<div class="line"> <span class="stringliteral">"t_step_save"</span>: 100,</div>
<div class="line"> <span class="stringliteral">"num_patches"</span>: 1,</div>
<div class="line"> <span class="stringliteral">"model_eqns"</span>: 2,</div>
<div class="line"> <span class="stringliteral">"num_fluids"</span>: 1,</div>
<div class="line"> <span class="stringliteral">"time_stepper"</span>: 3,</div>
<div class="line"> <span class="stringliteral">"weno_order"</span>: 5,</div>
<div class="line"> <span class="stringliteral">"riemann_solver"</span>: 1,</div>
<div class="line"> <span class="stringliteral">"patch_icpp(1)%geometry"</span>: 1,</div>
<div class="line"> <span class="stringliteral">"patch_icpp(1)%x_centroid"</span>: 0.5,</div>
<div class="line"> <span class="stringliteral">"patch_icpp(1)%length_x"</span>: 1.0,</div>
<div class="line"> <span class="stringliteral">"patch_icpp(1)%vel(1)"</span>: 0.0,</div>
<div class="line"> <span class="stringliteral">"patch_icpp(1)%pres"</span>: 1.0,</div>
<div class="line"> <span class="stringliteral">"patch_icpp(1)%alpha_rho(1)"</span>: 1.0,</div>
<div class="line"> <span class="stringliteral">"patch_icpp(1)%alpha(1)"</span>: 1.0,</div>
<div class="line"> <span class="stringliteral">"fluid_pp(1)%gamma"</span>: 0.4,</div>
<div class="line"> <span class="stringliteral">"fluid_pp(1)%pi_inf"</span>: 0.0,</div>
<div class="line">}))</div>
</div><!-- fragment --><p>Keep grids small and runtimes short.</p>
<p><b>Step 2: Register as a regression test</b> (<span class="tt">toolchain/mfc/test/cases.py</span>)</p>
<p>Add your case using the <span class="tt">Case</span> dataclass and the stack pattern for parameterized variations:</p>
<div class="fragment"><div class="line">stack.push(<span class="stringliteral">"my_feature"</span>, {<span class="stringliteral">"my_param"</span>: value})</div>
<div class="line">cases.append(define_case_d(stack, <span class="stringliteral">''</span>, {}))</div>
<div class="line">stack.pop()</div>
</div><!-- fragment --><p><b>Step 3: Generate golden files</b></p>
<div class="fragment"><div class="line">./mfc.sh test --generate -o <test_id></div>
</div><!-- fragment --><p>Golden files are stored as binary snapshots in <span class="tt">tests/<hash>/</span>.</p>
<p><b>Step 4: Run</b></p>
<div class="fragment"><div class="line">./mfc.sh test -j $(nproc)</div>
</div><!-- fragment --><h3 class="doxsection"><a class="anchor" id="autotoc_md111"></a>
How to Create a New Fortran Module</h3>
<p><b>Step 1: Create the file</b></p>
<p>Name it <span class="tt">src/<target>/m_<feature>.fpp</span>. CMake auto-discovers <span class="tt">.fpp</span> files — no build system changes needed.</p>
<p><b>Step 2: Use this boilerplate:</b></p>
<div class="fragment"><div class="line"><span class="comment">!> @file m_my_feature.fpp</span></div>
<div class="line"><span class="comment">!! @brief Description of the module</span></div>
<div class="line"> </div>
<div class="line"><span class="preprocessor">#:include 'case.fpp'</span></div>
<div class="line"><span class="preprocessor"></span><span class="preprocessor">#:include 'macros.fpp'</span></div>
<div class="line"><span class="preprocessor"></span> </div>
<div class="line"><span class="keyword">module</span> m_my_feature</div>
<div class="line"> </div>
<div class="line"> <span class="keywordtype">use </span>m_derived_types</div>
<div class="line"> <span class="keywordtype">use </span>m_global_parameters</div>
<div class="line"> <span class="keywordtype">use </span>m_mpi_proxy</div>
<div class="line"> </div>
<div class="line"> <span class="keywordtype">implicit none</span></div>
<div class="line"> </div>
<div class="line"> <span class="keywordtype">private</span>; <span class="keywordtype">public</span> :: s_initialize_my_feature, &</div>
<div class="line"> s_compute_my_feature, &</div>
<div class="line"> s_finalize_my_feature</div>
<div class="line"> </div>
<div class="line"> <span class="comment">! Module-level data</span></div>
<div class="line"><span class="keywordtype"> real</span>(wp), <span class="keywordtype">allocatable</span>, <span class="keywordtype">dimension(:,:,:)</span> :: work_array</div>
<div class="line"> </div>
<div class="line"><span class="keyword">contains</span></div>
<div class="line"><span class="comment"></span> </div>
<div class="line"><span class="comment"> !> Initialize module data</span></div>
<div class="line"> <span class="keywordtype">impure</span> <span class="keyword">subroutine </span>s_initialize_my_feature()</div>
<div class="line"> @:<span class="keyword">ALLOCATE</span>(work_array(0:m, 0:n, 0:p))</div>
<div class="line"> <span class="keyword">end subroutine </span>s_initialize_my_feature</div>
<div class="line"><span class="comment"></span> </div>
<div class="line"><span class="comment"> !> Core computation</span></div>
<div class="line"> <span class="keyword">subroutine </span>s_compute_my_feature(q_prim_vf, rhs_vf)</div>
<div class="line"> <span class="keywordtype">type</span>(scalar_field), <span class="keywordtype">dimension(sys_size)</span>, <span class="keywordtype">intent(in)</span> :: q_prim_vf</div>
<div class="line"> <span class="keywordtype">type</span>(scalar_field), <span class="keywordtype">dimension(sys_size)</span>, <span class="keywordtype">intent(inout)</span> :: rhs_vf</div>
<div class="line"> <span class="comment">! ...</span></div>
<div class="line"> <span class="keyword">end subroutine </span>s_compute_my_feature</div>
<div class="line"><span class="comment"></span> </div>
<div class="line"><span class="comment"> !> Clean up module data</span></div>
<div class="line"> <span class="keywordtype">impure</span> <span class="keyword">subroutine </span>s_finalize_my_feature()</div>
<div class="line"> @:<span class="keyword">DEALLOCATE</span>(work_array)</div>
<div class="line"> <span class="keyword">end subroutine </span>s_finalize_my_feature</div>
<div class="line"> </div>
<div class="line"><span class="keyword">end module </span>m_my_feature</div>
</div><!-- fragment --><p><b>Step 3: Register the module in the architecture docs</b></p>
<p>Add your module name to the appropriate category in <span class="tt">docs/module_categories.json</span>. This ensures it appears on the <a class="el" href="architecture.html" title="Code Architecture">Code Architecture</a> page. The precheck linter will fail if a module is missing from this file.</p>
<p>Key conventions:</p><ul>
<li><span class="tt">private</span> by default, explicitly <span class="tt">public</span> for the module API</li>
<li>Initialize/finalize subroutines for allocation lifecycle</li>
<li>Every <span class="tt">@:ALLOCATE</span> has a matching <span class="tt">@:DEALLOCATE</span></li>
<li>Every argument has explicit <span class="tt">intent</span></li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md112"></a>
Working with the Precision System</h3>
<p>MFC supports double (default), single, and mixed precision. The types are defined in <span class="tt">src/common/m_precision_select.f90</span>:</p>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">Type </th><th class="markdownTableHeadNone">Purpose </th><th class="markdownTableHeadNone">Example </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">wp</span> </td><td class="markdownTableBodyNone">Working precision (computation) </td><td class="markdownTableBodyNone"><span class="tt">real(wp) :: velocity</span> </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">stp</span> </td><td class="markdownTableBodyNone">Storage precision (I/O, field storage) </td><td class="markdownTableBodyNone"><span class="tt">real(stp), pointer :: sf(:,:,:)</span> </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">mpi_p</span> </td><td class="markdownTableBodyNone">MPI type matching <span class="tt">wp</span> </td><td class="markdownTableBodyNone"><span class="tt">call MPI_BCAST(var, 1, mpi_p, ...)</span> </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">mpi_io_p</span> </td><td class="markdownTableBodyNone">MPI type matching <span class="tt">stp</span> </td><td class="markdownTableBodyNone">Used in parallel I/O </td></tr>
</table>
<p>Rules:</p><ul>
<li>Use <span class="tt">real(wp)</span> for all computational variables</li>
<li>Literal constants need the <span class="tt">_wp</span> suffix: <span class="tt">1.0_wp</span>, <span class="tt">3.14159_wp</span>, <span class="tt">1e-6_wp</span></li>
<li>Use <b>generic</b> intrinsics only: <span class="tt">sqrt</span>, <span class="tt">abs</span>, <span class="tt">sin</span>, <span class="tt">exp</span>, <span class="tt">log</span>, <span class="tt">max</span>, <span class="tt">min</span></li>
<li><b>Forbidden</b> double-precision intrinsics: <span class="tt">dsqrt</span>, <span class="tt">dexp</span>, <span class="tt">dlog</span>, <span class="tt">dble</span>, <span class="tt">dabs</span>, <span class="tt">real(8)</span>, <span class="tt">real(4)</span></li>
<li>Conversions between <span class="tt">stp</span> and <span class="tt">wp</span> must be intentional, especially in MPI pack/unpack</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md113"></a>
How to Extend MPI Halo Exchange</h3>
<p>Halo exchange is in <span class="tt">src/simulation/m_mpi_proxy.fpp</span> (and <span class="tt">src/common/m_mpi_common.fpp</span> for buffer allocation).</p>
<p>To add new data to the halo exchange:</p>
<p><b>Step 1: Update buffer sizing</b> (<span class="tt">src/common/m_mpi_common.fpp</span>)</p>
<p><span class="tt">v_size</span> determines how many variables are packed per cell. If your new data adds fields per cell, increase <span class="tt">v_size</span>:</p>
<div class="fragment"><div class="line">v_size = sys_size + my_extra_fields</div>
</div><!-- fragment --><p><b>Step 2: Add pack loop</b> (<span class="tt">src/simulation/m_mpi_proxy.fpp</span>)</p>
<p>Pack your data into the send buffer using a linear index:</p>
<div class="fragment"><div class="line">$:gpu_parallel_loop(collapse=3, <span class="keywordtype">private</span>=<span class="stringliteral">'[j,k,l,r]'</span>)</div>
<div class="line"><span class="keywordflow">do</span> l = 0, p</div>
<div class="line"> <span class="keywordflow">do</span> k = 0, n</div>
<div class="line"> <span class="keywordflow">do</span> j = 0, buff_size - 1</div>
<div class="line"> r = j + buff_size*(k + (n + 1)*l)</div>
<div class="line"> buff_send(r) = my_data%sf(j + pack_offset, k, l)</div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow">end do</span></div>
<div class="line">$:end_gpu_parallel_loop()</div>
</div><!-- fragment --><p><b>Step 3: GPU data coherence</b></p>
<p>For non-RDMA MPI, add host/device transfers around the MPI call:</p>
<div class="fragment"><div class="line">$:gpu_update(host=<span class="stringliteral">'[buff_send]'</span>) <span class="comment">! GPU → CPU before send</span></div>
<div class="line"><span class="keyword">call </span>mpi_sendrecv(buff_send, ..., buff_recv, ..., ierr)</div>
<div class="line">$:gpu_update(device=<span class="stringliteral">'[buff_recv]'</span>) ! cpu → gpu after receive</div>
</div><!-- fragment --><p><b>Step 4: Add unpack loop</b> mirroring the pack loop with <span class="tt">unpack_offset</span>.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md114"></a>
How to Add a Post-Processing Output Variable</h3>
<p>Post-processing derived variables live in <span class="tt">src/post_process/m_derived_variables.fpp</span>.</p>
<p><b>Step 1: Allocate storage</b> in <span class="tt">s_initialize_derived_variables_module</span>:</p>
<div class="fragment"><div class="line"><span class="keywordflow">if</span> (my_var_wrt) <span class="keywordflow">then</span></div>
<div class="line"> <span class="keyword">allocate</span>(my_var_sf(-offset_x%beg:m + offset_x%end, &</div>
<div class="line"> -offset_y%beg:n + offset_y%end, &</div>
<div class="line"> -offset_z%beg:p + offset_z%end))</div>
<div class="line"><span class="keyword">end </span>if</div>
</div><!-- fragment --><p><b>Step 2: Create derivation subroutine:</b></p>
<div class="fragment"><div class="line"><span class="keyword">subroutine </span>s_derive_my_variable(q_prim_vf, q_sf)</div>
<div class="line"> <span class="keywordtype">type</span>(scalar_field), <span class="keywordtype">dimension(sys_size)</span>, <span class="keywordtype">intent(in)</span> :: q_prim_vf</div>
<div class="line"><span class="keywordtype"> real</span>(wp), <span class="keywordtype">dimension(-offset_x%beg:m + offset_x%end, &
-offset_y%beg:n + offset_y%end, &
-offset_z%beg:p + offset_z%end)</span>, &</div>
<div class="line"> <span class="keywordtype">intent(inout)</span> :: q_sf</div>
<div class="line"> <span class="keywordtype">integer</span> :: i, j, k</div>
<div class="line"> </div>
<div class="line"> <span class="keywordflow">do</span> k = -offset_z%beg, p + offset_z%end</div>
<div class="line"> <span class="keywordflow">do</span> j = -offset_y%beg, n + offset_y%end</div>
<div class="line"> <span class="keywordflow">do</span> i = -offset_x%beg, m + offset_x%end</div>
<div class="line"> q_sf(i, j, k) = <span class="comment">! ... compute from q_prim_vf ...</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keywordflow"> end do</span></div>
<div class="line"><span class="keyword">end subroutine </span>s_derive_my_variable</div>
</div><!-- fragment --><p><b>Step 3: Call from output</b> in <span class="tt">m_data_output.fpp</span>:</p>
<div class="fragment"><div class="line"><span class="keywordflow">if</span> (my_var_wrt) <span class="keywordflow">then</span></div>
<div class="line"> <span class="keyword">call </span>s_derive_my_variable(q_prim_vf, q_sf)</div>
<div class="line"> <span class="keyword">call </span>s_write_variable_to_formatted_database_file(q_sf, <span class="stringliteral">'my_variable'</span>, dbfile, dbroot)</div>
<div class="line"><span class="keyword">end </span>if</div>
</div><!-- fragment --><h3 class="doxsection"><a class="anchor" id="autotoc_md115"></a>
Modifying <span class="tt">src/common/</span></h3>
<p>Code in <span class="tt">src/common/</span> is compiled into all three executables (pre_process, simulation, post_process). Changes here have wide blast radius.</p>
<p>Checklist:</p><ul>
<li>Test all three targets: <span class="tt">./mfc.sh test</span> covers this</li>
<li>If adding GPU code, remember that only <span class="tt">simulation</span> is GPU-accelerated. Guard GPU macros with <span class="tt">#:if MFC_SIMULATION</span></li>
<li>Check that new <span class="tt">use</span> statements don't create circular dependencies</li>
<li>New modules need <span class="tt">implicit none</span> and explicit <span class="tt">intent</span> on all arguments</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md116"></a>
Debugging</h3>
<p>See <a class="el" href="troubleshooting.html" title="Troubleshooting Guide">Troubleshooting Guide</a> for debugging workflows, profiling tools, GPU diagnostic environment variables, common build/runtime errors, and fixes.</p>
<h3 class="doxsection"><a class="anchor" id="how-to-document-physics-constraints"></a>
How to Document Physics Constraints</h3>
<p>When adding a new <span class="tt">check_</span> method to <span class="tt">case_validator.py</span>, document its physics by adding an entry to the <span class="tt">PHYSICS_DOCS</span> dict at the top of the file:</p>
<div class="fragment"><div class="line">PHYSICS_DOCS = {</div>
<div class="line"> ...</div>
<div class="line"> <span class="stringliteral">"check_my_feature"</span>: {</div>
<div class="line"> <span class="stringliteral">"title"</span>: <span class="stringliteral">"My Feature Constraint"</span>, <span class="comment"># Required: human-readable title</span></div>
<div class="line"> <span class="stringliteral">"category"</span>: <span class="stringliteral">"Thermodynamic Constraints"</span>, <span class="comment"># Required: groups the constraint in docs</span></div>
<div class="line"> <span class="stringliteral">"explanation"</span>: <span class="stringliteral">"Why this constraint exists."</span>, <span class="comment"># Required: plain English</span></div>
<div class="line"> <span class="stringliteral">"math"</span>: <span class="stringliteral">r"\alpha > 0"</span>, <span class="comment"># Optional: LaTeX formula</span></div>
<div class="line"> <span class="stringliteral">"references"</span>: [<span class="stringliteral">"Wilfong26"</span>], <span class="comment"># Optional: BibTeX keys from references.bib</span></div>
<div class="line"> <span class="stringliteral">"exceptions"</span>: [<span class="stringliteral">"IBM cases"</span>], <span class="comment"># Optional: when constraint doesn't apply</span></div>
<div class="line"> },</div>
<div class="line">}</div>
</div><!-- fragment --><p>The <a class="el" href="physics_constraints.html" title="Physics Constraints">Physics Constraints</a> page is <b>auto-generated</b> — run <span class="tt">./mfc.sh generate</span> to rebuild it. The generator merges your <span class="tt">PHYSICS_DOCS</span> entry with the AST-extracted <span class="tt">prohibit()</span>/<span class="tt">warn()</span> calls, so stage, severity, and parameter information appear automatically.</p>
<p><b>Fields:</b></p>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">Field </th><th class="markdownTableHeadNone">Required </th><th class="markdownTableHeadNone">Description </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">title</span> </td><td class="markdownTableBodyNone">Yes </td><td class="markdownTableBodyNone">Section heading in generated docs </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">category</span> </td><td class="markdownTableBodyNone">Yes </td><td class="markdownTableBodyNone">Grouping category (e.g., "Mixture Constraints") </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">explanation</span> </td><td class="markdownTableBodyNone">Yes </td><td class="markdownTableBodyNone">Plain English description of the physics </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">math</span> </td><td class="markdownTableBodyNone">No </td><td class="markdownTableBodyNone">LaTeX formula (rendered by Doxygen's MathJax) </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone"><span class="tt">references</span> </td><td class="markdownTableBodyNone">No </td><td class="markdownTableBodyNone">List of BibTeX cite keys from <span class="tt">docs/references.bib</span> </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone"><span class="tt">exceptions</span> </td><td class="markdownTableBodyNone">No </td><td class="markdownTableBodyNone">List of cases where the constraint doesn't apply </td></tr>
</table>
<p><b>Categories:</b> Thermodynamic Constraints, Mixture Constraints, Domain and Geometry, Velocity and Dimensional Consistency, Model Equations, Boundary Conditions, Bubble Physics, Feature Compatibility, Numerical Schemes, Acoustic Sources, Post-Processing.</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md117"></a>
Testing</h2>
<p>MFC has 500+ regression tests. See <a class="el" href="testing.html" title="Testing">Testing</a> for the full guide.</p>
<ul>
<li><b>Add tests</b> for any new feature or bug fix</li>
<li>Use <span class="tt">./mfc.sh test --generate</span> to create golden files for new cases</li>
<li>Keep tests fast: use small grids and short runtimes</li>
<li>Test with <span class="tt">-a</span> to include post-processing validation</li>
</ul>
<h2 class="doxsection"><a class="anchor" id="autotoc_md118"></a>
CI Pipeline</h2>
<p>Every push to a PR triggers CI. Understanding the pipeline helps you fix failures quickly.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md119"></a>
Lint Gate (runs first, blocks all other jobs)</h3>
<p>All five checks must pass before any builds start:</p>
<ol type="1">
<li><b>Formatting</b> — <span class="tt">./mfc.sh format</span> (auto-handled by pre-commit hook)</li>
<li><b>Spelling</b> — <span class="tt">./mfc.sh spelling</span></li>
<li><b>Toolchain lint</b> — <span class="tt">./mfc.sh lint</span> (ruff + Python unit tests)</li>
<li><b>Source lint</b> — checks for:<ul>
<li>Raw <span class="tt">!$acc</span> or <span class="tt">!$omp</span> directives (must use Fypp GPU macros)</li>
<li>Double-precision intrinsics (<span class="tt">dsqrt</span>, <span class="tt">dexp</span>, <span class="tt">dble</span>, etc.)</li>
</ul>
</li>
<li><b>Doc references</b> — validates documentation cross-references</li>
</ol>
<h3 class="doxsection"><a class="anchor" id="autotoc_md120"></a>
Build and Test Matrix</h3>
<p>After the lint gate passes:</p>
<ul>
<li><b>Platforms:</b> Ubuntu and macOS</li>
<li><b>Compilers:</b> GNU (both), Intel OneAPI (Ubuntu only)</li>
<li><b>Modes:</b> debug + release, MPI + no-MPI, double + single precision</li>
<li><b>HPC runners:</b> Phoenix (NVIDIA/nvfortran), Frontier (AMD/Cray ftn) — both OpenACC and OpenMP backends</li>
<li><b>Retries:</b> Tests retry up to 3 times before failing</li>
<li><b>Cleanliness check:</b> Compiler warnings are tracked — your PR cannot increase the warning count</li>
</ul>
<h3 class="doxsection"><a class="anchor" id="autotoc_md121"></a>
Common CI Failures</h3>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">Failure </th><th class="markdownTableHeadNone">Fix </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Formatting check </td><td class="markdownTableBodyNone">Pre-commit hook handles this; if you bypassed it, run <span class="tt">./mfc.sh format</span> </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Raw pragma detected </td><td class="markdownTableBodyNone">Replace <span class="tt">!$acc</span>/<span class="tt">!$omp</span> with Fypp GPU macros (see <a class="el" href="gpuParallelization.html" title="GPU Parallelization">GPU Parallelization</a>) </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Double-precision intrinsic </td><td class="markdownTableBodyNone">Use generic intrinsic with <span class="tt">wp</span> kind (e.g., <span class="tt">sqrt</span> not <span class="tt">dsqrt</span>) </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">Golden file mismatch </td><td class="markdownTableBodyNone">If intentional: <span class="tt">./mfc.sh test --generate --only <UUID></span> </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">Warnings increased </td><td class="markdownTableBodyNone">Fix the new compiler warnings before merging </td></tr>
</table>
<p>See <a class="el" href="troubleshooting.html" title="Troubleshooting Guide">Troubleshooting Guide</a> for detailed debugging workflows.</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md122"></a>
Documentation</h2>
<ul>
<li>Add or update <b>Doxygen docstrings</b> in source files for new public routines</li>
<li>Update <b>markdown docs</b> under <span class="tt">docs/</span> if user-facing behavior changes</li>
<li>Provide a minimal <b>example case</b> in <span class="tt">examples/</span> for new features when practical</li>
</ul>
<h2 class="doxsection"><a class="anchor" id="autotoc_md123"></a>
Submitting a Pull Request</h2>
<ol type="1">
<li><b>PRs come from your fork.</b> Do not create branches on <span class="tt">MFlowCode/MFC</span> directly. Push to your fork and open a PR from there against <span class="tt">MFlowCode/MFC:master</span>.</li>
<li><b>One PR = one logical change.</b> Split large changes into focused PRs.</li>
<li><b>Fill out the PR template.</b> Remove checklist items that don't apply.</li>
<li><b>Link issues</b> with <span class="tt">Fixes #<id></span> or <span class="tt">Part of #<id></span>.</li>
<li><b>Ensure CI passes</b> before requesting review. Run <span class="tt">./mfc.sh test</span> locally first. Formatting and linting are handled automatically by the pre-commit hook.</li>
<li><b>Describe your testing</b>: what you ran, which compilers/platforms you used.</li>
</ol>
<p>If your change touches GPU code (<span class="tt">src/simulation/</span>), see the GPU checklist in the PR template.</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md124"></a>
Code Review and Merge</h2>
<ul>
<li>Respond to reviewer comments promptly</li>
<li>Push focused updates; each push re-runs CI, so batch your fixes</li>
<li>A maintainer will merge your PR once all reviews are approved and CI is green</li>
</ul>
<p>If your PR is large or architectural, consider opening an issue first to discuss the approach.</p>
<div style="text-align:center; font-size:0.75rem; color:#888; padding:16px 0 0;">Page last updated: 2026-02-15</div> </div></div><!-- contents -->
</div><!-- PageDoc -->
</div><!-- doc-content -->
<div id="page-nav" class="page-nav-panel">
<div id="page-nav-resize-handle"></div>
<div id="page-nav-tree">
<div id="page-nav-contents">
</div><!-- page-nav-contents -->
</div><!-- page-nav-tree -->
</div><!-- page-nav -->
</div><!-- container -->
<!-- HTML footer for doxygen 1.9.1-->
<!-- start footer part -->
<div id="nav-path" class="navpath">
<ul></ul>
</div>
</body>
</html>